summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/cssom
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/css/cssom
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/cssom')
-rw-r--r--testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html47
-rw-r--r--testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html23
-rw-r--r--testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html35
-rw-r--r--testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html21
-rw-r--r--testing/web-platform/tests/css/cssom/CSSFontFaceRule.html21
-rw-r--r--testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html192
-rw-r--r--testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html54
-rw-r--r--testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html118
-rw-r--r--testing/web-platform/tests/css/cssom/CSSKeyframeRule.html52
-rw-r--r--testing/web-platform/tests/css/cssom/CSSKeyframesRule.html85
-rw-r--r--testing/web-platform/tests/css/cssom/CSSNamespaceRule.html32
-rw-r--r--testing/web-platform/tests/css/cssom/CSSRuleList.html27
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html60
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html158
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleRule.html113
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html66
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html19
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html34
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html22
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html22
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html83
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html84
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-cssRules.html41
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html42
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html801
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html29
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html58
-rw-r--r--testing/web-platform/tests/css/cssom/CSSStyleSheet.html132
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html44
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html37
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html31
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html39
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html26
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html25
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html26
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html7
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html14
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event-002.html60
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event.html32
-rw-r--r--testing/web-platform/tests/css/cssom/HTMLStyleElement-load-event.html23
-rw-r--r--testing/web-platform/tests/css/cssom/META.yml5
-rw-r--r--testing/web-platform/tests/css/cssom/MediaList.html55
-rw-r--r--testing/web-platform/tests/css/cssom/MediaList2.xhtml45
-rw-r--r--testing/web-platform/tests/css/cssom/MutationObserver-style.html43
-rw-r--r--testing/web-platform/tests/css/cssom/StyleSheetList.html34
-rw-r--r--testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html92
-rw-r--r--testing/web-platform/tests/css/cssom/at-namespace.html29
-rw-r--r--testing/web-platform/tests/css/cssom/base-uri.html55
-rw-r--r--testing/web-platform/tests/css/cssom/border-shorthand-serialization.html42
-rw-r--r--testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html50
-rw-r--r--testing/web-platform/tests/css/cssom/caretPositionFromPoint.html67
-rw-r--r--testing/web-platform/tests/css/cssom/change-rule-with-layers-crash.html19
-rw-r--r--testing/web-platform/tests/css/cssom/computed-style-001.html66
-rw-r--r--testing/web-platform/tests/css/cssom/computed-style-002.html17
-rw-r--r--testing/web-platform/tests/css/cssom/computed-style-003.html20
-rw-r--r--testing/web-platform/tests/css/cssom/computed-style-004.html29
-rw-r--r--testing/web-platform/tests/css/cssom/computed-style-005.html64
-rw-r--r--testing/web-platform/tests/css/cssom/computed-style-set-property.html36
-rw-r--r--testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html148
-rw-r--r--testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html16
-rw-r--r--testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html71
-rw-r--r--testing/web-platform/tests/css/cssom/css-style-reparse.html59
-rw-r--r--testing/web-platform/tests/css/cssom/cssimportrule-parent.html20
-rw-r--r--testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html24
-rw-r--r--testing/web-platform/tests/css/cssom/cssimportrule.html125
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html28
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html37
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html63
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-fontfacerule.html58
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html223
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-pagerule.html77
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html75
-rw-r--r--testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html77
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html27
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html43
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html32
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html12
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html126
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html19
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html69
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html20
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html15
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html16
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html16
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html16
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html15
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html58
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html18
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html160
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html103
-rw-r--r--testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html73
-rw-r--r--testing/web-platform/tests/css/cssom/declaration-block-all-crash.html10
-rw-r--r--testing/web-platform/tests/css/cssom/delete-namespace-rule-when-child-rule-exists.html16
-rw-r--r--testing/web-platform/tests/css/cssom/escape.html102
-rw-r--r--testing/web-platform/tests/css/cssom/flex-serialization.html36
-rw-r--r--testing/web-platform/tests/css/cssom/font-family-serialization-001.html44
-rw-r--r--testing/web-platform/tests/css/cssom/font-shorthand-serialization.html17
-rw-r--r--testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html131
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html34
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html49
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html39
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html34
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html26
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html33
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html34
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-crash.html40
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-logical-crash.html31
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-roundtrip.html41
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html21
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html21
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-grid.html25
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-multicol-absolute-crash.html29
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html18
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html19
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-relpos-inline.html84
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html19
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html26
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html22
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html30
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html30
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html23
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html27
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-margins-roundtrip.html35
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html26
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html294
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html42
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html38
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html22
-rw-r--r--testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html26
-rw-r--r--testing/web-platform/tests/css/cssom/historical.html58
-rw-r--r--testing/web-platform/tests/css/cssom/idlharness.html77
-rw-r--r--testing/web-platform/tests/css/cssom/inline-style-001.html78
-rw-r--r--testing/web-platform/tests/css/cssom/insert-dir-rule-crash.html14
-rw-r--r--testing/web-platform/tests/css/cssom/insert-dir-rule-in-iframe-crash.html17
-rw-r--r--testing/web-platform/tests/css/cssom/insert-invalid-where-rule-crash.html10
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-across-context.html32
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html32
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html6
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-from-script.html12
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-import-no-index.html33
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html40
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-no-index.html35
-rw-r--r--testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html14
-rw-r--r--testing/web-platform/tests/css/cssom/invalid-pseudo-elements.html17
-rw-r--r--testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html5
-rw-r--r--testing/web-platform/tests/css/cssom/medialist-dynamic-001.html12
-rw-r--r--testing/web-platform/tests/css/cssom/medialist-interfaces-001.html83
-rw-r--r--testing/web-platform/tests/css/cssom/medialist-interfaces-002.html71
-rw-r--r--testing/web-platform/tests/css/cssom/medialist-interfaces-004.html64
-rw-r--r--testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html16
-rw-r--r--testing/web-platform/tests/css/cssom/overflow-serialization.html61
-rw-r--r--testing/web-platform/tests/css/cssom/page-descriptors.html40
-rw-r--r--testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html22
-rw-r--r--testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html22
-rw-r--r--testing/web-platform/tests/css/cssom/property-accessors.html64
-rw-r--r--testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html16
-rw-r--r--testing/web-platform/tests/css/cssom/rule-restrictions.html51
-rw-r--r--testing/web-platform/tests/css/cssom/selectorSerialize.html141
-rw-r--r--testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html13
-rw-r--r--testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html22
-rw-r--r--testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html31
-rw-r--r--testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html32
-rw-r--r--testing/web-platform/tests/css/cssom/serialize-all-longhands.html36
-rw-r--r--testing/web-platform/tests/css/cssom/serialize-custom-props.html69
-rw-r--r--testing/web-platform/tests/css/cssom/serialize-media-rule.html185
-rw-r--r--testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html257
-rw-r--r--testing/web-platform/tests/css/cssom/serialize-values.html614
-rw-r--r--testing/web-platform/tests/css/cssom/serialize-variable-reference.html36
-rw-r--r--testing/web-platform/tests/css/cssom/setproperty-null-undefined.html47
-rw-r--r--testing/web-platform/tests/css/cssom/shorthand-serialization.html86
-rw-r--r--testing/web-platform/tests/css/cssom/shorthand-values.html51
-rw-r--r--testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html48
-rw-r--r--testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html140
-rw-r--r--testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html40
-rw-r--r--testing/web-platform/tests/css/cssom/stylesheet-dom-mutation-event-crash.html13
-rw-r--r--testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html7
-rw-r--r--testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html12
-rw-r--r--testing/web-platform/tests/css/cssom/stylesheet-same-origin.css3
-rw-r--r--testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html83
-rw-r--r--testing/web-platform/tests/css/cssom/stylesheet-title.html39
-rw-r--r--testing/web-platform/tests/css/cssom/support/1x1-green.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/1x1-lime.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/1x1-maroon.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/1x1-navy.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/1x1-red.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/1x1-white.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/60x60-gg-rr.pngbin0 -> 224 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/60x60-green.pngbin0 -> 218 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/README28
-rw-r--r--testing/web-platform/tests/css/cssom/support/a-green.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/b-green.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/black.css4
-rw-r--r--testing/web-platform/tests/css/cssom/support/c-red.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/cat.pngbin0 -> 1883 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/constructable-import.css3
-rw-r--r--testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js375
-rw-r--r--testing/web-platform/tests/css/cssom/support/import-charset.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/import-green.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/import-red.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/import-rule.css1
-rw-r--r--testing/web-platform/tests/css/cssom/support/malformed-http-response.asis1
-rw-r--r--testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.pngbin0 -> 231 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/pattern-tr.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/ruler-h-50%.pngbin0 -> 691 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/ruler-h-50px.pngbin0 -> 671 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/ruler-v-100px.pngbin0 -> 760 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/ruler-v-50px.pngbin0 -> 757 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/square-purple.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/square-teal.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/square-white.pngbin0 -> 78 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/support/README4
-rw-r--r--testing/web-platform/tests/css/cssom/support/support/swatch-green.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/support/swatch-red.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-blue.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-green.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-lime.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-orange.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-red.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-teal.pngbin0 -> 156 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-white.pngbin0 -> 85 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/swatch-yellow.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/test-bl.pngbin0 -> 1368 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/test-br.pngbin0 -> 1045 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/test-inner-half-size.pngbin0 -> 180 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/test-outer.pngbin0 -> 2412 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/test-tl.pngbin0 -> 1025 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/test-tr.pngbin0 -> 1235 bytes
-rw-r--r--testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml1
-rw-r--r--testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html61
-rw-r--r--testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html56
-rw-r--r--testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html25
-rw-r--r--testing/web-platform/tests/css/cssom/variable-names.html48
-rw-r--r--testing/web-platform/tests/css/cssom/xml-stylesheet-pi-in-doctype.xhtml17
235 files changed, 10773 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html b/testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html
new file mode 100644
index 0000000000..3145467073
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSS-namespace-object-class-string.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM - Symbol.toStringTag value of CSS namespace object</title>
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-namespaces">
+<link rel="help" href="https://drafts.csswg.org/cssom/#namespacedef-css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+
+test(() => {
+ assert_own_property(CSS, Symbol.toStringTag);
+
+ const propDesc = Object.getOwnPropertyDescriptor(CSS, Symbol.toStringTag);
+ assert_equals(propDesc.value, "CSS", "value");
+ assert_equals(propDesc.writable, false, "writable");
+ assert_equals(propDesc.enumerable, false, "enumerable");
+ assert_equals(propDesc.configurable, true, "configurable");
+}, "@@toStringTag exists on the namespace object with the appropriate descriptor");
+
+test(() => {
+ assert_equals(CSS.toString(), "[object CSS]");
+ assert_equals(Object.prototype.toString.call(CSS), "[object CSS]");
+}, "Object.prototype.toString applied to the namespace object");
+
+test(t => {
+ assert_own_property(CSS, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object");
+ t.add_cleanup(() => {
+ Object.defineProperty(CSS, Symbol.toStringTag, { value: "CSS" });
+ });
+
+ Object.defineProperty(CSS, Symbol.toStringTag, { value: "Test" });
+ assert_equals(CSS.toString(), "[object Test]");
+ assert_equals(Object.prototype.toString.call(CSS), "[object Test]");
+}, "Object.prototype.toString applied after modifying the namespace object's @@toStringTag");
+
+test(t => {
+ assert_own_property(CSS, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object");
+ t.add_cleanup(() => {
+ Object.defineProperty(CSS, Symbol.toStringTag, { value: "CSS" });
+ });
+
+ assert_true(delete CSS[Symbol.toStringTag]);
+ assert_equals(CSS.toString(), "[object Object]");
+ assert_equals(Object.prototype.toString.call(CSS), "[object Object]");
+}, "Object.prototype.toString applied after deleting @@toStringTag");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html b/testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html
new file mode 100644
index 0000000000..bccfc3135a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSConditionRule-conditionText.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>CSSConditionRule.conditionText</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://drafts.csswg.org/css-conditional-3/#cssconditionrule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+@media not all {
+ :root { color: lime }
+}
+</style>
+<script>
+test(function(t) {
+ let rule = document.styleSheets[0].cssRules[0];
+ assert_true(rule instanceof CSSConditionRule);
+ assert_equals(rule.conditionText, "not all");
+ rule.conditionText = 1;
+ assert_equals(rule.conditionText, "not all");
+ rule.conditionText = "all";
+ assert_equals(rule.conditionText, "not all");
+ assert_not_equals(getComputedStyle(document.documentElement).color, "rgb(0, 255, 0)");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html b/testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html
new file mode 100644
index 0000000000..4e01c0b470
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSContainerRule.tentative.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSContainerRule</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#the-csscontainerrule-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style id="sheet"></style>
+<script>
+ const kTests = [
+ {
+ rule: "@container name (min-width: 100px) {}",
+ name: "name",
+ query: "(min-width: 100px)",
+ },
+ {
+ rule: "@container (min-width: 100px) {}",
+ name: "",
+ query: "(min-width: 100px)",
+ },
+ ];
+
+ const sheet = document.getElementById("sheet").sheet;
+ for (let { rule, name, query } of kTests) {
+ test(function() {
+ sheet.insertRule(rule, 0);
+ let r = sheet.cssRules[0];
+ assert_true(r instanceof CSSContainerRule);
+ assert_equals(r.containerName, name, "containerName");
+ assert_equals(r.containerQuery, query, "containerQuery");
+ assert_equals(r.conditionText, name ? `${name} ${query}` : query, "conditionText");
+ }, rule)
+ }
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html b/testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html
new file mode 100644
index 0000000000..afae84b0b2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSCounterStyleRule.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM - CSSCounterStyleRule interface</title>
+<link rel="help" href="https://drafts.csswg.org/css-counter-styles-3/#csscounterstylerule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @counter-style foo {
+ system: cyclic;
+ symbols: ‣;
+ suffix: " ";
+ }
+</style>
+<script>
+test(function() {
+ // TODO: The exact serialization isn't well specified (the ordering of the
+ // descriptors isn't defined).
+ let rule = document.styleSheets[0].cssRules[0];
+ assert_false(rule.cssText.includes('\n'));
+}, "CSSCounterStyleRule.cssText doesn't serialize with newlines");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSFontFaceRule.html b/testing/web-platform/tests/css/cssom/CSSFontFaceRule.html
new file mode 100644
index 0000000000..438c30df9e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSFontFaceRule.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM - CSSFontFaceRule interface</title>
+<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#cssfontfacerule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @font-face {
+ src: local("foo");
+ font-family: foo;
+ font-weight: bold;
+ }
+</style>
+<script>
+test(function() {
+ // TODO: The exact serialization isn't consistent across browsers
+ // (Firefox doesn't preserve ordering of the descriptors, WebKit/Blink does).
+ let rule = document.styleSheets[0].cssRules[0];
+ assert_false(rule.cssText.includes('\n'));
+}, "CSSFontFaceRule.cssText doesn't serialize with newlines");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html b/testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html
new file mode 100644
index 0000000000..a0ea64a503
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSFontFeatureValuesRule.html
@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>CSSOM - CSSFontFeatureValuesRule interface</title>
+<link
+ rel="help"
+ href="https://drafts.csswg.org/css-fonts-4/#om-fontfeaturevalues"
+/>
+<link rel="author" title="Dominik Röttsches" href="drott@chromium.org" />
+<meta
+ name="assert"
+ content="CSSFontFeatureValuesRule interface is accessible and supports read and write access."
+/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style></style>
+<body></body>
+<script>
+ let rule_initial = `
+ @font-feature-values test_family {
+ @annotation {
+ the_first: 6;
+ }
+ @styleset {
+ yo: 7;
+ del: 4;
+ di: 10 9 4 5;
+ }
+ }
+ `;
+
+ let rule_added = `
+ @font-feature-values second_family {
+ @annotation {
+ yabab: 2;
+ yogog: 4;
+ bidib: 5;
+ }
+ }
+ `;
+
+ let rule_overlap = `
+ @font-feature-values test_family {
+ @annotation {
+ baric: 17;
+ }
+ @ornaments {
+ fooic: 1;
+ }
+ }
+ `;
+
+ function resetStateAndTest(testFunction) {
+ var sheet = document.styleSheets[0];
+ while (sheet.cssRules.length > 0) sheet.deleteRule(0);
+ sheet.insertRule(rule_initial);
+ testFunction();
+ }
+
+ test(function () {
+ resetStateAndTest(() => {
+ let rule = document.styleSheets[0].cssRules[0];
+ assert_equals(rule.type, 14);
+ assert_equals(rule.annotation.size, 1);
+ assert_equals(rule.styleset.size, 3);
+ assert_array_equals(rule.styleset.get("yo"), [7]);
+ assert_array_equals(rule.styleset.get("del"), [4]);
+ assert_array_equals(rule.styleset.get("di"), [10, 9, 4, 5]);
+ assert_array_equals(rule.annotation.get("the_first"), [6]);
+ assert_equals(rule.stylistic.size, 0);
+ assert_equals(rule.swash.size, 0);
+ assert_equals(rule.ornaments.size, 0);
+ assert_equals(rule.characterVariant.size, 0);
+ assert_equals(rule.fontFamily, "test_family");
+ });
+ }, "CSSFontFeatureValuesRule is correctly parsed and accessible via CSSOM.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ let family_override = "test_changed_family";
+ let rule = document.styleSheets[0].cssRules[0];
+ rule.fontFamily = family_override;
+ let read_rule = document.styleSheets[0].cssRules[0];
+ assert_equals(read_rule.fontFamily, family_override);
+ });
+ }, "CSSFontFeatureValuesRule family is settable and readable.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ let rule = document.styleSheets[0].cssRules[0];
+ let override_number = 43;
+ assert_equals(rule.styleset.get("di").length, 4);
+ rule.styleset.set("di", override_number);
+ assert_array_equals(
+ document.styleSheets[0].cssRules[0].styleset.get("di"),
+ [override_number]
+ );
+ assert_equals(rule.styleset.size, 3);
+ assert_equals(
+ document.styleSheets[0].cssRules[0].styleset.get("di").length,
+ 1
+ );
+ });
+ }, "CSSFontFeatureValuesMap entries are settable to single values.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ let rule = document.styleSheets[0].cssRules[0];
+ let override_sequence = [43, 22];
+ assert_equals(
+ document.styleSheets[0].cssRules[0].styleset.get("di").length,
+ 4
+ );
+ rule.styleset.set("di", override_sequence);
+ assert_array_equals(
+ document.styleSheets[0].cssRules[0].styleset.get("di"),
+ override_sequence
+ );
+ assert_equals(document.styleSheets[0].cssRules[0].styleset.size, 3);
+ assert_equals(
+ document.styleSheets[0].cssRules[0].styleset.get("di").length,
+ 2
+ );
+ });
+ }, "CSSFontFeatureValuesMap entries are settable to sequences of numbers.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ document.styleSheets[0].insertRule(rule_added, 1);
+ assert_equals(document.styleSheets[0].cssRules[1].type, 14);
+ assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 3);
+ });
+ }, "New rules can be added.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ document.styleSheets[0].insertRule(rule_added, 1);
+ assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 3);
+ document.styleSheets[0].cssRules[1].annotation.delete("yogog");
+ assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 2);
+ });
+ }, "Deleting single entries is possible.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ document.styleSheets[0].insertRule(rule_added, 1);
+ assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 3);
+ document.styleSheets[0].cssRules[1].annotation.clear();
+ assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 0);
+ });
+ }, "Clearing all entries is possible.");
+
+ test(function () {
+ resetStateAndTest(() => {
+ assert_equals(document.styleSheets[0].cssRules.length, 1);
+ document.styleSheets[0].insertRule(rule_overlap, 1);
+ assert_equals(document.styleSheets[0].cssRules.length, 2);
+
+ // Force updating internal state.
+ getComputedStyle(document.body).borderTop;
+
+ assert_equals(
+ document.styleSheets[0].cssRules[0].annotation.size,
+ 1,
+ "1 annotation."
+ );
+ assert_equals(
+ document.styleSheets[0].cssRules[0].styleset.size,
+ 3,
+ "3 entries in styleset."
+ );
+ assert_equals(
+ document.styleSheets[0].cssRules[0].ornaments.size,
+ 0,
+ "No ornament entries."
+ );
+ assert_false(
+ document.styleSheets[0].cssRules[0].annotation.has("baric"),
+ "Annotation must not contain 'baric'."
+ );
+ assert_false(
+ document.styleSheets[0].cssRules[0].ornaments.has("fooic"),
+ "Ornaments must not contain 'fooic'."
+ );
+
+ assert_equals(document.styleSheets[0].cssRules[1].annotation.size, 1);
+ assert_equals(document.styleSheets[0].cssRules[1].ornaments.size, 1);
+ assert_false(
+ document.styleSheets[0].cssRules[1].annotation.has("the_first")
+ );
+ });
+ }, "Multiple rules for the same family are kept separate in CSSOM.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html b/testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html
new file mode 100644
index 0000000000..e4afd5e560
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSGroupingRule-cssRules.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSGroupingRule - cssRules</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ function create(t) {
+ var style = document.createElement('style');
+ style.appendChild(document.createTextNode('@media all { * {} }'));
+ document.head.appendChild(style);
+ t.add_cleanup(function() {
+ document.head.removeChild(style);
+ });
+
+ assert_true(!!style.sheet, 'setup - sheet defined');
+ assert_equals(
+ style.sheet.cssRules.length, 1, 'setup - grouping rule created'
+ );
+ assert_equals(
+ style.sheet.cssRules[0].cssRules.length, 1, 'setup - rule created'
+ );
+ return style.sheet.cssRules[0];
+ }
+
+ test(function (t) {
+ var groupingRule = create(t);
+ groupingRule.cssRules.wptMarker = 'wpt';
+
+ // `insertRule` is used to prompt non-conforming implementations to
+ // create a new CSSRuleList object. Its behavior is verified by a
+ // dedicated test and should not influence the result of this
+ // particular test.
+ try {
+ groupingRule.insertRule('.foo {}', 0);
+ groupingRule.insertRule('.bar {}', 0);
+ groupingRule.insertRule('.baz {}', 0);
+ } catch (err) {}
+
+ assert_equals(groupingRule.cssRules.wptMarker, 'wpt');
+
+ try {
+ groupingRule.deleteRule('.foo {}', 0);
+ groupingRule.deleteRule('.bar {}', 0);
+ groupingRule.deleteRule('.baz {}', 0);
+ } catch (err) {}
+
+ assert_equals(groupingRule.cssRules.wptMarker, 'wpt');
+ }, '[SameObject] is honored');
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html b/testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html
new file mode 100644
index 0000000000..d131e3975d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSGroupingRule-insertRule.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSGroupingRule - insertRule</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssgroupingrule-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ function create(t) {
+ var style = document.createElement('style');
+ style.appendChild(document.createTextNode('@media all { * {} }'));
+ document.head.appendChild(style);
+ t.add_cleanup(function() {
+ document.head.removeChild(style);
+ });
+
+ assert_true(!!style.sheet, 'setup - sheet defined');
+ assert_equals(
+ style.sheet.cssRules.length, 1, 'setup - grouping rule created'
+ );
+ assert_equals(
+ style.sheet.cssRules[0].cssRules.length, 1, 'setup - rule created'
+ );
+ return style.sheet.cssRules[0];
+ }
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+ var result;
+
+ result = groupingRule.insertRule('.foo {}', 0);
+
+ assert_equals(groupingRule.cssRules.length, 2);
+ assert_not_equals(groupingRule.cssRules[0].cssText, first);
+ assert_equals(groupingRule.cssRules[1].cssText, first);
+ assert_equals(result, 0, 'result');
+ }, 'index before first');
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+ var result;
+
+ result = groupingRule.insertRule('.foo {}', 1);
+
+ assert_equals(groupingRule.cssRules.length, 2);
+ assert_equals(groupingRule.cssRules[0].cssText, first);
+ assert_not_equals(groupingRule.cssRules[1].cssText, first);
+ assert_equals(result, 1);
+ }, 'index after final');
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+ var result;
+
+ result = groupingRule.insertRule('.foo {}');
+
+ assert_equals(groupingRule.cssRules.length, 2);
+ assert_not_equals(groupingRule.cssRules[0].cssText, first);
+ assert_equals(groupingRule.cssRules[1].cssText, first);
+ assert_equals(result, 0);
+ }, 'index not specified');
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+
+ assert_throws_dom('IndexSizeError', function() {
+ // The syntax error is intentional; it verifies that the insertion
+ // index is validated prior to the CSS text.
+ groupingRule.insertRule('???', 2);
+ });
+
+ assert_equals(groupingRule.cssRules.length, 1);
+ assert_equals(groupingRule.cssRules[0].cssText, first);
+ }, 'index exceeds length');
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+
+ assert_throws_dom('SyntaxError', function() {
+ groupingRule.insertRule('???', 0);
+ });
+
+ assert_equals(groupingRule.cssRules.length, 1);
+ assert_equals(groupingRule.cssRules[0].cssText, first);
+ }, 'CSS parsing error');
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+
+ assert_throws_dom('HierarchyRequestError', function() {
+ groupingRule.insertRule('@import url("foo.css");', 0);
+ });
+
+ assert_equals(groupingRule.cssRules.length, 1);
+ assert_equals(groupingRule.cssRules[0].cssText, first);
+ }, 'constraint violation');
+
+ test(function (t) {
+ var groupingRule = create(t);
+ var first = groupingRule.cssRules[0].cssText;
+
+ assert_throws_dom('HierarchyRequestError', function() {
+ groupingRule.insertRule('@namespace url(http://www.w3.org/1999/xhtml);', 0);
+ });
+
+ assert_equals(groupingRule.cssRules.length, 1);
+ assert_equals(groupingRule.cssRules[0].cssText, first);
+ }, 'disallowed namespace rule');
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/CSSKeyframeRule.html b/testing/web-platform/tests/css/cssom/CSSKeyframeRule.html
new file mode 100644
index 0000000000..3f6d182186
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSKeyframeRule.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset="utf-8">
+<title></title>
+<link rel="help" href="https://drafts.csswg.org/css-animations/#interface-csskeyframerule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type="text/css" id="styleElement">
+ div { animation: 3s slidein; }
+ @keyframes slidein {
+ from {
+ margin-left: 100%;
+ width: 300%;
+ }
+
+ to {
+ margin-left: 0%;
+ width: 100%;
+ }
+ }
+</style>
+<script>
+ var styleSheet = document.getElementById("styleElement").sheet;
+ var keyframesRule = styleSheet.cssRules[1];
+ var fromRule = keyframesRule.cssRules[0];
+ var toRule = keyframesRule.cssRules[1];
+
+ test(function() {
+ assert_equals(keyframesRule.name, "slidein");
+ assert_equals(typeof fromRule.style, "object");
+ assert_equals(fromRule.style.marginLeft, "100%");
+ assert_equals(fromRule.style.width, "300%");
+
+ assert_equals(typeof toRule.style, "object");
+ assert_equals(toRule.style.marginLeft, "0%");
+ assert_equals(toRule.style.width, "100%");
+
+ toRule.style.marginLeft = "-5%";
+ toRule.style.width = "50%";
+
+ assert_equals(fromRule.style.marginLeft, "100%");
+ assert_equals(fromRule.style.width, "300%");
+ assert_equals(toRule.style.marginLeft, "-5%");
+ assert_equals(toRule.style.width, "50%");
+ }, "CSSKeyframeRule: style property");
+
+ test(function() {
+ fromRule.style = "margin-left: 50%; width: 100%;";
+
+ assert_equals(fromRule.style.marginLeft, "50%", "margin-left");
+ assert_equals(fromRule.style.width, "100%", "width");
+ }, "CSSKeyframeRule: style property has [PutForwards]");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSKeyframesRule.html b/testing/web-platform/tests/css/cssom/CSSKeyframesRule.html
new file mode 100644
index 0000000000..a1adac383f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSKeyframesRule.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSKeyframesRule interface</title>
+ <link rel="help" href="https://drafts.csswg.org/css-animations/#interface-csskeyframesrule">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ @keyframes foo {
+ 0% { top: 0px; }
+ 100% { top: 200px; }
+ }
+ @keyframes empty {}
+ @keyframes indexed-access {
+ 0% { top: 0px; }
+ 100% { top: 200px; }
+ }
+ </style>
+
+ <script>
+ test(function () {
+ var keyframe = document.styleSheets[0].cssRules[0];
+ assert_equals(keyframe.name, "foo", "CSSKeyframesRule name attribute");
+ assert_equals(keyframe.cssRules.length, 2, "CSSKeyframesRule cssRule length attribute");
+ assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute");
+ assert_equals(keyframe.cssRules[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule cssRule cssText attribute");
+
+ keyframe.appendRule("50% { top: 100px; }");
+ assert_equals(keyframe.cssRules.length, 3, "CSSKeyframesRule cssRule length attribute after appendRule function");
+ assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+ assert_equals(keyframe.cssRules[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+ assert_equals(keyframe.cssRules[2].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+
+ keyframe.appendRule("0% { top: 50px; }");
+ assert_equals(keyframe.cssRules.length, 4, "CSSKeyframesRule cssRule length attribute after appendRule function");
+ assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+ assert_equals(keyframe.cssRules[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+ assert_equals(keyframe.cssRules[2].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+ assert_equals(keyframe.cssRules[3].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after appendRule function");
+
+ var find1 = keyframe.findRule("50%");
+ assert_equals(find1.cssText, "50% { top: 100px; }", "CSSKeyframesRule findRule function");
+ var find2 = keyframe.findRule("0%");
+ assert_equals(find2.cssText, "0% { top: 50px; }", "CSSKeyframesRule findRule function");
+ var find3 = keyframe.findRule("70%");
+ assert_equals(find3, null, "CSSKeyframesRule findRule function");
+
+ keyframe.deleteRule("100%");
+ assert_equals(keyframe.cssRules.length, 3, "CSSKeyframesRule cssRule length attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[1].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[2].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[3], undefined, "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+
+ keyframe.deleteRule("80%");
+ assert_equals(keyframe.cssRules.length, 3, "CSSKeyframesRule cssRule length attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[1].cssText, "50% { top: 100px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[2].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+ assert_equals(keyframe.cssRules[3], undefined, "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
+
+ var empty = document.styleSheets[0].cssRules[1];
+ empty.name = "bar";
+ assert_equals(empty.name, "bar", "CSSKeyframesRule name setter");
+ assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframesbar{}", "CSSKeyframesRule cssText attribute");
+
+ empty.name = "initial";
+ assert_equals(empty.name, "initial", "CSSKeyframesRule name setter, CSS-wide keyword");
+ assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframes\"initial\"{}", "CSSKeyframesRule cssText attribute with CSS-wide keyword name");
+
+ empty.name = "none";
+ assert_equals(empty.name, "none", "CSSKeyframesRule name setter, 'none'");
+ assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframes\"none\"{}", "CSSKeyframesRule cssText attribute with 'none' name");
+ }, 'name, cssRules, appendRule, findRule, deleteRule');
+
+ test(function () {
+ const keyframes = document.styleSheets[0].cssRules[2];
+ assert_equals(keyframes[0].cssText, "0% { top: 0px; }", "CSSKeyframesRule indexed getter [0]");
+ assert_equals(keyframes[1].cssText, "100% { top: 200px; }", "CSSKeyframesRule indexed getter [1]");
+ assert_equals(keyframes.length, 2, "CSSKeyframesRule.length");
+ }, 'indexed getter, length');
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/CSSNamespaceRule.html b/testing/web-platform/tests/css/cssom/CSSNamespaceRule.html
new file mode 100644
index 0000000000..fbbaa3c569
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSNamespaceRule.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSNamespaceRule interface</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssnamespacerule-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ @namespace svg url(http://servo);
+ @namespace url(http://servo1);
+ @namespace svg url("http://servo2");
+ </style>
+
+ <script>
+ test(function () {
+ var rules = document.styleSheets[0].cssRules;
+ assert_equals(rules[0].prefix, "svg", "CSSNamespaceRule prefix attribute");
+ assert_equals(rules[0].namespaceURI, "http://servo", "CSSNamespaceRule namespaceURI attribute");
+ assert_equals(rules[0].cssText, "@namespace svg url(\"http://servo\");", "CSSNamespaceRule cssText attribute");
+
+ assert_equals(rules[1].prefix, "", "CSSNamespaceRule prefix attribute");
+ assert_equals(rules[1].namespaceURI, "http://servo1", "CSSNamespaceRule namespaceURI attribute");
+ assert_equals(rules[1].cssText, "@namespace url(\"http://servo1\");", "CSSNamespaceRule cssText attribute");
+
+ assert_equals(rules[2].prefix, "svg", "CSSNamespaceRule prefix attribute");
+ assert_equals(rules[2].namespaceURI, "http://servo2", "CSSNamespaceRule namespaceURI attribute");
+ assert_equals(rules[2].cssText, "@namespace svg url(\"http://servo2\");", "CSSNamespaceRule cssText attribute");
+ });
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/CSSRuleList.html b/testing/web-platform/tests/css/cssom/CSSRuleList.html
new file mode 100644
index 0000000000..4605b74964
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSRuleList.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSRuleList interface</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssrulelist-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ body { width: 50%; }
+ #foo { height: 100px; }
+ </style>
+
+ <script>
+ test(function () {
+ var ruleList = document.styleSheets[0].cssRules;
+ assert_equals(ruleList.length, 2, "CSSRuleList length attribute");
+ assert_equals(ruleList[0].cssText, "body { width: 50%; }", "CSSRuleList indexed getter");
+ assert_equals(ruleList[1].cssText, "#foo { height: 100px; }", "CSSRuleList indexed getter");
+ assert_equals(ruleList[2], undefined, "CSSRuleList indexed getter");
+ assert_equals(ruleList.item(0).cssText, "body { width: 50%; }", "CSSRuleList item function");
+ assert_equals(ruleList.item(1).cssText, "#foo { height: 100px; }", "CSSRuleList item function");
+ assert_equals(ruleList.item(2), null, "CSSRuleList item function");
+ });
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html
new file mode 100644
index 0000000000..4da0a333e9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText-namespace.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CSSOM StyleRule selectorText property setter with namespaces</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstylerule-selectortext">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<style type="text/css" id="styleElement">
+@namespace url(http://www.w3.org/1999/xhtml);
+@namespace svg url(http://www.w3.org/2000/svg);
+
+svg|*.style0 { background-color: rgb(0, 0, 255) !important; }
+svg|*.style1 { background-color: rgb(255, 0, 255); }
+</style>
+
+<span>
+ <p></p>
+
+ <svg height="30" width="200" id="container" class="style1" lang="zh-CN" language segment="42 43">
+ <text x="0" y="15">SVG text</text>
+ </svg>
+</span>
+
+<script>
+ var styleSheet = document.getElementById("styleElement").sheet;
+ var rule = styleSheet.cssRules[2];
+
+ var divContainerStyle = getComputedStyle(document.getElementById("container"));
+
+ const originalStyleSelector = "svg|*.style0";
+
+ var assertColors = function(selectorMatches) {
+ assert_equals(divContainerStyle.getPropertyValue('background-color'), selectorMatches ? "rgb(0, 0, 255)" : "rgb(255, 0, 255)")
+ };
+
+ [
+ {selector: ".style1", isMatch: false, },
+ {selector: "svg|*.style1 ", isMatch: true, normalizedSelector: "svg|*.style1"},
+ {selector: "*|*.style1 ", isMatch: true, normalizedSelector: "*|*.style1"},
+ {selector: " *.style1 ", isMatch: false, normalizedSelector: ".style1"},
+ {selector: "p", isMatch: false},
+ ].forEach(function(testCase) {
+ test(function() {
+ // Check if starting with the default value.
+ assert_equals(rule.selectorText, originalStyleSelector);
+
+ this.add_cleanup(function() { rule.selectorText = originalStyleSelector; });
+
+ assertColors(false);
+
+ rule.selectorText = testCase.selector;
+
+ var expectedSelector = testCase.normalizedSelector ? testCase.normalizedSelector : testCase.selector;
+
+ assert_equals(rule.selectorText, expectedSelector);
+
+ assertColors(testCase.isMatch);
+ }, "CSSStyleRule: selectorText value: |" + testCase.selector + "| isMatch: " + testCase.isMatch);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html
new file mode 100644
index 0000000000..e29db52ec6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleRule-set-selectorText.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>CSSOM StyleRule selectorText property setter</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstylerule-selectortext">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<style type="text/css" id="styleElement">
+ .style0 { background-color: rgb(0, 0, 255) !important; }
+ .style1 { background-color: rgb(255, 0, 255); }
+</style>
+
+<span>
+ <p></p>
+ <div id="container" class="style1" lang="zh-CN" language segment="42 43">
+ </div>
+</span>
+
+<script>
+ var styleSheet = document.getElementById("styleElement").sheet;
+ var rule = styleSheet.cssRules[0];
+
+ var divContainerStyle = getComputedStyle(document.getElementById("container"));
+
+ const originalStyleSelector = ".style0";
+
+ var assertColors = function(selectorMatches) {
+ assert_equals(divContainerStyle.backgroundColor, selectorMatches ? "rgb(0, 0, 255)" : "rgb(255, 0, 255)")
+ };
+
+ test(function() {
+ assert_equals(typeof rule.selectorText, "string");
+ assert_equals(rule.selectorText, originalStyleSelector);
+ }, "CSSStyleRule: Can read selectorText value.");
+
+ [ // Invalid selector values.
+ "",
+ " ",
+ "!!",
+ "123",
+ "-",
+ "$",
+ ":",
+ "::",
+ ":::",
+ "::gibberish",
+ ":gibberish",
+ ".",
+ "#",
+ "[]",
+ "[",
+ "()",
+ "(",
+ "{}",
+ "{",
+ ].forEach(function(selector) {
+ test(function() {
+ assert_equals(rule.selectorText, originalStyleSelector);
+
+ this.add_cleanup(function() { rule.selectorText = originalStyleSelector; });
+
+ rule.selectorText = selector;
+
+ assert_equals(rule.selectorText, originalStyleSelector);
+ }, "CSSStyleRule: Invalid CSS selector: " + selector);
+ });
+
+
+ [ // Valid selector values.
+ {selector: "#container", isMatch: true},
+ {selector: "#container ", isMatch: true, normalizedSelector: "#container"},
+ {selector: " #container ", isMatch: true, normalizedSelector: "#container"},
+ {selector: ".style1", isMatch: true},
+ {selector: "div.style1", isMatch: true},
+ {selector: "div:not(#non-existing-id)", isMatch: true},
+ {selector: "div", isMatch: true},
+ {selector: "*", isMatch: true},
+
+ {selector: "#no-match", isMatch: false},
+ {selector: "ÇĞıİ", isMatch: false},
+ {selector: "🤓", isMatch: false},
+
+ {selector: "[language]", isMatch: true},
+ {selector: "[language-no]", isMatch: false},
+ {selector: "[lang=\"zh-CN\"]", isMatch: true},
+ {selector: "[lang=\"ab-CD\"]", isMatch: false},
+ {selector: "[segment~=\"43\"]", isMatch: true},
+ {selector: "[segment~=\"42\"]", isMatch: true},
+ {selector: "[lang|=\"zh\"]", isMatch: true},
+ {selector: "[lang|=\"zh-CN\"]", isMatch: true},
+ {selector: "[lang|=\"ab\"]", isMatch: false},
+ {selector: "[lang|=\"z\"]", isMatch: false},
+ {selector: "[lang^=\"z\"]", isMatch: true},
+ {selector: "[lang^=\"ab\"]", isMatch: false},
+ {selector: "[segment$=\"43\"]", isMatch: true},
+ {selector: "[segment$=\"3\"]", isMatch: true},
+ {selector: "[segment$=\"42\"]", isMatch: false},
+ {selector: "[lang*=\"-\"]", isMatch: true},
+ {selector: "[lang*=\"h-\"]", isMatch: true},
+ {selector: "[lang*=\"ab\"]", isMatch: false},
+
+ {selector: "*|div", isMatch: true, normalizedSelector: "div"},
+ {selector: "|div", isMatch: false},
+ {selector: "*|a", isMatch: false, normalizedSelector: "a"},
+ {selector: "*|*", isMatch: true, normalizedSelector: "*"},
+ {selector: "[*|lang]", isMatch: true, normalizedSelector: "[*|lang]"},
+ {selector: "[|lang]", isMatch: true, normalizedSelector: "[lang]"},
+
+ {selector: ":active", isMatch: false},
+ {selector: ":not(:active)", isMatch: true},
+ {selector: "*:not(:active)", isMatch: true, normalizedSelector: ":not(:active)"},
+ {selector: "div:not(:active)", isMatch: true},
+ {selector: "div:active", isMatch: false},
+
+ {selector: "span div", isMatch: true},
+ {selector: "span div ", isMatch: true, normalizedSelector: "span div"},
+ {selector: "span > div", isMatch: true},
+ {selector: "div div", isMatch: false},
+ {selector: "div > div", isMatch: false},
+ {selector: "p + div", isMatch: true},
+ {selector: "span + div", isMatch: false},
+ {selector: "p ~ div", isMatch: true},
+ {selector: "span ~ div", isMatch: false},
+
+ {selector: ":lang(zh-CN)", isMatch: true},
+ {selector: ":lang(zh)", isMatch: true},
+ {selector: ":lang(tr-AZ)", isMatch: false},
+
+ {selector: "::after", isMatch: false, normalizedSelector: "::after"},
+ {selector: ":after", isMatch: false, normalizedSelector: "::after"},
+ {selector: "::before", isMatch: false, normalizedSelector: "::before"},
+ {selector: ":before", isMatch: false, normalizedSelector: "::before"},
+ {selector: "::first-letter", isMatch: false, normalizedSelector: "::first-letter"},
+ {selector: ":first-letter", isMatch: false, normalizedSelector: "::first-letter"},
+ {selector: "::first-line", isMatch: false, normalizedSelector: "::first-line"},
+ {selector: ":first-line", isMatch: false, normalizedSelector: "::first-line"},
+
+ {selector: "div:focus:not([lang=\"zh-CN\"])", isMatch: false},
+ {selector: "div[lang=\"zh-CN\"]:not(:focus)", isMatch: true},
+ ].forEach(function(testCase) {
+ test(function() {
+ // Check if starting with the default value.
+ assert_equals(rule.selectorText, originalStyleSelector);
+
+ this.add_cleanup(function() { rule.selectorText = originalStyleSelector; });
+
+ assertColors(false);
+
+ rule.selectorText = testCase.selector;
+
+ var expectedSelector = testCase.normalizedSelector ? testCase.normalizedSelector : testCase.selector;
+
+ assert_equals(rule.selectorText, expectedSelector);
+
+ assertColors(testCase.isMatch);
+ }, "CSSStyleRule: selectorText value: |" + testCase.selector + "| isMatch: " + testCase.isMatch);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleRule.html b/testing/web-platform/tests/css/cssom/CSSStyleRule.html
new file mode 100644
index 0000000000..3acdfb1285
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleRule.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM CSSRule CSSStyleRule interface</title>
+ <link rel="author" title="Letitia Lew" href="mailto:lew.letitia@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-rules">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstylerule-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="All properties for this CSSStyleRule instance of CSSRule are initialized correctly">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <style id="styleElement" type="text/css">
+ div { margin: 10px; padding: 0px; }
+ </style>
+</head>
+<body>
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ var rule;
+ setup(function() {
+ var styleSheet = document.getElementById("styleElement").sheet;
+ var ruleList = styleSheet.cssRules;
+ rule = ruleList[0];
+ });
+
+ test(function() {
+ assert_true(rule instanceof CSSRule);
+ assert_true(rule instanceof CSSStyleRule);
+ }, "CSSRule and CSSStyleRule types");
+
+ test(function() {
+ assert_equals(rule.STYLE_RULE, 1);
+ assert_equals(rule.IMPORT_RULE, 3);
+ assert_equals(rule.MEDIA_RULE, 4);
+ assert_equals(rule.FONT_FACE_RULE, 5);
+ assert_equals(rule.PAGE_RULE, 6);
+ assert_equals(rule.NAMESPACE_RULE, 10);
+ assert_idl_attribute(rule, "type");
+ assert_equals(typeof rule.type, "number");
+ }, "Type of CSSRule#type and constant values");
+
+ test(function() {
+ assert_true(rule instanceof CSSRule);
+ assert_idl_attribute(rule, "cssText");
+ assert_idl_attribute(rule, "parentRule");
+ assert_idl_attribute(rule, "parentStyleSheet");
+ }, "Existence of CSSRule attributes");
+
+ test(function() {
+ assert_readonly(rule, "type");
+ assert_readonly(rule, "parentRule");
+ assert_readonly(rule, "parentStyleSheet");
+ }, "Writability of CSSRule attributes");
+
+ test(function() {
+ assert_equals(rule.type, rule.STYLE_RULE);
+ assert_equals(typeof rule.cssText, "string");
+ assert_equals(rule.cssText, "div { margin: 10px; padding: 0px; }");
+ assert_equals(rule.parentRule, null);
+ assert_true(rule.parentStyleSheet instanceof CSSStyleSheet);
+ }, "Values of CSSRule attributes");
+
+ test(function() {
+ assert_idl_attribute(rule, "selectorText");
+ assert_equals(typeof rule.selectorText, "string");
+ assert_idl_attribute(rule, "style");
+ }, "Existence and type of CSSStyleRule attributes");
+
+ test(function() {
+ // CSSStyleRule.style has PutForwards=cssText and SameObject.
+ var initial = rule.style.cssText;
+ var style = rule.style;
+
+ rule.style = "";
+ assert_equals(rule.style.cssText, "");
+ assert_equals(rule.style, style);
+
+ rule.style = "margin: 42px;";
+ assert_equals(rule.style.margin, "42px");
+ assert_equals(rule.style, style);
+
+ rule.style = initial;
+ assert_equals(rule.style, style);
+ }, "Assigning to CSSStyleRule.style assigns to cssText; CSSStyleRule.style returns the same object");
+
+ test(function() {
+ assert_equals(rule.selectorText, "div");
+ assert_true(rule.style instanceof CSSStyleDeclaration);
+ }, "Values of CSSStyleRule attributes");
+
+ test(function() {
+ assert_equals(rule.style.margin, "10px");
+ assert_equals(rule.style.padding, "0px");
+
+ rule.style.padding = "5px";
+ rule.style.border = "1px solid";
+
+ assert_equals(rule.style.padding, "5px");
+ assert_equals(rule.style.border, "1px solid");
+ }, "Mutability of CSSStyleRule's style attribute");
+
+ test(function() {
+ rule.style = "margin: 15px; padding: 2px;";
+
+ assert_equals(rule.style.margin, "15px", "margin");
+ assert_equals(rule.style.padding, "2px", "padding");
+ }, "CSSStyleRule's style has [PutForwards]");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html
new file mode 100644
index 0000000000..8997a59e9c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-baseURL.tentative.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>CSSStyleSheet baseURL</title>
+<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com">
+<link rel="help" href="https://github.com/WICG/construct-stylesheets/issues/95#issuecomment-593545252">
+<div id="target"></div>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+
+function currentLocation() {
+ const sections = location.href.split("/")
+ sections.pop();
+ return sections.join("/");
+}
+
+test(() => {
+ const span = document.createElement("span");
+ target.appendChild(span);
+ span.attachShadow({ mode: "open" })
+ const shadowDiv = document.createElement("div");
+ span.shadowRoot.appendChild(shadowDiv);
+
+ const fileName = "example.png"
+ const baseURL = `${location.origin}/custom/path/`;
+ const fullURL = `${baseURL}${fileName}`;
+
+ const sheet = new CSSStyleSheet({ baseURL });
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+
+ sheet.replaceSync(`* { background-image: url("${fileName}"); }`);
+ const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage;
+
+ sheet.replaceSync(`* { background-image: url("${fullURL}"); }`);
+ const styleFromFull = getComputedStyle(shadowDiv).backgroundImage;
+
+ assert_equals(styleFromRelative, styleFromFull);
+}, "Constructing sheet with custom base URL ueses that URL for CSS rules");
+
+test(() => {
+ const span = document.createElement("span");
+ target.appendChild(span);
+ span.attachShadow({ mode: "open" })
+ const shadowDiv = document.createElement("div");
+ span.shadowRoot.appendChild(shadowDiv);
+
+ const fileName = "example.png"
+ const baseURL = "custom/path/";
+ const fullURL = `${currentLocation()}/${baseURL}${fileName}`;
+
+ const sheet = new CSSStyleSheet({ baseURL });
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+
+ sheet.replaceSync(`* { background-image: url("${fileName}"); }`);
+ const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage;
+
+ sheet.replaceSync(`* { background-image: url("${fullURL}"); }`);
+ const styleFromFull = getComputedStyle(shadowDiv).backgroundImage;
+
+ assert_equals(styleFromRelative, styleFromFull);
+}, "Constructing sheet with relative URL adds to the constructor document's base URL");
+
+test(() => {
+ assert_throws_dom("NotAllowedError", () => { new CSSStyleSheet({ baseURL: "https://test:test/"}) });
+}, "Constructing sheet with invalid base URL throws a NotAllowedError");
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html
new file mode 100644
index 0000000000..b453323459
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Adoptedstylesheets.concat should work when starting empty</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+<span>This should be green</span><br>
+<span>This should be green</span>
+<pre>Tests:
+PASS
+PASS
+PASS
+PASS
+</pre>
+
+<style>
+ span {
+ background-color:green;
+ }
+</style>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html
new file mode 100644
index 0000000000..87ba6bfea5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-concat.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Adoptedstylesheets.concat should work when starting empty</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom/#extensions-to-the-document-or-shadow-root-interface">
+<link rel="match" href="CSSStyleSheet-constructable-concat-ref.html">
+
+<span>This should be green</span>
+<div id=host></div>
+<pre id=tests>Tests:
+</pre>
+
+<script>
+function assert_equals(val1,val2) {
+ const t = val1 === val2 ? 'PASS' : `FAIL! ${val1} !== ${val2}`;
+ tests.appendChild(document.createTextNode(`${t}\n`));
+}
+window.onload = () => {
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync('span {background-color:green;}');
+ assert_equals(document.adoptedStyleSheets.length,0);
+ document.adoptedStyleSheets = document.adoptedStyleSheets.concat([sheet]);
+ assert_equals(document.adoptedStyleSheets.length,1);
+
+ const host = document.getElementById('host');
+ const shadow = host.attachShadow({mode: 'open'});
+ shadow.innerHTML = '<span>This should be green</span>';
+ assert_equals(shadow.adoptedStyleSheets.length,0);
+ shadow.adoptedStyleSheets = shadow.adoptedStyleSheets.concat([sheet]);
+ assert_equals(shadow.adoptedStyleSheets.length,1);
+ document.documentElement.classList.remove("reftest-wait");
+};
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html
new file mode 100644
index 0000000000..72b0e15c8b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-cssRules.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSStyleSheet.replace/replaceSync() doesn't change cssRules object</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/cssom/#extensions-to-the-document-or-shadow-root-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1752392">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<span>Should be green</span>
+<script>
+promise_test(async function() {
+ const sheet = new CSSStyleSheet();
+ let rules = sheet.cssRules;
+ sheet.replaceSync('span {color:blue;}');
+ assert_equals(rules, sheet.cssRules, "Rules should be the same after replaceSync");
+ await sheet.replace('span {color: lime;}');
+ assert_equals(rules, sheet.cssRules, "Rules should be the same after replace()");
+ document.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(document.querySelector("span")).color, "rgb(0, 255, 0)", "Sheet should apply");
+}, "cssRules doesn't change on replace / replaceSync");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html
new file mode 100644
index 0000000000..ef4ea14806
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disabled-regular-sheet-insertion.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Shouldn't crash / assert when inserting a stylesheet after there are disabled constructable sheets</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://wicg.github.io/construct-stylesheets/">
+<script src = '/resources/testharness.js'></script>
+<script src = '/resources/testharnessreport.js'></script>
+<div id="host"></div>
+<script>
+test(function() {
+ let sheet = new CSSStyleSheet({ disabled: true });
+ sheet.replaceSync("div { color: red }");
+
+ let root = document.getElementById("host").attachShadow({ mode: "open" });
+ root.adoptedStyleSheets = [sheet];
+ let style = document.createElement("style");
+ root.innerHTML = `
+ <style>div { color: green }</style>
+ <div>Should be green</div>
+ `;
+ assert_equals(getComputedStyle(root.querySelector("div")).color, "rgb(0, 128, 0)", "Should insert the sheet at the right position and not crash");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html
new file mode 100644
index 0000000000..d49a084fbe
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-disallow-import.tentative.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>CSSStyleSheet Disallow Import Rules</title>
+<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com">
+<link rel="help" href="https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-597733392">
+<div id="target"></div>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script>
+
+const greenStyleText = ".green { color: green; }";
+const import_text = '@import url("support/constructable-import.css");';
+
+function attachShadowDiv(host) {
+ const shadowRoot = host.attachShadow({mode: 'open'});
+ const shadowDiv = document.createElement("div");
+ shadowRoot.appendChild(shadowDiv);
+ return shadowDiv;
+}
+
+test(() => {
+ assert_throws_dom("SyntaxError", () => { (new CSSStyleSheet).insertRule(import_text) });
+}, 'Inserting an @import rule through insertRule on a constructed stylesheet throws an exception');
+
+promise_test(async t => {
+ const importUrl = "support/constructable-import.css";
+ const sheet = new CSSStyleSheet();
+
+ sheet.replaceSync(import_text);
+ await sheet.replace(import_text);
+ assert_throws_dom("SyntaxError", () => { sheet.insertRule(import_text) });
+
+ const timeAfterImportsInvoked = performance.now();
+
+ let link = document.createElement("link");
+ t.add_cleanup(() => { link.remove(); });
+ link.rel = "stylesheet";
+ link.href = importUrl;
+
+ await new Promise((resolve, reject) => {
+ link.addEventListener("load", resolve);
+ link.addEventListener("error", reject);
+ document.body.appendChild(link);
+ });
+
+ let entries = window.performance.getEntriesByType('resource').filter(entry => entry.name.includes(importUrl));
+ assert_equals(entries.length, 1, "There should be only one entry for the import URL");
+ assert_greater_than_equal(entries[0].startTime, timeAfterImportsInvoked, "The entry's start time should be after all throws");
+}, "@import rules should not trigger any loads.")
+
+promise_test(() => {
+ const span = document.createElement("span");
+ target.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ shadowDiv.classList.add("green");
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ const sheet_promise = sheet.replace(`${import_text} ${greenStyleText}`);
+ return sheet_promise.then((sheet) => {
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, greenStyleText);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 128, 0)");
+ }).catch((reason) => {
+ assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`);
+ });
+}, '@import rules are not parsed in CSSStyleSheet.replace');
+
+test(() => {
+ const span = document.createElement("span");
+ target.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ shadowDiv.classList.add("green");
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ // Replace and assert that the imported rule is applied.
+ const sheet_promise = sheet.replaceSync(`${import_text} ${greenStyleText}`);
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, greenStyleText);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 128, 0)");
+}, '@import rules are not parsed in CSSStyleSheet.replaceSync');
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html
new file mode 100644
index 0000000000..579f5d0fce
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-duplicate.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<title>Cascade order of a stylesheet for duplicate sheets.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://wicg.github.io/construct-stylesheets/">
+<script src = '/resources/testharness.js'></script>
+<script src = '/resources/testharnessreport.js'></script>
+<div id="target"></div>
+<script>
+
+function attachShadowDiv(host) {
+ const shadowRoot = host.attachShadow({mode: 'open'});
+ const shadowDiv = document.createElement("div");
+ shadowRoot.appendChild(shadowDiv);
+ return shadowDiv;
+}
+
+function blueSheetsWithIncreasingZIndex(n) {
+ let sheets = [];
+ for (let i = 0; i < n; ++i) {
+ sheets.push(new CSSStyleSheet());
+ sheets[i].replaceSync("div { z-index: " + i + "; color: blue; }");
+ }
+ return sheets;
+}
+
+let sheets = [];
+
+test(function() {
+ sheets = blueSheetsWithIncreasingZIndex(2);
+
+ document.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1]];
+ assert_equals(getComputedStyle(document.querySelector("div")).zIndex, "1", "duplicate stylesheet should take right position in the cascade");
+
+ document.adoptedStyleSheets = [];
+}, "Duplicate stylesheets have the right cascade position in the Document");
+
+test(function() {
+ sheets = blueSheetsWithIncreasingZIndex(2);
+
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync("div { color: red; }");
+
+ document.adoptedStyleSheets = [sheets[1], sheets[0]];
+ assert_equals(getComputedStyle(document.querySelector("div")).zIndex, "0", "backmost stylesheet should take precedence");
+ assert_equals(getComputedStyle(document.querySelector("div")).color, "rgb(0, 0, 255)", "backmost stylesheet should take precedence");
+
+ document.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1], sheet];
+ assert_equals(getComputedStyle(document.querySelector("div")).zIndex, "1", "duplicate stylesheet should take the right position in the cascade");
+ assert_equals(getComputedStyle(document.querySelector("div")).color, "rgb(255, 0, 0)", "backmost stylesheet should take precedence");
+
+ document.adoptedStyleSheets = [];
+}, "Appending duplicate stylesheets yields the correct cascade position in the Document");
+
+test(function() {
+ sheets = blueSheetsWithIncreasingZIndex(2);
+
+ const span = document.createElement("span");
+ target.appendChild(span);
+ attachShadowDiv(span);
+
+ span.shadowRoot.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1]];
+ assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).zIndex, "1", "duplicate stylesheet should take right position in the cascade");
+}, "Duplicate stylesheets have the right cascade position in the ShadowRoot");
+
+test(function() {
+ sheets = blueSheetsWithIncreasingZIndex(2);
+
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync("div { color: red; }");
+
+ const span = document.createElement("span");
+ target.appendChild(span);
+ attachShadowDiv(span);
+
+ span.shadowRoot.adoptedStyleSheets = [sheets[1], sheets[0]];
+ assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).zIndex, "0", "backmost stylesheet should take precedence");
+ assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).color, "rgb(0, 0, 255)", "backmost stylesheet should take precedence");
+
+ span.shadowRoot.adoptedStyleSheets = [sheets[1], sheets[0], sheets[1], sheet];
+ assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).zIndex, "1", "duplicate stylesheet should take right position in the cascade");
+ assert_equals(getComputedStyle(span.shadowRoot.querySelector("div")).color, "rgb(255, 0, 0)", "backmost stylesheet should take precedence");
+}, "Appending duplicate stylesheets yields the correct cascade position in the ShadowRoot");
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-cssRules.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-cssRules.html
new file mode 100644
index 0000000000..adb1b5f709
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-cssRules.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSStyleSheet.replace reflects the right cssRules.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1864815">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p></p>
+<script>
+async function runTest(sync) {
+ let sheet = new CSSStyleSheet();
+ let rules = sheet.cssRules;
+ function replace(text) {
+ return sync ? sheet.replaceSync(text) : sheet.replace(text);
+ }
+ function assert_color(color) {
+ assert_equals(sheet.cssRules, rules, "StyleSheet.cssRules is [SameObject]");
+ assert_equals(rules.length, 1, "Should have one rule");
+ assert_equals(rules[0].style.color, color, "Should be the right css rule");
+ }
+ async function replace_and_test(color) {
+ await replace(`p { color: ${color} }`);
+ assert_equals(sheet.cssRules, rules, "StyleSheet.cssRules is [SameObject]");
+ assert_equals(rules.length, 1, "Should have one rule");
+ assert_equals(rules[0].style.color, color, "Should be the right css rule");
+ }
+
+ await replace_and_test("red");
+ await replace_and_test("green");
+ document.adoptedStyleSheets.push(sheet);
+ assert_equals(getComputedStyle(document.querySelector("p")).color, "rgb(0, 128, 0)", "Sheet should apply");
+ document.adoptedStyleSheets.pop(sheet);
+ assert_not_equals(getComputedStyle(document.querySelector("p")).color, "rgb(0, 128, 0)", "Sheet should stop applying");
+}
+
+for (let sync of [true, false]) {
+ promise_test(() => runTest(sync), `cssRules tests (sync: ${sync})`);
+}
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html
new file mode 100644
index 0000000000..76bee452a6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable-replace-on-regular-sheet.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>replace / replaceSync on non-constructed stylesheet</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://wicg.github.io/construct-stylesheets/">
+<script src = '/resources/testharness.js'></script>
+<script src = '/resources/testharnessreport.js'></script>
+<style id="style">
+ @import url("support/constructable-import.css");
+ :root { background-color: lime }
+</style>
+<script>
+
+promise_test(async () => {
+ await new Promise(resolve => window.addEventListener("load", resolve));
+ assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "precondition")
+ let sheet = document.styleSheets[0];
+ let childSheet = sheet.cssRules[0].styleSheet;
+ assert_throws_dom("NotAllowedError", () => sheet.replaceSync(":root { background-color: red }"), "replaceSync on non-constructed sheet should throw");
+ assert_throws_dom("NotAllowedError", () => childSheet.replaceSync(":root { background-color: red }"), "replaceSync on non-constructed sheet should throw");
+ assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "old sheet should still apply after replace");
+}, "CSSStyleSheet.replaceSync throws NotAllowedError for non-constructed sheets")
+
+promise_test(async function(t) {
+ assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "precondition")
+ let sheet = document.styleSheets[0];
+ let childSheet = sheet.cssRules[0].styleSheet;
+ await promise_rejects_dom(t, "NotAllowedError", sheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise");
+ await promise_rejects_dom(t, "NotAllowedError", childSheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise");
+ assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "old sheet should still apply after replace");
+}, "CSSStyleSheet.replace returns a rejected promise for non-constructed sheets")
+
+promise_test(async function(t) {
+ assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 255, 0)", "precondition")
+ let sheet = document.styleSheets[0];
+ let childSheet = sheet.cssRules[0].styleSheet;
+ style.remove() // sheet's associated document becomes null.
+ await promise_rejects_dom(t, "NotAllowedError", sheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise");
+ await promise_rejects_dom(t, "NotAllowedError", childSheet.replace(":root { background-color: red }"), "replace on non-constructed sheet should return a rejected promise");
+ assert_equals(getComputedStyle(document.documentElement).backgroundColor, "rgba(0, 0, 0, 0)", "old sheet was removed, so the default color should apply");
+}, "CSSStyleSheet.replace returns a rejected promise for non-constructed sheets that have no associated document")
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html
new file mode 100644
index 0000000000..f84e4ea9af
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-constructable.html
@@ -0,0 +1,801 @@
+<!DOCTYPE html>
+<title>CSSStyleSheet constructor and adoptedStyleSheets</title>
+<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org">
+<link rel="help" href="https://wicg.github.io/construct-stylesheets/">
+<script src = '/resources/testharness.js'></script>
+<script src = '/resources/testharnessreport.js'></script>
+
+<section id="firstSection">
+ <div>
+ <span class="green"></span>
+ <span class="red"></span>
+ <span class="blue"></span>
+ <span class="white"></span>
+ <span class="yellow"></span>
+ </div>
+</section>
+<section id="shadowHost"></section>
+<section id="thirdSection"></section>
+
+<script>
+'use strict';
+const greenStyleText = ".green { color: green; }";
+const redStyleTexts = [".red { color: red; }", ".red + span + span { color: red; }"];
+const blueStyleTexts = [".blue { color: blue; }", ".blue + span + span { color: blue; }"];
+const whiteStyleText = "* { color: white; }";
+const yellowStyleText = ".yellow { color: yellow; }";
+
+const firstDiv = document.querySelector('#firstSection > div');
+const secondDiv = firstDiv.cloneNode(true);
+const shadowHost = document.querySelector('#shadowHost');
+const shadowRoot = shadowHost.attachShadow({mode: 'open'});
+shadowRoot.appendChild(secondDiv);
+
+const greenSpan = firstDiv.children[0];
+const redSpan = firstDiv.children[1];
+const blueSpan = firstDiv.children[2];
+const whiteSpan = firstDiv.children[3];
+const yellowSpan = firstDiv.children[4];
+const greenShadowSpan = secondDiv.children[0];
+const redShadowSpan = secondDiv.children[1];
+const blueShadowSpan = secondDiv.children[2];
+const whiteShadowSpan = secondDiv.children[3];
+const yellowShadowSpan = secondDiv.children[4];
+
+test(() => {
+ assert_equals(document.adoptedStyleSheets.length, 0);
+}, "document.adoptedStyleSheets should initially have length 0.");
+
+test(() => {
+ const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"});
+ assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string");
+ assert_equals(sheet.ownerNode, null);
+ assert_equals(sheet.ownerRule, null);
+ assert_equals(sheet.media.length, 2);
+ assert_equals(sheet.media.item(0), "screen");
+ assert_equals(sheet.media.item(1), "print");
+ assert_true(sheet.disabled);
+ assert_equals(sheet.cssRules.length, 0);
+
+ sheet.insertRule(redStyleTexts[0]);
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]);
+
+ sheet.insertRule(redStyleTexts[1]);
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]);
+
+ const sheet2 = new CSSStyleSheet({});
+ assert_equals(sheet2.title, null, "The title attribute must return the title or null if title is the empty string");
+ assert_equals(sheet2.ownerNode, null);
+ assert_equals(sheet2.ownerRule, null);
+ assert_equals(sheet2.media.length, 0);
+ assert_false(sheet2.disabled);
+ assert_equals(sheet2.cssRules.length, 0);
+
+ sheet2.insertRule(redStyleTexts[1]);
+ assert_equals(sheet2.cssRules.length, 1);
+ assert_equals(sheet2.cssRules[0].cssText, redStyleTexts[1]);
+
+ sheet2.deleteRule(0);
+ assert_equals(sheet2.cssRules.length, 0);
+
+ const sheet3 = new CSSStyleSheet();
+ assert_equals(sheet3.title, null, "The title attribute must return the title or null if title is the empty string");
+ assert_equals(sheet3.ownerNode, null);
+ assert_equals(sheet3.ownerRule, null);
+ assert_equals(sheet3.media.length, 0);
+ assert_false(sheet3.disabled);
+ assert_equals(sheet3.cssRules.length, 0);
+
+ sheet3.insertRule(redStyleTexts[1]);
+ assert_equals(sheet3.cssRules.length, 1);
+ assert_equals(sheet3.cssRules[0].cssText, redStyleTexts[1]);
+
+ sheet3.deleteRule(0);
+ assert_equals(sheet3.cssRules.length, 0);
+}, 'new CSSStyleSheet produces empty CSSStyleSheet');
+
+test(() => {
+ const sheet = new CSSStyleSheet({title: "something"});
+ assert_equals(sheet.title, null, "title and alternate are not supported by the constructor. https://github.com/WICG/construct-stylesheets/issues/105");
+}, "title can be set in the CSSStyleSheet constructor");
+
+promise_test(() => {
+ const sheet = new CSSStyleSheet({disabled: true, media: "screen, print"});
+ const promise_sheet = sheet.replace(redStyleTexts[0]);
+ return promise_sheet.then(function(sheet) {
+ assert_equals(sheet.title, null, "The title attribute must return the title or null if title is the empty string");
+ assert_equals(sheet.ownerNode, null);
+ assert_equals(sheet.ownerRule, null);
+ assert_equals(sheet.media.length, 2);
+ assert_equals(sheet.media.item(0), "screen");
+ assert_equals(sheet.media.item(1), "print");
+ assert_true(sheet.disabled);
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]);
+
+ sheet.insertRule(redStyleTexts[1]);
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]);
+ });
+}, 'CSSStyleSheet.replace produces Promise<CSSStyleSheet>');
+
+function createAllSheetsPromise() {
+ const greenSheet = new CSSStyleSheet();
+ const redSheet = new CSSStyleSheet({media: "screen, print"});
+ const blueSheet = new CSSStyleSheet({disabled: true});
+ const whiteSheet = new CSSStyleSheet({disabled: true});
+ const yellowSheet = new CSSStyleSheet({disabled: false});
+
+ const greenPromise = greenSheet.replace(greenStyleText);
+ const redPromise = redSheet.replace(redStyleTexts[0] + redStyleTexts[1]);
+ const bluePromise = blueSheet.replace(blueStyleTexts[0] + blueStyleTexts[1]);
+ const whitePromise = whiteSheet.replace(whiteStyleText);
+ const yellowPromise = yellowSheet.replace(yellowStyleText);
+ return [greenPromise, redPromise, bluePromise, whitePromise, yellowPromise];
+}
+
+promise_test(() => {
+ return Promise.all(createAllSheetsPromise()).then(values => {
+ const greenStyleSheet = values[0];
+ const redStyleSheet = values[1];
+ const blueStyleSheet = values[2];
+ const whiteStyleSheet = values[3];
+ const yellowStyleSheet = values[4];
+
+ // Lists of style sheets can be created, assigned and read.
+
+ // disabled stylesheets aren't applied
+ document.adoptedStyleSheets = [whiteStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ // disable dsheets don't block other styles from applying
+ document.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet];
+
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)");
+
+ document.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 128, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(255, 255, 0)");
+ document.adoptedStyleSheets = [];
+ });
+}, 'Constructed style sheets can be applied on document');
+
+promise_test(() => {
+ return Promise.all(createAllSheetsPromise()).then(values => {
+ const greenStyleSheet = values[0];
+ const redStyleSheet = values[1];
+ const blueStyleSheet = values[2];
+ const whiteStyleSheet = values[3];
+ const yellowStyleSheet = values[4];
+ shadowRoot.adoptedStyleSheets = [whiteStyleSheet];
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)");
+
+ shadowRoot.adoptedStyleSheets = [greenStyleSheet, blueStyleSheet];
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)");
+
+ shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet];
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)");
+
+ shadowRoot.adoptedStyleSheets = [redStyleSheet, yellowStyleSheet, greenStyleSheet, blueStyleSheet];
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(255, 255, 0)");
+ });
+}, 'Constructed style sheets can be applied on shadow root');
+
+promise_test(() => {
+ return Promise.all(createAllSheetsPromise()).then(values => {
+ const greenStyleSheet = values[0];
+ const redStyleSheet = values[1];
+ shadowRoot.adoptedStyleSheets = [greenStyleSheet];
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies connected");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(0, 0, 0)", "Style applies when connected");
+ let hostParent = shadowHost.parentNode;
+ hostParent.removeChild(shadowHost);
+ assert_equals(getComputedStyle(greenShadowSpan).color, "", "Style doesn't apply when detached");
+ assert_equals(getComputedStyle(redShadowSpan).color, "", "Style doesn't apply when detached");
+ shadowRoot.adoptedStyleSheets = [redStyleSheet, greenStyleSheet];
+ hostParent.appendChild(shadowHost);
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 128, 0)", "Style applies after reattach");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)", "Style applies after reattach");
+ });
+}, 'Re-attaching shadow host with adopted stylesheets work');
+
+test(() => {
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync(":host { color: red; }");
+ const host = document.createElement("div");
+ let sr = host.attachShadow({mode: "open"});
+ sr.adoptedStyleSheets = [sheet];
+ document.body.appendChild(host);
+ assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies when connected");
+ sheet.replaceSync(":host { color: blue; }");
+ assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Style update applies when connected");
+}, 'Attaching a shadow root that already has adopted stylesheets work');
+
+test(() => {
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync(":host([red]) { color: red; } :host(.blue) { color: blue; }");
+ const host = document.createElement("div");
+ host.toggleAttribute("red");
+ document.body.appendChild(host);
+ assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "No style applies yet");
+
+ let sr = host.attachShadow({mode: "open"});
+ sr.adoptedStyleSheets = [sheet];
+
+ assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after adding style");
+ document.body.removeChild(host);
+ document.body.appendChild(host);
+ assert_equals(getComputedStyle(host).color, "rgb(255, 0, 0)", "Style applies after reattachment");
+ host.toggleAttribute("red");
+ assert_equals(getComputedStyle(host).color, "rgb(0, 0, 0)", "Attribute updates to the element after reattachment apply");
+ host.classList.toggle("blue");
+ assert_equals(getComputedStyle(host).color, "rgb(0, 0, 255)", "Class updates to the element after reattachment apply");
+
+}, "Re-attaching shadow host and updating attributes work");
+
+promise_test(() => {
+ const plainSheet = new CSSStyleSheet();
+ const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]);
+ return redStyleSheetPromise.then(function(redStyleSheet) {
+ document.adoptedStyleSheets = [redStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ redStyleSheet.insertRule(redStyleTexts[1]);
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ redStyleSheet.deleteRule(1);
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ redStyleSheet.cssRules[0].style.color = "white";
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 255, 255)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+});
+}, 'Changes to constructed stylesheets through CSSOM is reflected');
+
+promise_test(() => {
+ const plainSheet = new CSSStyleSheet();
+ const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]);
+ return redStyleSheetPromise.then(function(redStyleSheet) {
+ document.adoptedStyleSheets = [redStyleSheet];
+ shadowRoot.adoptedStyleSheets = [redStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)");
+
+ shadowRoot.adoptedStyleSheets[0].insertRule(redStyleTexts[1]);
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ assert_equals(getComputedStyle(greenShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueShadowSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteShadowSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowShadowSpan).color, "rgb(0, 0, 0)");
+ document.adoptedStyleSheets = [];
+ });
+}, 'Constructed stylesheet can be used and modified in multiple TreeScopes');
+
+promise_test(() => {
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ const thirdDiv = firstDiv.cloneNode(true);
+ iframe.contentDocument.body.appendChild(thirdDiv);
+ const greenIframeSpan = thirdDiv.children[0];
+ const redIframeSpan = thirdDiv.children[1];
+ const blueIframeSpan = thirdDiv.children[2];
+ const whiteIframeSpan = thirdDiv.children[3];
+ const yellowIframeSpan = thirdDiv.children[4];
+
+ const plainSheet = new CSSStyleSheet();
+ const redStyleSheetPromise = plainSheet.replace(redStyleTexts[0]);
+ return redStyleSheetPromise.then(function(redStyleSheet) {
+ assert_throws_dom(
+ 'NotAllowedError',
+ iframe.contentWindow.DOMException,
+ () => { iframe.contentDocument.adoptedStyleSheets = [redStyleSheet]; }
+ );
+ assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)");
+
+ document.adoptedStyleSheets = [redStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ document.adoptedStyleSheets[0].insertRule(redStyleTexts[1]);
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+ });
+}, 'Stylesheets constructed on the main Document cannot be used in iframes');
+
+promise_test(async () => {
+ const iframe = document.createElement("iframe");
+ const iframeLoaded = new Promise(resolve => iframe.addEventListener("load", resolve));
+ document.body.appendChild(iframe);
+ await iframeLoaded;
+ const thirdDiv = firstDiv.cloneNode(true);
+ iframe.contentDocument.body.appendChild(thirdDiv);
+ const greenIframeSpan = thirdDiv.children[0];
+ const redIframeSpan = thirdDiv.children[1];
+ const blueIframeSpan = thirdDiv.children[2];
+ const whiteIframeSpan = thirdDiv.children[3];
+ const yellowIframeSpan = thirdDiv.children[4];
+
+ // Make sure both the main Document and the iframe are not styled
+ const emptyStyleSheet = new CSSStyleSheet();
+ document.adoptedStyleSheets = [emptyStyleSheet];
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)");
+
+ const iframePlainSheet = new iframe.contentWindow.CSSStyleSheet();
+ const iframeRedStyleSheetPromise = iframePlainSheet.replace(redStyleTexts[0]);
+ return iframeRedStyleSheetPromise.then(function(iframeRedStyleSheet) {
+ assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeRedStyleSheet]; });
+ assert_equals(getComputedStyle(greenSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(blueSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowSpan).color, "rgb(0, 0, 0)");
+
+ iframe.contentDocument.adoptedStyleSheets = [iframeRedStyleSheet];
+ assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)");
+
+ iframe.contentDocument.adoptedStyleSheets[0].insertRule(redStyleTexts[1]);
+ assert_equals(getComputedStyle(greenIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(redIframeSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(blueIframeSpan).color, "rgb(0, 0, 0)");
+ assert_equals(getComputedStyle(whiteIframeSpan).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(yellowIframeSpan).color, "rgb(0, 0, 0)");
+ });
+}, 'Stylesheet constructed on iframe cannot be used in the main Document');
+</script>
+
+<div id="divNonConstructed" class="nonConstructed">
+</div>
+
+<script>
+`use strict`;
+const shadowRootNonConstructed = divNonConstructed.attachShadow({mode:'open'})
+nonConstructedStyle = document.createElement("style");
+shadowRootNonConstructed.appendChild(nonConstructedStyle);
+nonConstructedStyle.sheet.insertRule(".nonConstructed { color: red; }", 0);
+const nonConstructedStyleSheet = nonConstructedStyle.sheet;
+
+test(() => {
+ assert_equals(getComputedStyle(divNonConstructed).color, "rgb(0, 0, 0)");
+ assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [nonConstructedStyleSheet]; });
+}, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet is in the same document tree as the AdoptedStyleSheets');
+
+test(() => {
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ iframeDiv = iframe.contentDocument.createElement("div");
+ iframeDiv.classList.add("nonConstructed");
+ iframe.contentDocument.body.appendChild(iframeDiv);
+
+ assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)");
+ assert_throws_dom('NotAllowedError', iframe.contentWindow.DOMException, () => {
+ iframe.contentDocument.adoptedStyleSheets = [nonConstructedStyleSheet];
+ });
+ assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)");
+
+ iframeStyle = iframe.contentDocument.createElement("style");
+ iframe.contentDocument.body.appendChild(iframeStyle);
+ iframeStyle.sheet.insertRule(".nonConstructedSpan { color: red; }");
+ const iframeStyleSheet = iframeStyle.sheet;
+ nonConstructedSpan = document.createElement("span");
+ nonConstructedSpan.classList.add(".nonConstructedSpan");
+ divNonConstructed.appendChild(nonConstructedSpan);
+
+ assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)");
+ assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets = [iframeStyleSheet]; });
+ assert_equals(getComputedStyle(iframeDiv).color, "rgb(0, 0, 0)");
+}, 'Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed when the owner document of the stylesheet and the AdoptedStyleSheets are in different document trees');
+
+function attachShadowDiv(host) {
+ const shadowRoot = host.attachShadow({mode: 'open'});
+ const shadowDiv = document.createElement("div");
+ shadowRoot.appendChild(shadowDiv);
+ return shadowDiv;
+}
+
+test(() => {
+ const sheet = new CSSStyleSheet();
+ assert_equals(sheet.cssRules.length, 0);
+
+ sheet.replaceSync(redStyleTexts[0])
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(redStyleTexts[0], sheet.cssRules[0].cssText);
+
+ sheet.replaceSync(redStyleTexts[1]);
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(redStyleTexts[1], sheet.cssRules[0].cssText);
+}, 'CSSStyleSheet.replaceSync replaces stylesheet text synchronously');
+
+test(() => {
+ // Attach a div inside a shadow root with the class ".red".
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ shadowDiv.classList.add("red");
+ // Create empty stylesheet.
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ // Replace the stylesheet text that will color it red.
+ sheet.replaceSync(redStyleTexts[0]);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)");
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, redStyleTexts[0]);
+ sheet.insertRule(redStyleTexts[1]);
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules[0].cssText, redStyleTexts[1]);
+}, 'CSSStyleSheet.replaceSync correctly updates the style of its adopters synchronously');
+
+test(() => {
+ // Attach a div inside a shadow root with the class ".red".
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ shadowDiv.classList.add("target");
+
+ // Create empty stylesheet.
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+
+ // Replace the stylesheet text that will color it red.
+ sheet.replaceSync(".target { color: red; }");
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)");
+
+ // Create a style element that will set colors to white.
+ const style = document.createElement("style");
+ style.textContent = ".target { color: white; }";
+ span.shadowRoot.appendChild(style)
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles");
+
+ span.shadowRoot.adoptedStyleSheets = [];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect");
+
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style");
+
+ sheet.disabled = true;
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect");
+
+ sheet.disabled = false;
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again");
+}, 'Adopted sheets are ordered after non-adopted sheets in the shadow root')
+
+test(() => {
+ // Attach a div inside a shadow root with the class ".red".
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ span.classList.add("target");
+
+ // Create empty stylesheet.
+ const sheet = new CSSStyleSheet();
+ document.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(span).color, "rgb(0, 0, 0)");
+
+ // Replace the stylesheet text that will color it red.
+ sheet.replaceSync(".target { color: red; }");
+ assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)");
+
+ // Create a style element that will set colors to white.
+ const style = document.createElement("style");
+ style.textContent = ".target { color: white; }";
+ span.appendChild(style)
+ assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "non-adopted styles should be ordered before adopted styles");
+
+ document.adoptedStyleSheets = [];
+ assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with no adopted styles in conflict, the non-adopted style should take effect");
+
+ document.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted style should be ordered after the non-adopted style");
+
+ sheet.disabled = true;
+ assert_equals(getComputedStyle(span).color, "rgb(255, 255, 255)", "with the adopted sheet disabled, the non-adopted style should take effect");
+
+ sheet.disabled = false;
+ assert_equals(getComputedStyle(span).color, "rgb(255, 0, 0)", "the adopted sheet re-enabled, it should take effect again")
+}, 'Adopted sheets are ordered after non-adopted sheets in the document')
+
+const import_text = '@import url("support/constructable-import.css");';
+
+test(() => {
+ assert_throws_dom("SyntaxError", () => { (new CSSStyleSheet).insertRule(import_text) });
+}, 'Inserting an @import rule through insertRule on a constructed stylesheet throws an exception');
+
+promise_test(t => {
+ const importUrl = "support/constructable-import.css";
+ const sheet = new CSSStyleSheet();
+
+ sheet.replaceSync(`@import url("${importUrl}");`);
+
+ const timeAfterReplaceSync = performance.now();
+ let link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = importUrl;
+
+ return new Promise(resolve => {
+ link.addEventListener("error", t.unreached_func("Load shouldn't fail"));
+ link.addEventListener("load", t.step_func(() => {
+ let entries = window.performance.getEntriesByType('resource').filter(entry => entry.name.includes(importUrl));
+ assert_equals(entries.length, 1, "There should be only one entry for the import URL");
+ assert_greater_than_equal(entries[0].startTime, timeAfterReplaceSync, "The entry's start time should be after replaceSync threw");
+ link.remove();
+ resolve();
+ }));
+ document.body.appendChild(link);
+ });
+}, "CSSStyleSheet.replaceSync should not trigger any loads from @import rules")
+
+promise_test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ // Replace and assert that the imported rule is NOT applied.
+ const sheet_promise = sheet.replace(import_text);
+ return sheet_promise.then((sheet) => {
+ // replace() ignores @import rules:
+ assert_equals(sheet.cssRules.length, 0);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ }).catch((reason) => {
+ assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`);
+ });
+}, 'CSSStyleSheet.replace allows, but ignores, import rule inside');
+
+promise_test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ const targetSpan = document.createElement("span");
+ targetSpan.classList.add("target");
+ shadowDiv.appendChild(targetSpan);
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ // Replace and assert that the imported rule is NOT applied, but regular rule does apply.
+ const sheet_promise = sheet.replace(import_text + ".target { color: blue; }");
+ return sheet_promise.then((sheet) => {
+ assert_equals(sheet.cssRules.length, 1);
+ // @import not applied:
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ // regular rule applied:
+ assert_equals(getComputedStyle(targetSpan).color, "rgb(0, 0, 255)");
+ }).catch((reason) => {
+ assert_unreached(`Promise was rejected (${reason}) when it should have been resolved`);
+ });
+}, 'CSSStyleSheet.replace ignores @import rule but still loads other rules');
+
+test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ // Replace and assert that the imported rule is NOT applied.
+ try {
+ sheet.replaceSync(import_text);
+ // replaceSync() ignores @import rules:
+ assert_equals(sheet.cssRules.length, 0);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+ } catch(reason) {
+ assert_unreached(`replaceSync threw an exception (${reason}) when it shouldn't have`);
+ }
+}, 'CSSStyleSheet.replaceSync allows, but ignores, import rule inside');
+
+promise_test(() => {
+ const sheet = new CSSStyleSheet();
+ const sheet_promise = sheet.replace("@import url('not-there.css');");
+
+ return sheet_promise.then((sheet) => {
+ // No exception here
+ }).catch((reason) => {
+ assert_unreached("Promise was rejected");
+ });
+}, 'CSSStyleSheet.replace does not reject on failed imports');
+
+test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+
+ const newSpan = span.cloneNode(true);
+ assert_equals(newSpan.shadowRoot, null);
+}, 'Cloning a shadow host will not clone shadow root, and also adoptedStyleSheets');
+
+test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ const newSpan = iframe.contentDocument.importNode(span, true);
+ iframe.contentDocument.body.appendChild(newSpan);
+ assert_equals(newSpan.shadowRoot, null);
+}, 'Importing a shadow host will not copy shadow root, and also adoptedStyleSheets');
+
+test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync("* { color: red; }");
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)");
+
+ document.adoptNode(span);
+ assert_equals(span.shadowRoot.adoptedStyleSheets.length, 1);
+ assert_equals(span.shadowRoot.adoptedStyleSheets[0], sheet);
+
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ iframe.contentDocument.adoptNode(span);
+ iframe.contentDocument.body.appendChild(span);
+ assert_not_equals(span.shadowRoot, null);
+ assert_equals(span.shadowRoot.adoptedStyleSheets.length, 0);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+}, 'Adopting a shadow host will empty adoptedStyleSheets if adopting to a different document');
+
+test(() => {
+ const span = document.createElement("span");
+ const div = document.createElement("div");
+ thirdSection.appendChild(span);
+ span.appendChild(div);
+ const shadowDiv = attachShadowDiv(div);
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync("* { color: red; }");
+ div.shadowRoot.adoptedStyleSheets = [sheet];
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(255, 0, 0)");
+
+ document.adoptNode(span);
+ assert_equals(div.shadowRoot.adoptedStyleSheets.length, 1);
+ assert_equals(div.shadowRoot.adoptedStyleSheets[0], sheet);
+
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ iframe.contentDocument.adoptNode(span);
+ iframe.contentDocument.body.appendChild(span);
+ assert_not_equals(div.shadowRoot, null);
+ assert_equals(div.shadowRoot.adoptedStyleSheets.length, 0);
+ assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
+}, `Adopting a shadow host's ancestor will empty adoptedStyleSheets if adopting to a different document`);
+
+test(() => {
+ const host = document.createElement("div");
+ const root = host.attachShadow({mode: "open"});
+ root.adoptedStyleSheets = [new CSSStyleSheet()];
+ document.body.offsetTop;
+}, 'Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.');
+
+test(() => {
+ const host = document.createElement("div");
+ thirdSection.appendChild(host);
+ const root = host.attachShadow({mode: "open"});
+ const sheet = new CSSStyleSheet();
+ root.adoptedStyleSheets = [sheet];
+ host.remove();
+ sheet.replaceSync('');
+}, 'Modifying an adopted stylesheet on a disconnected shadow root should not crash.');
+
+function currentLocation() {
+ const sections = location.href.split("/")
+ sections.pop();
+ return sections.join("/");
+}
+
+test(() => {
+ const span = document.createElement("span");
+ thirdSection.appendChild(span);
+ const shadowDiv = attachShadowDiv(span);
+
+ const fileName = "example.png"
+ const fullPath = `${currentLocation()}/${fileName}`
+
+ const sheet = new CSSStyleSheet();
+ span.shadowRoot.adoptedStyleSheets = [sheet];
+
+ sheet.replaceSync(`* { background-image: url("${fileName}"); }`);
+ const styleFromRelative = getComputedStyle(shadowDiv).backgroundImage;
+
+ sheet.replaceSync(`* { background-image: url("${fullPath}"); }`);
+ const styleFromFull = getComputedStyle(shadowDiv).backgroundImage;
+
+ assert_equals(styleFromRelative, styleFromFull);
+}, "Constructing a sheet with the default base URL uses the constructor document's base URL for CSS rules");
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html
new file mode 100644
index 0000000000..010c0b9328
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-modify-after-removal.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>CSS Test: CSSStyleSheet modifications after removal</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstylesheet-interface">
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssrule-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="frm"></iframe>
+<iframe id="frm2"></iframe>
+<script>
+ test(() => {
+ frm.contentDocument.body.innerHTML = "<style>div {color:red}</style>";
+ let sheet = frm.contentDocument.querySelector("style").sheet;
+ assert_equals(sheet.cssRules.length, 1);
+ frm.remove();
+ document.body.offsetTop;
+ sheet.insertRule("span {color: green}", 0);
+ assert_equals(sheet.cssRules.length, 2);
+ }, "Modify sheet from removed iframe");
+
+ test(() => {
+ frm2.contentWindow.eval("let sheet = new CSSStyleSheet(); document.adoptedStyleSheets = [ sheet ];");
+ let sheet = frm2.contentDocument.adoptedStyleSheets[0];
+ assert_equals(sheet.cssRules.length, 0);
+ frm2.remove();
+ document.body.offsetTop;
+ sheet.insertRule("span {color: green}", 0);
+ assert_equals(sheet.cssRules.length, 1);
+ }, "Modify constructed sheet from removed iframe");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html
new file mode 100644
index 0000000000..e014627ed8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet-template-adoption.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<title>adoptedStyleSheets should stay when adopting to/from template document</title>
+<link rel="author" title="Rakina Zata Amni" href="mailto:rakina@chromium.org">
+<link rel="help" href="https://wicg.github.io/construct-stylesheets/">
+<script src = '/resources/testharness.js'></script>
+<script src = '/resources/testharnessreport.js'></script>
+<body>
+<div id="host"></div>
+<template id='template'>
+ <template id='nested_template'></template>
+</template>
+</body>
+<script>
+test(function() {
+ let sheet = new CSSStyleSheet();
+ sheet.replaceSync("div { color: blue }");
+
+ let host = document.getElementById("host");
+ let root = host.attachShadow({ mode: "open" });
+ root.innerHTML = `<div></div>`;
+ root.adoptedStyleSheets = [sheet];
+
+ function assertAdoptedStyleSheet() {
+ assert_equals(host.ownerDocument, root.firstChild.ownerDocument, "Shadow root was not adopted?");
+ assert_equals(root.adoptedStyleSheets.length, 1);
+ assert_equals(root.adoptedStyleSheets[0], sheet);
+ if (root.ownerDocument == document) {
+ assert_equals(getComputedStyle(root.firstChild).color, "rgb(0, 0, 255)", "Sheet should apply");
+ }
+ }
+
+ assertAdoptedStyleSheet();
+
+ // adoptedStyleSheets is not cleared when adopted into a <template>.
+ const template = document.getElementById("template");
+ template.content.appendChild(host);
+
+ assert_not_equals(host.ownerDocument, document, "Should've been adopted");
+ assertAdoptedStyleSheet();
+
+ // adoptedStyleSheets is not cleared when adopted back in the main document.
+ document.body.appendChild(host);
+ assert_equals(host.ownerDocument, document, "Should've been re-adopted");
+ assertAdoptedStyleSheet();
+
+ // adoptedStyleSheets is not cleared when adopted into a nested <template>.
+ const nested_template = template.content.firstElementChild;
+ nested_template.content.appendChild(host);
+ assert_not_equals(host.ownerDocument, document, "Should've been adopted");
+ assertAdoptedStyleSheet();
+
+ // adoptedStyleSheets is cleared when adopted into an <iframe>.
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ iframe.contentDocument.body.appendChild(host);
+ assert_equals(root.adoptedStyleSheets.length, 0);
+}, "adoptedStyleSheets won'te be cleared when adopting into/from <template>s");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/CSSStyleSheet.html b/testing/web-platform/tests/css/cssom/CSSStyleSheet.html
new file mode 100644
index 0000000000..c744382b55
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/CSSStyleSheet.html
@@ -0,0 +1,132 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSStyleSheet interface</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstylesheet-interface">
+ <link rel="help" href="https://drafts.csswg.org/cssom/#legacy-css-style-sheet-members">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style id="my-stylesheet">
+ body { width: 50%; }
+ #foo { height: 100px; }
+ </style>
+ <style id="empty-stylesheet"></style>
+
+ <script>
+ var styleSheet, emptyStyleSheet;
+
+ setup(function() {
+ styleSheet = document.styleSheets[0];
+ styleSheet.cssRules[0].randomProperty = 1;
+ styleSheet.cssRules[1].randomProperty = 2;
+ emptyStyleSheet = document.styleSheets[1];
+ });
+
+ test(function() {
+ assert_equals(styleSheet, document.getElementById("my-stylesheet").sheet, "CSSStyleSheet and LinkStyle's sheet attribute");
+ assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute");
+ assert_equals(styleSheet.cssRules[0].cssText, "body { width: 50%; }", "CSSStyleSheet cssRules attribute");
+ assert_equals(styleSheet.cssRules[1].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute");
+ assert_equals(styleSheet.cssRules[2], undefined, "CSSStyleSheet cssRules attribute");
+
+ assert_equals(emptyStyleSheet, document.getElementById("empty-stylesheet").sheet, "CSSStyleSheet and LinkStyle's sheet attribute");
+ assert_equals(emptyStyleSheet.cssRules.length, 0, "CSSStyleSheet cssRules attribute");
+ }, "preconditions");
+
+ test(function() {
+ styleSheet.insertRule("#bar { margin: 10px; }", 1);
+ assert_equals(styleSheet.cssRules.length, 3, "CSSStyleSheet cssRules attribute after insertRule function");
+ assert_equals(styleSheet.cssRules[0].cssText, "body { width: 50%; }", "CSSStyleSheet cssRules attribute");
+ assert_equals(styleSheet.cssRules[1].cssText, "#bar { margin: 10px; }", "CSSStyleSheet cssRules attribute after insertRule function");
+ assert_equals(styleSheet.cssRules[2].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after insertRule function");
+ assert_equals(styleSheet.cssRules[0].randomProperty, 1, "[SameObject] cssRules attribute after insertRule function");
+ assert_equals(styleSheet.cssRules[2].randomProperty, 2, "[SameObject] cssRules attribute after insertRule function");
+ }, 'insertRule with #bar selector');
+
+ test(function() {
+ assert_throws_js(TypeError, function() { styleSheet.insertRule() });
+ }, 'insertRule with no argument throws');
+
+ test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ styleSheet.insertRule("#bar { margin: 10px; }", styleSheet.cssRules.length + 1)
+ });
+ }, 'insertRule with index greater than length throws');
+
+ test(function() {
+ styleSheet.deleteRule(1);
+ assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute after deleteRule function");
+ assert_equals(styleSheet.cssRules[0].cssText, "body { width: 50%; }", "CSSStyleSheet cssRules attribute after deleteRule function");
+ assert_equals(styleSheet.cssRules[1].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after deleteRule function");
+ assert_equals(styleSheet.cssRules[2], undefined, "CSSStyleSheet cssRules attribute after deleteRule function");
+ assert_equals(styleSheet.cssRules[0].randomProperty, 1, "[SameObject] cssRules attribute after deleteRule function");
+ assert_equals(styleSheet.cssRules[1].randomProperty, 2, "[SameObject] cssRules attribute after deleteRule function");
+ }, 'deleteRule(1)');
+
+ test(function() {
+ assert_throws_js(TypeError, function() { styleSheet.deleteRule() });
+ }, 'deleteRule with no argument throws');
+
+ test(function() {
+ assert_throws_dom("IndexSizeError", function() { emptyStyleSheet.deleteRule(0) });
+ }, 'deleteRule on empty style sheet throws');
+
+ test(function() {
+ styleSheet.removeRule();
+ assert_equals(styleSheet.cssRules.length, 1, "CSSStyleSheet cssRules attribute after removeRule function");
+ assert_equals(styleSheet.cssRules[0].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after removeRule function");
+ }, 'removeRule with no argument removes first rule');
+
+ test(function() {
+ assert_throws_dom("IndexSizeError", function() { emptyStyleSheet.removeRule(0) });
+ }, 'removeRule on empty style sheet throws');
+
+ test(function() {
+ assert_equals(styleSheet.addRule("@media all", "#foo { color: red }"), -1);
+ assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute after addRule function");
+ assert_true(styleSheet.cssRules[1] instanceof CSSMediaRule, "CSSStyleSheet addRule does some silly string concatenation");
+ }, 'addRule with @media rule');
+
+ test(function() {
+ styleSheet.removeRule(1);
+ assert_equals(styleSheet.cssRules.length, 1, "CSSStyleSheet cssRules attribute after removeRule function with index");
+ assert_equals(styleSheet.cssRules[0].cssText, "#foo { height: 100px; }", "CSSStyleSheet cssRules attribute after deleteRule function with index");
+ }, 'removeRule(1)');
+
+ test(function() {
+ assert_equals(styleSheet.addRule("#foo", "color: red"), -1);
+ assert_equals(styleSheet.cssRules.length, 2, "CSSStyleSheet cssRules attribute after addRule function with simple selector");
+ assert_equals(styleSheet.cssRules[1].cssText, "#foo { color: red; }", "CSSStyleSheet cssRules attribute after addRule function without index appends to the end");
+
+ assert_equals(styleSheet.addRule("#foo", "color: blue", 0), -1);
+ assert_equals(styleSheet.cssRules.length, 3, "CSSStyleSheet cssRules attribute after addRule function with simple selector with index");
+ assert_equals(styleSheet.cssRules[0].cssText, "#foo { color: blue; }", "addRule function with index performs an insertion");
+ }, 'addRule with #foo selectors');
+
+ test(function() {
+ assert_equals(styleSheet.addRule(), -1);
+ assert_equals(styleSheet.cssRules.length, 4, "CSSStyleSheet cssRules attribute after addRule function without arguments");
+ assert_equals(styleSheet.cssRules[3].cssText, "undefined { }", "addRule arguments default to undefined");
+ }, 'addRule with no argument adds "undefined" selector');
+
+ test(function() {
+ assert_throws_dom("IndexSizeError", function() {
+ styleSheet.addRule("#foo", "color: red", styleSheet.cssRules.length + 1);
+ });
+ }, 'addRule with index greater than length throws');
+
+ test(function() {
+ assert_equals(styleSheet.cssRules, styleSheet.rules);
+ }, "cssRules and rules are the same object");
+
+ test(function() {
+ assert_equals(styleSheet.cssRules, styleSheet.cssRules);
+ }, "cssRules returns the same object twice");
+
+ test(function() {
+ assert_equals(styleSheet.rules, styleSheet.rules);
+ }, "rules returns the same object twice");
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html
new file mode 100644
index 0000000000..f2c5d0a545
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>&lt;link disabled&gt;, HTMLLinkElement.disabled and CSSStyleSheet.disabled interactions</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link title="alt" rel="stylesheet" disabled href="data:text/css,html { background: green }">
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+const link = document.querySelector("link[disabled]");
+
+test(function() {
+ assert_equals(document.styleSheets.length, 0);
+ assert_applies(false);
+}, "<link disabled> prevents the stylesheet from being in document.styleSheets (from parser)");
+
+async_test(function(t) {
+ assert_true(link.disabled);
+
+ link.onload = t.step_func_done(function() {
+ assert_equals(document.styleSheets.length, 1);
+ let sheet = document.styleSheets[0];
+ assert_equals(sheet.ownerNode, link);
+ assert_applies(true);
+
+ link.disabled = true;
+ assert_equals(sheet.ownerNode, null);
+ assert_false(sheet.disabled);
+ assert_applies(false);
+ assert_true(link.hasAttribute("disabled"));
+
+ assert_equals(document.styleSheets.length, 0);
+ assert_applies(false);
+ });
+
+ link.disabled = false;
+ assert_true(!link.hasAttribute("disabled"));
+ assert_false(link.disabled);
+}, "HTMLLinkElement.disabled reflects the <link disabled> attribute, and behaves consistently");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html
new file mode 100644
index 0000000000..34bd8182fe
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>&lt;link disabled&gt;, HTMLLinkElement.disabled and CSSStyleSheet.disabled interactions (alternate)</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link title="alt" rel="alternate stylesheet" disabled href="data:text/css,html { background: green }">
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+const link = document.querySelector("link[disabled]");
+
+async_test(function(t) {
+ assert_true(link.disabled);
+
+ link.onload = t.step_func_done(function() {
+ assert_equals(document.styleSheets.length, 1);
+ let sheet = document.styleSheets[0];
+ assert_equals(sheet.ownerNode, link);
+ assert_applies(true);
+
+ link.disabled = true;
+ assert_equals(sheet.ownerNode, null);
+ assert_false(sheet.disabled);
+ assert_applies(false);
+ assert_true(link.hasAttribute("disabled"));
+ assert_equals(document.styleSheets.length, 0);
+ });
+
+ link.disabled = false;
+ assert_true(!link.hasAttribute("disabled"));
+ assert_false(link.disabled);
+}, "HTMLLinkElement.disabled reflects the <link disabled> attribute, and behaves consistently, when the sheet is an alternate");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html
new file mode 100644
index 0000000000..3d391bbaff
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>&lt;link disabled&gt;'s "explicitly enabled" state persists after getting disconnected from the tree</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link title="alt" rel="alternate stylesheet" disabled href="data:text/css,html { background: green }">
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+const link = document.querySelector("link[disabled]");
+async_test(function(t) {
+ assert_true(link.disabled);
+ link.disabled = false;
+ assert_false(link.disabled);
+ assert_true(!link.hasAttribute("disabled"));
+ link.remove();
+
+ link.onload = t.step_func_done(function() {
+ assert_equals(document.styleSheets.length, 1);
+ let sheet = document.styleSheets[0];
+ assert_equals(sheet.ownerNode, link);
+ assert_applies(true);
+ });
+
+ document.head.appendChild(link);
+}, "HTMLLinkElement.disabled's explicitly enabled state persists when disconnected and connected again");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html
new file mode 100644
index 0000000000..f163f1d68e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>&lt;link disabled&gt;'s "explicitly enabled" state doesn't persist for clones</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link title="alt" rel="alternate stylesheet" disabled href="data:text/css,html { background: green }">
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+const link = document.querySelector("link[disabled]");
+
+async_test(function(t) {
+ link.remove();
+ link.disabled = false; // `link` is explicitly enabled.
+
+ let clonesLoaded = 0;
+
+ for (let shallow of [true, false]) {
+ const clone = link.cloneNode(shallow);
+ clone.onload = t.step_func(function() {
+ assert_false(link.disabled);
+ // Even though it's not disabled, it still doesn't apply, since it's an alternate.
+ assert_applies(false);
+ if (++clonesLoaded == 2) {
+ link.onload = t.step_func_done(function() {
+ assert_false(link.disabled);
+ assert_applies(true); // `link` is still explicitly enabled.
+ });
+ document.head.appendChild(link);
+ }
+ });
+ document.head.appendChild(clone);
+ }
+}, "HTMLLinkElement.disabled's explicitly enabled state doesn't persist on clones");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html
new file mode 100644
index 0000000000..76de206327
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>&lt;link disabled&gt;'s "explicitly enabled" persists across rel changes</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link title="alt" rel="yadayada" disabled href="data:text/css,html { background: green }">
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+const link = document.querySelector("link[disabled]");
+
+async_test(function(t) {
+ link.onload = t.step_func_done(function() {
+ assert_applies(true);
+ link.setAttribute("rel", "alternate stylesheet");
+ assert_applies(true);
+ assert_false(link.disabled);
+ });
+ link.disabled = false;
+ link.setAttribute("rel", "stylesheet");
+}, "HTMLLinkElement.disabled's explicitly enabled state persists regardless of rel");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html
new file mode 100644
index 0000000000..02749a2e11
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>&lt;link disabled&gt;'s "explicitly enabled" state isn't magically set from the setter</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+async_test(function(t) {
+ const link = document.createElement("link");
+ link.setAttribute("rel", "alternate stylesheet");
+ link.setAttribute("title", "alt");
+ link.href = "data:text/css,html { background: green }";
+ link.disabled = false; // This should do nothing, and is the point of this test.
+ link.onload = t.step_func_done(function() {
+ assert_applies(false); // Should not apply, since it's an alternate that hasn't been enabled.
+ assert_false(link.disabled);
+ });
+ document.head.appendChild(link);
+}, "HTMLLinkElement.disabled setter does nothing if the attribute isn't present already.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html
new file mode 100644
index 0000000000..9a695a964c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>&lt;link disabled&gt;'s "explicitly enabled" state works when set explicitly back and forth</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function assert_applies(applies) {
+ (applies ? assert_equals : assert_not_equals)(getComputedStyle(document.documentElement).backgroundColor, "rgb(0, 128, 0)");
+}
+
+async_test(function(t) {
+ const link = document.createElement("link");
+ link.setAttribute("rel", "alternate stylesheet");
+ link.setAttribute("title", "alt");
+ link.href = "data:text/css,html { background: green }";
+ link.disabled = true;
+ link.disabled = false; // This should make it "explicitly enabled".
+ link.onload = t.step_func_done(function() {
+ assert_applies(true); // Should apply, since it's explicitly enabled.
+ assert_false(link.disabled);
+ });
+ document.head.appendChild(link);
+}, "HTMLLinkElement.disabled setter sets the explicitly enabled state if toggled back and forth.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html
new file mode 100644
index 0000000000..5d87bfdaf5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<style>
+ html { background: green }
+</style>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html
new file mode 100644
index 0000000000..f1457c0dcf
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>CSS Test: alternate stylesheets can be disabled by HTMLLinkElement.disabled if they have the disabled attribute already</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#attr-link-disabled">
+<link rel="match" href="HTMLLinkElement-disabled-alternate-ref.html">
+<link title="alt" rel="alternate stylesheet" href="data:text/css,html { background: green }" disabled onload="document.documentElement.className = ''">
+<script>
+ onload = function() {
+ const link = document.querySelector("link[rel='alternate stylesheet']");
+ link.disabled = false;
+ }
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event-002.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event-002.html
new file mode 100644
index 0000000000..79780813a1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event-002.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Link element load event doesn't block the parser.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ let NUM_LOADS = 0;
+
+ function sheetUrl(token) {
+ return "stylesheet-same-origin.css?" + token;
+ }
+
+ function createLink(token) {
+ let link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = sheetUrl(token);
+ return link;
+ }
+
+ function waitForEnoughTimeToLoadSheet(token) {
+ return new Promise(resolve => {
+ let link = createLink(token);
+ link.onload = resolve;
+ document.head.appendChild(link);
+ });
+ }
+
+ promise_test(async function (t) {
+ let link = createLink("removed");
+ link.addEventListener("load", t.unreached_func("got unexpected load event"));
+ link.addEventListener("error", t.unreached_func("got unexpected error event"));
+ document.head.appendChild(link);
+ link.remove();
+
+ await waitForEnoughTimeToLoadSheet("removed-wait");
+ }, "Load event doesn't fire on removed link");
+
+ promise_test(async function (t) {
+ let link = createLink("changed-initial");
+ let sawLoad = false;
+ let load = new Promise(resolve => {
+ link.addEventListener("load", function(e) {
+ assert_false(sawLoad, "Should only see load event once");
+ sawLoad = true;
+ resolve();
+ });
+ });
+ link.addEventListener("error", t.unreached_func("got unexpected error event"));
+ document.head.appendChild(link);
+ link.href = sheetUrl("changed-change");
+
+ await waitForEnoughTimeToLoadSheet("changed-wait");
+ await load;
+
+ assert_true(sawLoad, "Should've seen the load event only once");
+ }, "Load event doesn't fire for removed sheet");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event.html
new file mode 100644
index 0000000000..7062f15450
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-load-event.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Link element load event doesn't block the parser.</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-stylesheet">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ let NUM_LOADS = 0;
+</script>
+<link rel="stylesheet" href="data:text/css,*{}" onload="++NUM_LOADS">
+<script>
+let t = async_test(document.title);
+window.addEventListener("load", t.step_func_done(() => {
+ assert_equals(NUM_LOADS, 2, "Load event should've fired for all links");
+}));
+t.step(function() {
+ assert_equals(document.styleSheets.length, 1, "Should expose the sheet to the OM before running script");
+ // We can't quite assert that NUM_LOADS is zero (even though it almost-always
+ // should be the case), in case the parser yields just before executing the
+ // script but after parsing the link load.
+ let loadsBefore = NUM_LOADS;
+ // Intentionally the same href as above, to test caching behavior.
+ document.write(`
+ <link rel="stylesheet" href="data:text/css,*{}" onload="++NUM_LOADS">
+ `);
+ assert_equals(document.styleSheets.length, 2, "Should expose both sheets to the OM before running second script");
+ assert_equals(NUM_LOADS, loadsBefore, "Shouldn't fire the load event sync");
+ assert_not_equals(document.styleSheets[0], document.styleSheets[1], "Should be different sheets");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/HTMLStyleElement-load-event.html b/testing/web-platform/tests/css/cssom/HTMLStyleElement-load-event.html
new file mode 100644
index 0000000000..8d4062ed0d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/HTMLStyleElement-load-event.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>style elements fire load events properly</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#update-a-style-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ let NUM_LOADS = 0;
+</script>
+<style onload="++NUM_LOADS"></style>
+<style onload="++NUM_LOADS">:root { background-color: lime }</style>
+<style onload="++NUM_LOADS">:root { background-color: lime }</style> <!-- Intentionally the same -->
+<script>
+async_test(function(t) {
+ assert_equals(document.styleSheets.length, 3, "Should expose the three stylesheets to the OM sync");
+ assert_equals(NUM_LOADS, 0, "Should not fire load event sync");
+ window.addEventListener("load", t.step_func_done(() => {
+ assert_equals(NUM_LOADS, 3, "Load event should've fired for all nodes");
+ }));
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/META.yml b/testing/web-platform/tests/css/cssom/META.yml
new file mode 100644
index 0000000000..3250801f64
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/META.yml
@@ -0,0 +1,5 @@
+spec: https://drafts.csswg.org/cssom/
+suggested_reviewers:
+ - dbaron
+ - plinss
+ - lilles
diff --git a/testing/web-platform/tests/css/cssom/MediaList.html b/testing/web-platform/tests/css/cssom/MediaList.html
new file mode 100644
index 0000000000..8308ee89e4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/MediaList.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - MediaList interface</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-medialist-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ @media screen and (min-width: 480px), print, projection {}
+ </style>
+
+ <script>
+ test(function () {
+ var media = document.styleSheets[0].cssRules[0].media;
+ assert_equals(media.length, 3, "MediaList length attribute");
+ assert_equals(media.mediaText, "screen and (min-width: 480px), print, projection", "MediaList mediaText attribute");
+ assert_equals(media.toString(), "screen and (min-width: 480px), print, projection", "MediaList toString method");
+ assert_equals(media[0], "screen and (min-width: 480px)", "MediaList indexed getter");
+ assert_equals(media[1], "print", "MediaList indexed getter");
+ assert_equals(media[2], "projection", "MediaList indexed getter");
+ assert_equals(media[3], undefined, "MediaList indexed getter with out of range");
+ assert_equals(media.item(0), "screen and (min-width: 480px)", "MediaList item method");
+ assert_equals(media.item(3), null, "MediaList item method");
+
+ media.deleteMedium("print");
+ assert_equals(media.length, 2, "MediaList length attribute after delete method");
+ assert_equals(media.mediaText, "screen and (min-width: 480px), projection", "MediaList mediaText attribute after delete method");
+ assert_equals(media.toString(), "screen and (min-width: 480px), projection", "MediaList toString method after delete method");
+ assert_equals(media[1], "projection", "MediaList indexed getter after delete method");
+ assert_equals(media[2], undefined, "MediaList indexed getter with out of range after delete method");
+ assert_equals(media.item(1), "projection", "MediaList indexed getter after delete method");
+ assert_equals(media.item(2), null, "MediaList item method after delete method");
+
+ media.appendMedium("speech");
+ assert_equals(media.length, 3, "MediaList length attribute after append method");
+ assert_equals(media.mediaText, "screen and (min-width: 480px), projection, speech", "MediaList mediaText attribute after append method");
+ assert_equals(media.toString(), "screen and (min-width: 480px), projection, speech", "MediaList toString method after append method");
+ assert_equals(media[1], "projection", "MediaList indexed getter after append method");
+ assert_equals(media[2], "speech", "MediaList indexed getter after append method");
+ assert_equals(media[3], undefined, "MediaList indexed getter with out of range after append method");
+ assert_equals(media.item(2), "speech", "MediaList item method after append method");
+ assert_equals(media.item(3), null, "MediaList item method after append method");
+
+ media.mediaText = null;
+ assert_equals(media.mediaText, "", "MediaList mediaText attribute should be empty string in case of null");
+ assert_equals(media.toString(), "", "MediaList toString method should be empty string in case of null");
+
+ var rule = document.styleSheets[0].cssRules[0];
+ rule.media = "speech";
+ assert_equals(rule.media.mediaText, "speech", "MediaList mediaText attribute should be updated");
+ });
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/MediaList2.xhtml b/testing/web-platform/tests/css/cssom/MediaList2.xhtml
new file mode 100644
index 0000000000..73acbdec25
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/MediaList2.xhtml
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>CSS Test: the MediaList interface</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com"/>
+<link rel="help" href="http://www.w3.org/TR/cssom-1/#the-medialist-interface"/>
+<link rel="help" href="http://dev.w3.org/2006/webapi/WebIDL/#getownproperty"/>
+<style media="screen, print" id="test-style"></style>
+<script src="/resources/testharness.js"/>
+<script src="/resources/testharnessreport.js"/>
+</head>
+<body>
+<div id="log"/>
+<script>
+test(function() {
+ var ss = document.styleSheets[0];
+ assert_equals(ss.ownerNode.id, "test-style", "Got the wrong style element");
+
+ var media = ss.media;
+ test(function() {
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17526
+ assert_equals(media.mediaText, "screen, print", "Serialization should be \"screen, print\"");
+ }, "MediaList.mediaText");
+
+ test(function() {
+ assert_equals(media.length, 2, "Got wrong number of media");
+ }, "MediaList.length");
+
+ test(function() {
+ assert_equals(media[-1], undefined, "media[-1] should return undefined");
+ assert_equals(media[0], "screen", "media[0] should return \"screen\"");
+ assert_equals(media[1], "print", "media[1] should return \"print\"");
+ assert_equals(media[2], undefined, "media[2] should return undefined");
+ }, "MediaList getter");
+
+ test(function() {
+ assert_equals(media.item(-1), null, "media.item(-1) should return null");
+ assert_equals(media.item(0), "screen", "media.item(0) should return \"screen\"");
+ assert_equals(media.item(1), "print", "media.item(1) should return \"print\"");
+ assert_equals(media.item(2), null, "media.item(2) should return null");
+ }, "MediaList.item");
+}, "MediaList");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/MutationObserver-style.html b/testing/web-platform/tests/css/cssom/MutationObserver-style.html
new file mode 100644
index 0000000000..673822d11e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/MutationObserver-style.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Observer notifications when updating styles</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<body>
+<script>
+ "use strict";
+
+ promise_test(async () => {
+ let called = 0;
+ const el = document.createElement("div");
+ document.body.appendChild(el);
+ const m = new MutationObserver(() => {
+ called++;
+ });
+ m.observe(el, { attributes: true });
+ el.style.height = "100px";
+ await Promise.resolve();
+ assert_equals(called, 1, "times callback called");
+ el.style.height = "100px";
+ await Promise.resolve();
+ assert_equals(called, 1, "times callback called");
+ }, "Updating style property with the same value does not trigger an observation callback");
+
+ promise_test(async () => {
+ let called = 0;
+ const el = document.createElement("div");
+ document.body.appendChild(el);
+ const m = new MutationObserver(() => {
+ called++;
+ });
+ m.observe(el, { attributes: true });
+ el.style.cssText = "height:100px";
+ await Promise.resolve();
+ assert_equals(called, 1, "times callback called");
+ el.style.cssText = "height:100px";
+ await Promise.resolve();
+ assert_equals(called, 2, "times callback called");
+ }, "Updating cssText triggers an observation callback even if the value is the same");
+</script>
+
diff --git a/testing/web-platform/tests/css/cssom/StyleSheetList.html b/testing/web-platform/tests/css/cssom/StyleSheetList.html
new file mode 100644
index 0000000000..bb7d2ff4e3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/StyleSheetList.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - StyleSheetList interface</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-stylesheetlist-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ body { width: 50%; }
+ </style>
+ <style>
+ #foo { width: 10%; }
+ #bar { height: 100px; }
+ </style>
+ <script>
+ test(function () {
+ var styleSheets = document.styleSheets;
+ assert_equals(styleSheets.length, 2, "StyleSheetList length attribute");
+ assert_equals(styleSheets[0].cssRules.length, 1, "StyleSheetList indexed getter length attribute");
+ assert_equals(styleSheets[1].cssRules.length, 2, "StyleSheetList indexed getter length attribute");
+ assert_equals(styleSheets[2], undefined, "StyleSheetList indexed getter length attribute");
+ assert_equals(styleSheets.item(0).cssRules.length, 1, "StyleSheetList item function length attribute");
+ assert_equals(styleSheets.item(1).cssRules.length, 2, "StyleSheetList item function length attribute");
+ assert_equals(styleSheets.item(2), null, "StyleSheetList item function length attribute");
+
+ styleSheets[0].randomProperty = 1;
+ var style = document.createElement("style");
+ document.head.appendChild(style);
+ assert_equals(styleSheets[0].randomProperty, 1, "[SameObject] StyleSheetList");
+ });
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html b/testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html
new file mode 100644
index 0000000000..783a054163
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/adoptedstylesheets-observablearray.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Adoptedstylesheets as ObservableArray</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom/#extensions-to-the-document-or-shadow-root-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<span id=target>Test Span</span>
+<div id="divNonConstructed" class="nonConstructed"></div>
+<style>
+ #target {background-color: red;}
+</style>
+
+<script>
+
+const shadowRootNonConstructed = divNonConstructed.attachShadow({mode:'open'})
+nonConstructedStyle = document.createElement("style");
+shadowRootNonConstructed.appendChild(nonConstructedStyle);
+nonConstructedStyle.sheet.insertRule(".nonConstructed { color: red; }", 0);
+const nonConstructedStyleSheet = nonConstructedStyle.sheet;
+
+function assert_is(targetStyle, color) {
+ assert_equals(targetStyle.getPropertyValue('background-color'), color);
+}
+
+function testRoot(d, targetStyle) {
+ const red = 'rgb(255, 0, 0)';
+ const green = 'rgb(0, 255, 0)';
+ const blue = 'rgb(0, 0, 255)';
+
+ const sheet1 = new CSSStyleSheet();
+ sheet1.replaceSync('#target {background-color:lime !important;}');
+ const sheet2 = new CSSStyleSheet();
+ sheet2.replaceSync('#target {background-color:blue !important;}');
+ assert_equals(d.adoptedStyleSheets.length, 0);
+ assert_is(targetStyle, red);
+
+ d.adoptedStyleSheets = [sheet1];
+ assert_equals(d.adoptedStyleSheets.length, 1);
+ assert_is(targetStyle, green);
+
+ d.adoptedStyleSheets.push(sheet2);
+ assert_equals(d.adoptedStyleSheets.length, 2);
+ assert_is(targetStyle, blue);
+
+ d.adoptedStyleSheets.pop();
+ assert_equals(d.adoptedStyleSheets.length, 1);
+ assert_is(targetStyle, green);
+
+ d.adoptedStyleSheets.push(sheet2);
+ d.adoptedStyleSheets.reverse();
+ assert_equals(d.adoptedStyleSheets.length, 2);
+ assert_is(targetStyle, green);
+
+ d.adoptedStyleSheets.splice(1, 1);
+ assert_equals(d.adoptedStyleSheets.length, 1);
+ assert_is(targetStyle, blue);
+ d.adoptedStyleSheets.splice(0, 1, sheet1);
+ assert_equals(d.adoptedStyleSheets.length, 1);
+ assert_is(targetStyle, green);
+
+ // Adding non-constructed stylesheet to AdoptedStyleSheets is not allowed.
+ assert_throws_dom('NotAllowedError', () => { document.adoptedStyleSheets.push(nonConstructedStyleSheet); });
+
+ assert_throws_js(TypeError, () => { document.adoptedStyleSheets.push("foo"); });
+}
+
+test(function() {
+ const target = document.querySelector('#target');
+ const targetStyle = window.getComputedStyle(target);
+ testRoot(document, targetStyle);
+}, "document.adoptedStyleSheets should allow mutation in-place");
+
+test(function() {
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const shadow = host.attachShadow({mode: 'open'});
+ shadow.innerHTML = '<span id=target>Test Shadow Span</span><style>#target{background-color: red;}</style>';
+ const target = shadow.querySelector('#target');
+ const targetStyle = window.getComputedStyle(target);
+ testRoot(shadow, targetStyle);
+}, "shadowRoot.adoptedStyleSheets should allow mutation in-place");
+
+test(function() {
+ assert_true(Array.isArray(document.adoptedStyleSheets));
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const shadow = host.attachShadow({mode: 'open'});
+ assert_true(Array.isArray(shadow.adoptedStyleSheets));
+}, "adoptedStyleSheets should return true for isArray()");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/at-namespace.html b/testing/web-platform/tests/css/cssom/at-namespace.html
new file mode 100644
index 0000000000..b1c76f3c58
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/at-namespace.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>CSS Test: @namespace in CSSOM is not severely broken</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1464865">
+<link rel="help" href="https://drafts.csswg.org/cssom/#insert-a-css-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style id="s">
+ div { color: green }
+</style>
+<div>Should be green</div>
+<script>
+test(function() {
+ assert_throws_dom("InvalidStateError", function() {
+ s.sheet.insertRule('@namespace myhtml url("http://www.w3.org/1999/xhtml")', 0);
+ });
+ assert_equals(s.sheet.cssRules.length, 1, "Shouldn't have been inserted");
+ assert_throws_dom("SyntaxError", function() {
+ s.sheet.insertRule("myhtml|div { color: red !important }", 0);
+ });
+ assert_equals(s.sheet.cssRules.length, 1);
+ assert_equals(
+ getComputedStyle(document.querySelector("div")).color,
+ "rgb(0, 128, 0)",
+ "Namespace shouldn't be registered"
+ );
+});
+</script>
+
diff --git a/testing/web-platform/tests/css/cssom/base-uri.html b/testing/web-platform/tests/css/cssom/base-uri.html
new file mode 100644
index 0000000000..b948d5a7f4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/base-uri.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM base URI is the document's base URI</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://html.spec.whatwg.org/#document-base-url">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1343919">
+<base href="/non-existent-base/">
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<div id="target"></div>
+<script>
+const target = document.getElementById("target");
+const kRelativeURI = "url(something.png)";
+const kKeyframes = [
+ { backgroundImage: kRelativeURI },
+ { backgroundImage: kRelativeURI },
+];
+function assertBackground() {
+ const image = getComputedStyle(target).backgroundImage;
+ assert_true(image.includes("non-existent-base"), image);
+}
+function assertNoBackground() {
+ const image = getComputedStyle(target).backgroundImage;
+ assert_equals(image, "none");
+}
+
+promise_test(async function() {
+ target.style.backgroundImage = kRelativeURI;
+ assertBackground();
+ target.style.backgroundImage = "";
+ assertNoBackground();
+}, "setProperty");
+
+promise_test(async function() {
+ const keyframe = new KeyframeEffect(target, kKeyframes, 10000);
+ const animation = new Animation(keyframe, document.timeline);
+ animation.play();
+ await animation.ready;
+ assertBackground();
+ animation.cancel();
+ assertNoBackground();
+}, "KeyframeEffect constructor");
+
+promise_test(async function() {
+ const keyframe = new KeyframeEffect(target, [], 10000);
+ keyframe.setKeyframes(kKeyframes);
+ const animation = new Animation(keyframe, document.timeline);
+ animation.play();
+ await animation.ready;
+ assertBackground();
+ animation.cancel();
+ assertNoBackground();
+}, "KeyframeEffect.setKeyframes");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/border-shorthand-serialization.html b/testing/web-platform/tests/css/cssom/border-shorthand-serialization.html
new file mode 100644
index 0000000000..9b5821563c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/border-shorthand-serialization.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+<link rel="author" title="Erik Nordin" href="mailto:enordin@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#propdef-border">
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration-block">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1594241">
+<meta charset="utf-8">
+<title>serialization of border shorthand</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style id="target">
+ .a {
+ border-width: 1px;
+ border-style: solid;
+ border-color: black;
+ }
+ .b {
+ border-width: 1px;
+ border-style: solid;
+ border-color: black;
+ border-image: linear-gradient(white,black);
+ }
+ .c {
+ border: 1px solid black;
+ }
+</style>
+<script>
+ test(function() {
+ let rule = document.getElementById('target').sheet.cssRules[0];
+ assert_equals(rule.style.border, "", "border shorthand isn't serialized if border-image longhands are not initial");
+ }, "Declaration with border longhands is not serialized to a border shorthand declaration.");
+ test(function() {
+ let rule = document.getElementById('target').sheet.cssRules[1];
+ assert_equals(rule.style.border, "", "border shorthand isn't serialized if border-image longhands are not initial");
+ }, "Declaration with border longhands and border-image is not serialized to a border shorthand declaration.");
+ test(function() {
+ let rule = document.getElementById('target').sheet.cssRules[2];
+ assert_not_equals(rule.style.border, "", "border shorthand ");
+ assert_equals(rule.cssText, ".c { border: 1px solid black; }");
+ }, "Border shorthand is serialized correctly if all border-image-* are set to their initial specified values.");
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html b/testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html
new file mode 100644
index 0000000000..f6ed98f3f2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/caretPositionFromPoint-with-transformation.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>getCaretPositionFromPoint should return the correct offset even in iframes with transformation</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-document-caretpositionfrompoint">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1546612">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #actual {
+ transform: translateX(100px);
+ }
+</style>
+<iframe id="expected"></iframe>
+<br>
+<iframe id="actual"></iframe>
+<script>
+ const expectedFrame = document.getElementById("expected");
+ const actualFrame = document.getElementById("actual");
+
+ const getCaretPositionOffset = frame => {
+ const source = `<!doctype html><meta charset="utf-8"><h1>title</h1><p>paragraph</p>`
+
+ const elementCenter = elem => {
+ const rect = elem.getBoundingClientRect();
+ return [rect.x + rect.width / 2, rect.y + rect.height / 2];
+ };
+
+ return new Promise((resolve, reject) => {
+ frame.srcdoc = source;
+ frame.onload = () => {
+ try {
+ const frameDoc = frame.contentDocument;
+ const {offset} = frameDoc.caretPositionFromPoint(
+ ...elementCenter(frameDoc.querySelector("h1"))
+ );
+ resolve(offset);
+ } catch (error) {
+ reject(error);
+ }
+ };
+ });
+ };
+
+ promise_test(async () => {
+ assert_equals(...await Promise.all([
+ getCaretPositionOffset(expectedFrame),
+ getCaretPositionOffset(actualFrame)
+ ]), "caret offset");
+ }, "iframe's with equal content should report the same caret offset");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/caretPositionFromPoint.html b/testing/web-platform/tests/css/cssom/caretPositionFromPoint.html
new file mode 100644
index 0000000000..a268acac42
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/caretPositionFromPoint.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>document.caretPositionFromPoint()</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-document-caretpositionfrompoint">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #textDiv {
+ display: inline-block;
+ }
+</style>
+<div id="textDiv">aaa</div>
+<script>
+ test(() => {
+ assert_throws_js(TypeError, () => { document.caretPositionFromPoint(); });
+ assert_throws_js(TypeError, () => { document.caretPositionFromPoint(5); });
+ assert_throws_js(TypeError, () => { document.caretPositionFromPoint("foo", 5); });
+ }, "document.caretPositionFromPoint() throws when called without the correct parameters");
+
+ test(() => {
+ const doc = document.implementation.createHTMLDocument("");
+ assert_equals(doc.caretPositionFromPoint(0, 0), null);
+ }, "document.caretPositionFromPoint() should return null for a document with no viewport");
+
+ test(() => {
+ assert_equals(document.caretPositionFromPoint(-5, 5), null);
+ assert_equals(document.caretPositionFromPoint(5, -5), null);
+ assert_equals(document.caretPositionFromPoint(document.documentElement.clientWidth * 2, 5), null);
+ assert_equals(document.caretPositionFromPoint(5, document.documentElement.clientHeight * 2), null);
+ }, "document.caretPositionFromPoint() should return null if given coordinates outside of the viewport");
+
+ test(() => {
+ const textDiv = document.getElementById("textDiv");
+ const rect = textDiv.getBoundingClientRect();
+ const characterWidth = rect.width / textDiv.textContent.length;
+ const characterIndex = 2
+ const x = rect.left + characterWidth * characterIndex;
+ const y = rect.top + rect.height / 2;
+ const caretPosition = document.caretPositionFromPoint(x, y);
+ assert_true(caretPosition instanceof CaretPosition);
+ assert_true(caretPosition.offsetNode instanceof Text);
+ assert_equals(typeof(caretPosition.offset), "number");
+ assert_equals(caretPosition.offsetNode, textDiv.firstChild);
+ assert_equals(caretPosition.offset, characterIndex);
+ }, "document.caretPositionFromPoint() should return a CaretPosition at the specified location");
+
+ test(() => {
+ const textDiv = document.getElementById("textDiv");
+ const rect = textDiv.getBoundingClientRect();
+ const characterWidth = rect.width / textDiv.textContent.length;
+ const characterIndex = 2
+ const x = rect.left + characterWidth * characterIndex;
+ const y = rect.top + rect.height / 2;
+ const caretPosition = document.caretPositionFromPoint(x, y);
+ const caretRangeExpected = new Range();
+ caretRangeExpected.setStart(textDiv.firstChild, characterIndex);
+ caretRectExpected = caretRangeExpected.getBoundingClientRect();
+ assert_true(caretPosition.getClientRect instanceof Function);
+ const caretRectActual = caretPosition.getClientRect();
+ assert_true(caretRectActual instanceof DOMRect);
+ assert_not_equals(caretRectActual, caretPosition.getClientRect(), "CaretPosition.getClientRect() should return a new DOMRect every time");
+ assert_equals(caretRectActual.x, caretRectExpected.x);
+ assert_equals(caretRectActual.y, caretRectExpected.y);
+ assert_equals(caretRectActual.width, 0, "Caret range should be collapsed");
+ assert_equals(caretRectActual.height, caretRectExpected.height);
+ }, "CaretRange.getClientRect() should return a DOMRect that matches one obtained from a manually constructed Range");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/change-rule-with-layers-crash.html b/testing/web-platform/tests/css/cssom/change-rule-with-layers-crash.html
new file mode 100644
index 0000000000..63e8ec5207
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/change-rule-with-layers-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Chrome crash when modifying rules with @layer</title>
+<link rel="help" href="https://crbug.com/1499277">
+<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
+<style id="s">
+.x {
+ transition: color 0.5s;
+ @layer warning {
+ :first-child { }
+ :last-child { }
+ }
+}
+</style>
+<p>Test passes if it does not crash.</p>
+<script>
+document.body.offsetTop;
+s.sheet.cssRules[0].style.transitionDuration = '1s';
+</script>
+
diff --git a/testing/web-platform/tests/css/cssom/computed-style-001.html b/testing/web-platform/tests/css/cssom/computed-style-001.html
new file mode 100644
index 0000000000..e487981e30
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/computed-style-001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: getComputedStyle</title>
+ <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-window-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#resolved-values">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="getComputedStyle returns a readonly CSSStyleDeclaration with resolved values">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style>
+ #outside {
+ width: 200px;
+ height: 200px;
+ }
+ #outside div {
+ font-size: 100px;
+ }
+ #inside {
+ width: 50%;
+ height: 100px;
+ }
+ </style>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+ <div id="outside"><div id="inside"></div></div>
+ <script type="text/javascript">
+ var outer = document.getElementById("outside");
+ var inner = document.getElementById("inside");
+ var innerStyle;
+
+ // do not allow modifications to a computed CSSStyleDeclaration
+ test(function() {
+ innerStyle = window.getComputedStyle(inner);
+ assert_throws_dom( "NO_MODIFICATION_ALLOWED_ERR",
+ function() { innerStyle.cssText = "color: blue;"; },
+ "do not allow setting cssText on a readonly CSSStyleDeclaration");
+ assert_throws_dom( "NO_MODIFICATION_ALLOWED_ERR",
+ function() { innerStyle.setProperty("color", "blue"); },
+ "do not allow calling setProperty on a readonly CSSStyleDeclaration");
+ assert_throws_dom( "NO_MODIFICATION_ALLOWED_ERR",
+ function() { innerStyle.color = "blue"; },
+ "do not allow setting a property on a readonly CSSStyleDeclaration");
+ }, "read_only");
+
+ // Directly set properties are resolved
+ test(function() {
+ assert_equals(innerStyle.getPropertyValue("height"), "100px");
+ }, "property_values");
+
+ // Inherited properties are resolved
+ test(function() {
+ assert_equals(innerStyle.getPropertyValue("font-size"), "100px");
+ }, "inherited_property_values");
+
+ // Relative properties are resolved
+ test(function() {
+ assert_equals(innerStyle.getPropertyValue("width"), "100px");
+ }, "relative_property_values");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/computed-style-002.html b/testing/web-platform/tests/css/cssom/computed-style-002.html
new file mode 100644
index 0000000000..7b31c96c09
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/computed-style-002.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>CSS Test: getComputedStyle - resolved width in iframe</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" />
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="frm" width="100"></iframe>
+<script>
+ test(() => {
+ const frmDoc = frm.contentWindow.document;
+ frmDoc.open();
+ frmDoc.write('<body style="margin:0"><div style="width:100%"></div>');
+ frmDoc.close();
+
+ assert_equals(frm.contentWindow.getComputedStyle(frmDoc.querySelector("div")).width, "100px");
+ }, "Check that a percent width in an iframe is resolved against iframe width for getComputedStyle.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/computed-style-003.html b/testing/web-platform/tests/css/cssom/computed-style-003.html
new file mode 100644
index 0000000000..e73b793b8f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/computed-style-003.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<title>CSS Test: getComputedStyle - resolved width in iframe dynamic display</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" />
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="frm" width="100" style="display:none"></iframe>
+<script>
+ const frmDoc = frm.contentWindow.document;
+ frmDoc.open();
+ frmDoc.write('<body style="margin:0"><div style="width:100%"></div>');
+ frmDoc.close();
+
+ document.body.offsetWidth; // Make sure we layout the top document.
+
+ test(() => {
+ frm.style.display = "inline";
+ assert_equals(frm.contentWindow.getComputedStyle(frmDoc.querySelector("div")).width, "100px");
+ }, "Check that a percent width in an iframe is the resolved width when the iframe is displayed.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/computed-style-004.html b/testing/web-platform/tests/css/cssom/computed-style-004.html
new file mode 100644
index 0000000000..c5b08712f9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/computed-style-004.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>CSS Test: getComputedStyle - resolved width in nested iframes dynamic width</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org" />
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="outer" width="100" scrolling="no" frameborder="0"></iframe>
+<script>
+ const outerDoc = outer.contentWindow.document;
+ outerDoc.open();
+ outerDoc.write('<body style="margin:0"><iframe id="inner" scrolling="no" frameborder="0" style="width:100%"></iframe>');
+ outerDoc.close();
+
+ const innerWindow = outerDoc.querySelector("#inner").contentWindow;
+ const innerDoc = innerWindow.document;
+ innerDoc.open();
+ innerDoc.write('<body style="margin:0"><div style="width:100%"></div>');
+ innerDoc.close();
+ innerDoc.body.offsetWidth; // Make sure we layout the top document.
+
+ test(() => {
+ assert_equals(innerWindow.getComputedStyle(innerDoc.querySelector("div")).width, "100px");
+ }, "Check that the initial width is 100px.");
+
+ test(() => {
+ outer.setAttribute("width", "200");
+ assert_equals(innerWindow.getComputedStyle(innerDoc.querySelector("div")).width, "200px");
+ }, "Check that the resolved width of the inner div is affected by changing the width of outer iframe.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/computed-style-005.html b/testing/web-platform/tests/css/cssom/computed-style-005.html
new file mode 100644
index 0000000000..49a5977acd
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/computed-style-005.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: getComputedStyle on blocks with auto margins</title>
+ <link rel="author" title="Brad Werth" href="mailto:bwerth@mozilla.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle" />
+ <meta name="assert" content="getComputedStyle produces pixel values for margin: auto blocks">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style>
+ x {
+ display: block;
+ position: relative;
+ background: grey;
+ width: 60px;
+ height: 10px;
+ }
+ y {
+ display: block;
+ background: blue;
+ width: 40px;
+ height: 10px;
+ margin: auto;
+ }
+
+ #absolute {
+ position: absolute;
+ left: 0;
+ right: 0;
+ }
+ #relative {
+ position: relative;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <x><y id="absolute"></y></x>
+ <x><y id="relative"></y></x>
+ <script type="text/javascript">
+ let idsToTest = [
+ "absolute",
+ "relative",
+ ];
+
+ for (let id of idsToTest) {
+ let elem = document.getElementById(id);
+ let elemStyle = window.getComputedStyle(elem);
+
+ // positioned element's auto margins should be resolved to 10px.
+ test(function() {
+ assert_equals(elemStyle.getPropertyValue("margin-left"), "10px");
+ assert_equals(elemStyle.getPropertyValue("margin-right"), "10px");
+ }, id + "_computed_margins");
+
+ // positioned element should have a left and right of 0px (as authored).
+ test(function() {
+ assert_equals(elemStyle.getPropertyValue("left"), "0px");
+ assert_equals(elemStyle.getPropertyValue("right"), "0px");
+ }, id + "_computed_left_and_right");
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/computed-style-set-property.html b/testing/web-platform/tests/css/cssom/computed-style-set-property.html
new file mode 100644
index 0000000000..a1f1380dcc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/computed-style-set-property.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<title>NoModificationAllowedError when mutating read only properties</title>
+<link rel="help" href="https://www.w3.org/TR/cssom-1/#dom-cssstyledeclaration-setpropertyvalue">
+<meta name="assert" content="This test verifies that NoModificationAllowedError is thrown when mutating read only properties" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body></body>
+<script>
+test(function(t) {
+ assert_equals(document.defaultView.getComputedStyle(document.body, null).parentRule, null);
+}, "Computed style parent (should be null)");
+
+test(function(t) {
+ assert_throws_dom("NoModificationAllowedError", function() {
+ document.defaultView.getComputedStyle(document.body, null).color = "blue";
+ });
+}, "Exception thrown when trying to change a computed style declaration via property");
+
+test(function(t) {
+ assert_throws_dom("NoModificationAllowedError", function() {
+ document.defaultView.getComputedStyle(document.body, null).setProperty("color", "blue");
+ });
+}, "Exception thrown when trying to change a computed style declaration via setProperty");
+
+test(function(t) {
+ assert_throws_dom("NoModificationAllowedError", function() {
+ document.defaultView.getComputedStyle(document.body, null).webkitTransition = "";
+ });
+}, "Exception thrown when trying to change a computed style alias via property");
+
+test(function(t) {
+ assert_throws_dom("NoModificationAllowedError", function() {
+ document.defaultView.getComputedStyle(document.body, null).setProperty("webkitTransition", "");
+ });
+}, "Exception thrown when trying to change a computed style alias via setProperty");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html b/testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html
new file mode 100644
index 0000000000..744b3a744c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/css-style-attr-decl-block.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="author" title="Xidorn Quan" href="mailto:me@upsuper.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#css-declaration-blocks">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+function createTestElement(style) {
+ let wrapper = document.createElement("div");
+ wrapper.innerHTML = `<div id="test" style="${style}"></div>`;
+ return wrapper.querySelector("#test");
+}
+
+test(function() {
+ let elem = createTestElement("z-index: 10;");
+ assert_equals(elem.style.cssText, "z-index: 10;");
+}, "Style attribute should create CSS declaration block based on its content");
+
+test(function() {
+ let elem = createTestElement("z-index: 20;");
+ let style = elem.style;
+ assert_equals(style.cssText, "z-index: 20;");
+ function assert_css_text(value, action) {
+ assert_equals(style.cssText, value, "CSS declaration block after " + action);
+ }
+ elem.setAttribute("style", "z-index: 21;");
+ assert_css_text("z-index: 21;", "changing the style attribute");
+ elem.removeAttribute("style");
+ assert_css_text("", "removing the style attribute");
+ elem.setAttribute("style", "position: absolute;");
+ assert_css_text("position: absolute;", "adding style attribute again");
+}, "Changes to style attribute should reflect on CSS declaration block");
+
+test(function() {
+ let elem = createTestElement("z-index: 30;");
+ let style = elem.style;
+ assert_equals(style.cssText, "z-index: 30;");
+ function assert_attr(value, action) {
+ assert_equals(elem.getAttribute("style"), value, "style attribute after " + action);
+ }
+ style.setProperty("z-index", "31");
+ assert_attr("z-index: 31;", "changing property in CSS declaration block");
+ style.removeProperty("z-index");
+ assert_attr("", "removing property from CSS declaration block");
+ style.setProperty("position", "absolute");
+ assert_attr("position: absolute;", "adding property to CSS declaration block");
+ style.cssText = "z-index: 32;";
+ assert_attr("z-index: 32;", "changing cssText");
+ style.cssText = "z-index: 33; invalid";
+ assert_attr("z-index: 33;", "changing cssText to a partial invalid value");
+}, "Changes to CSS declaration block should reflect on style attribute");
+
+test(function() {
+ let elem = createTestElement("z-index: 40;");
+ let style = elem.style;
+ assert_equals(style.cssText, "z-index: 40;");
+ // Create an observer for the element.
+ let observer = new MutationObserver(function() {});
+ observer.observe(elem, {attributes: true, attributeOldValue: true});
+ function assert_record_with_old_value(oldValue, action) {
+ let records = observer.takeRecords();
+ assert_equals(records.length, 1, "number of mutation records after " + action);
+ let record = records[0];
+ assert_equals(record.type, "attributes", "mutation type after " + action);
+ assert_equals(record.attributeName, "style", "mutated attribute after " + action);
+ assert_equals(record.oldValue, oldValue, "old value after " + action);
+ }
+ style.setProperty("z-index", "41");
+ assert_record_with_old_value("z-index: 40;", "changing property in CSS declaration block");
+ style.cssText = "z-index: 42;";
+ assert_record_with_old_value("z-index: 41;", "changing cssText");
+ style.cssText = "z-index: 42;";
+ assert_record_with_old_value("z-index: 42;", "changing cssText with the same content");
+ style.removeProperty("z-index");
+ assert_record_with_old_value("z-index: 42;", "removing property from CSS declaration block");
+ // Mutation to shorthand properties should also trigger only one mutation record.
+ style.setProperty("margin", "1px");
+ assert_record_with_old_value("", "adding shorthand property to CSS declaration block");
+ style.removeProperty("margin");
+ assert_record_with_old_value("margin: 1px;", "removing shorthand property from CSS declaration block");
+ // Final sanity check.
+ assert_equals(elem.getAttribute("style"), "");
+}, "Changes to CSS declaration block should queue mutation record for style attribute");
+
+test(function() {
+ let elem = createTestElement("z-index: 50; invalid");
+ let style = elem.style;
+ assert_equals(style.cssText, "z-index: 50;");
+ // Create an observer for the element.
+ let observer = new MutationObserver(function() {});
+ observer.observe(elem, {attributes: true});
+ function assert_no_record(action) {
+ let records = observer.takeRecords();
+ assert_equals(records.length, 0, "expect no record after " + action);
+ }
+ style.setProperty("z-index", "invalid");
+ assert_no_record("setting invalid value to property");
+ // Longhand property.
+ style.removeProperty("position");
+ assert_no_record("removing non-existing longhand property");
+ style.setProperty("position", "");
+ assert_no_record("setting empty string to non-existing longhand property");
+ // Shorthand property.
+ style.removeProperty("margin");
+ assert_no_record("removing non-existing shorthand property");
+ style.setProperty("margin", "");
+ assert_no_record("setting empty string to non-existing shorthand property");
+ // Check that the value really isn't changed.
+ assert_equals(elem.getAttribute("style"), "z-index: 50; invalid",
+ "style attribute after removing non-existing properties");
+}, "Removing non-existing property or setting invalid value on CSS declaration block shouldn't queue mutation record");
+
+test(function() {
+ let elem = createTestElement("background-image: url(./);");
+ let style = elem.style;
+ let base = document.createElement("base");
+ base.href = "/";
+ document.body.appendChild(elem);
+ let originalComputedValue = getComputedStyle(elem).backgroundImage;
+ document.head.appendChild(base);
+ this.add_cleanup(() => {
+ document.head.removeChild(base);
+ document.body.removeChild(elem);
+ });
+ style.setProperty("background-color", "green");
+ assert_equals(getComputedStyle(elem).backgroundImage, originalComputedValue,
+ "getComputedStyle(elem).backgroundImage after setting background-color");
+ style.setProperty("background-image", "url(./)");
+ assert_not_equals(getComputedStyle(elem).backgroundImage, originalComputedValue,
+ "getComputedStyle(elem).backgroundImage after setting background-image");
+}, "Changes to CSS declaration block after a base URL change");
+
+test(function() {
+ let e1 = document.createElement('div');
+ let e2 = document.createElement('div');
+ document.body.append(e1, e2);
+ this.add_cleanup(() => {
+ e1.remove();
+ e2.remove();
+ });
+ e1.style.cssText = "all:revert;border-bottom-left-radius:1px;";
+ e2.style.cssText = "all:unset;border-bottom-left-radius:1px;";
+ let processed = e1.style.cssText.split(';')
+ .map(x => x.replace(/revert$/, 'unset')).join(';');
+ assert_equals(processed, e2.style.cssText);
+}, "Expansion of all:unset and all:revert treated identically");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html b/testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html
new file mode 100644
index 0000000000..524a5adca3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/css-style-attribute-modifications.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:ecobos@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-elementcssinlinestyle-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="test" style="color: red"></div>
+<script>
+test(function() {
+ var el = document.getElementById("test");
+ el.style.color = "";
+ assert_true(el.hasAttribute("style"));
+
+ el.removeAttribute("style");
+ assert_false(el.hasAttribute("style"));
+}, "Mutating the style declaration doesn't remove the style attribute");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html b/testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html
new file mode 100644
index 0000000000..a0e2275491
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/css-style-declaration-modifications.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: CSSStyleDeclaration Interface</title>
+ <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="CSSStyleDeclaration is properly initialized and can be modified through its interface">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style id="styleElement">
+ #test { color: green; }
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <div id="test"></div>
+ <script type="text/javascript">
+ var declaration;
+ setup(function() {
+ var styleElement = document.getElementById("styleElement");
+ declaration = styleElement.sheet.cssRules.item(0).style;
+ });
+
+ test(function() {
+ assert_equals(declaration.cssText, "color: green;");
+ assert_equals(declaration.getPropertyValue("color"), "green");
+ }, "Reading CSSStyleDeclaration initialized from a style element");
+
+ test(function() {
+ declaration.cssText = "margin-left:10px; padding-left:10px";
+ assert_equals(declaration.cssText, "margin-left: 10px; padding-left: 10px;");
+ assert_equals(declaration.length, 2);
+ assert_equals(declaration.item(0), "margin-left");
+ assert_equals(declaration.item(1), "padding-left");
+ assert_equals(declaration.getPropertyValue("margin-left"), "10px");
+ assert_equals(declaration.getPropertyValue("padding-left"), "10px");
+
+ var computedStyle = window.getComputedStyle(document.getElementById("test"));
+ assert_equals(computedStyle.getPropertyValue("margin-left"), "10px");
+ assert_equals(computedStyle.getPropertyValue("padding-left"), "10px");
+ }, "Setting CSSStyleDeclaration#cssText");
+
+ test(function() {
+ while (declaration.length > 0) {
+ declaration.removeProperty(declaration.item(0));
+ }
+ declaration.setProperty("margin-left", "15px");
+ declaration.setProperty("padding-left", "15px");
+
+
+ assert_equals(declaration.length, 2);
+ assert_equals(declaration.item(0), "margin-left");
+ assert_equals(declaration.item(1), "padding-left");
+ assert_equals(declaration.getPropertyValue("margin-left"), "15px");
+ assert_equals(declaration.getPropertyValue("padding-left"), "15px");
+
+ var computedStyle = window.getComputedStyle(document.getElementById("test"));
+ assert_equals(computedStyle.getPropertyValue("margin-left"), "15px");
+ assert_equals(computedStyle.getPropertyValue("padding-left"), "15px");
+
+ }, "Calling CSSStyleDeclaration#setProperty");
+
+ test(function() {
+ declaration.setProperty("background-color", "red", "ImPoRtAnt");
+ assert_equals(declaration.getPropertyPriority("background-color"), "important");
+ }, "setProperty priority should be case-insensitive");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/css-style-reparse.html b/testing/web-platform/tests/css/cssom/css-style-reparse.html
new file mode 100644
index 0000000000..d5153cf267
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/css-style-reparse.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset=utf-8>
+ <title>CSS Test: DOM modification re-parsing test</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <script src=/resources/testharness.js></script>
+ <script src=/resources/testharnessreport.js></script>
+ <style>div { min-width: 0px; }</style>
+ <style id="style-element"></style>
+</head>
+<body>
+<div id="test-div"></div>
+<script type="text/javascript">
+ var style = document.getElementById("style-element");
+ var div = document.getElementById("test-div");
+
+ function testProperty(prop) {
+ // Assigning an empty string to textContent or innerHTML should trigger a
+ // reparse only if the element is not empty.
+ style.sheet.insertRule("#test-div { min-width: 42px; }");
+ assert_equals(getComputedStyle(div).minWidth, "42px");
+
+ style[prop] = "";
+ assert_equals(getComputedStyle(div).minWidth, "42px");
+
+ style[prop] = " ";
+ assert_equals(getComputedStyle(div).minWidth, "0px");
+
+ style.sheet.insertRule("#test-div { min-width: 42px; }");
+ assert_equals(getComputedStyle(div).minWidth, "42px");
+
+ style[prop] = "";
+ assert_equals(getComputedStyle(div).minWidth, "0px");
+
+ style.sheet.insertRule("#test-div { min-width: 42px; }");
+ assert_equals(getComputedStyle(div).minWidth, "42px");
+
+ style.appendChild(document.createTextNode(""));
+ assert_equals(getComputedStyle(div).minWidth, "0px");
+
+ style.sheet.insertRule("#test-div { min-width: 42px; }");
+ assert_equals(getComputedStyle(div).minWidth, "42px");
+
+ style[prop] = "";
+ assert_equals(getComputedStyle(div).minWidth, "0px");
+ }
+
+ test(function() {
+ testProperty("textContent");
+ }, "style.textContent modification");
+
+ test(function() {
+ testProperty("innerHTML");
+ }, "style.innerHTML modification");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssimportrule-parent.html b/testing/web-platform/tests/css/cssom/cssimportrule-parent.html
new file mode 100644
index 0000000000..2792af69c0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssimportrule-parent.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>CSSImportRule correctly unlinks its child stylesheet from its parent</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssimportrule-interface">
+<style>
+ @import "data:text/css,:root{background:red}";
+</style>
+<script>
+let t = async_test("@import stylesheet is properly unlinked from parent after removal");
+window.onload = t.step_func_done(function() {
+ let sheet = document.styleSheets[0];
+ let childSheet = sheet.cssRules[0].styleSheet;
+ assert_not_equals(childSheet, null, "@import rule should have a stylesheet");
+ assert_equals(childSheet.parentStyleSheet, sheet, "@import rule should the correct parent");
+ sheet.deleteRule(0);
+ assert_equals(childSheet.parentStyleSheet, null, "@import rule should be correctly unlinked");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html b/testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html
new file mode 100644
index 0000000000..3db5634ebe
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssimportrule-sheet-identity.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>CSSImportRule has different sheets even if referencing the same URL</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssimportrule-interface">
+<link rel="stylesheet" href="support/import-rule.css">
+<link rel="stylesheet" href="support/import-rule.css">
+<script>
+let t = async_test("CSSImportRule has different sheets even if referencing the same URL");
+window.onload = t.step_func_done(function() {
+ let sheet1 = document.styleSheets[0];
+ let sheet2 = document.styleSheets[1];
+
+ assert_not_equals(sheet1, sheet2);
+
+ let childSheet1 = sheet1.cssRules[0].styleSheet;
+ let childSheet2 = sheet2.cssRules[0].styleSheet;
+
+ assert_not_equals(childSheet1, null);
+ assert_not_equals(childSheet2, null);
+ assert_not_equals(childSheet1, childSheet2, "@import pointing to the same URL shouldn't point to the same StyleSheet object");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssimportrule.html b/testing/web-platform/tests/css/cssom/cssimportrule.html
new file mode 100644
index 0000000000..84cd7292f3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssimportrule.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM CSSRule CSSImportRule interface</title>
+ <link rel="author" title="Letitia Lew" href="mailto:lew.letitia@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-rules">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssimportrule-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="All properties for this CSSImportRule instance of CSSRule are initialized correctly">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <style id="styleElement" type="text/css">
+ @import url("support/a-green.css");
+ @import url("support/a-green.css") screen;
+ @import url("support/a-green.css") all;
+ @import url("support/a-green") supports((display: flex) or (display: block));
+ @import url('quote"quote');
+ @page { background-color: red; }
+ </style>
+</head>
+<body>
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ var styleSheet, ruleList, rule, ruleWithMedia, ruleWithMediaAll, ruleWithSupports, ruleWithQuote;
+ setup(function() {
+ styleSheet = document.getElementById("styleElement").sheet;
+ ruleList = styleSheet.cssRules;
+ rule = ruleList[0];
+ ruleWithMedia = ruleList[1];
+ ruleWithMediaAll = ruleList[2];
+ ruleWithSupports = ruleList[3];
+ ruleWithQuote = ruleList[4];
+ });
+
+ test(function() {
+ assert_true(rule instanceof CSSRule);
+ assert_true(rule instanceof CSSImportRule);
+ assert_true(ruleWithMedia instanceof CSSRule);
+ assert_true(ruleWithMedia instanceof CSSImportRule);
+ assert_true(ruleWithSupports instanceof CSSRule);
+ assert_true(ruleWithSupports instanceof CSSImportRule);
+ }, "CSSRule and CSSImportRule types");
+
+ test(function() {
+ assert_equals(rule.STYLE_RULE, 1);
+ assert_equals(rule.IMPORT_RULE, 3);
+ assert_equals(rule.MEDIA_RULE, 4);
+ assert_equals(rule.FONT_FACE_RULE, 5);
+ assert_equals(rule.PAGE_RULE, 6);
+ assert_equals(rule.NAMESPACE_RULE, 10);
+ assert_idl_attribute(rule, "type");
+ assert_equals(typeof rule.type, "number");
+ }, "Type of CSSRule#type and constant values");
+
+ test(function() {
+ assert_true(rule instanceof CSSRule);
+ assert_idl_attribute(rule, "cssText");
+ assert_idl_attribute(rule, "parentRule");
+ assert_idl_attribute(rule, "parentStyleSheet");
+
+ assert_readonly(rule, "type");
+ assert_readonly(rule, "parentRule");
+ assert_readonly(rule, "parentStyleSheet");
+ }, "Existence and writability of CSSRule attributes");
+
+ test(function() {
+ assert_equals(rule.type, rule.IMPORT_RULE);
+ assert_equals(typeof rule.cssText, "string");
+ assert_equals(rule.cssText, '@import url("support/a-green.css");');
+ assert_equals(ruleWithMedia.cssText, '@import url("support/a-green.css") screen;');
+ assert_equals(ruleWithMediaAll.cssText, '@import url("support/a-green.css") all;');
+ assert_equals(ruleWithSupports.cssText, '@import url("support/a-green") supports((display: flex) or (display: block));');
+ assert_equals(ruleWithQuote.cssText, '@import url("quote\\\"quote");');
+ assert_equals(rule.parentRule, null);
+ assert_true(rule.parentStyleSheet instanceof CSSStyleSheet);
+ }, "Values of CSSRule attributes");
+
+ test(function() {
+ assert_idl_attribute(rule, "href");
+ assert_idl_attribute(rule, "media");
+ assert_idl_attribute(rule, "styleSheet");
+
+ assert_readonly(rule, "href");
+ assert_readonly(rule, "media");
+ assert_readonly(rule, "styleSheet");
+ }, "Existence and writability of CSSImportRule attributes");
+
+ test(function() {
+ assert_equals(typeof rule.href, "string");
+ assert_true(rule.media instanceof MediaList);
+ assert_true(rule.styleSheet instanceof CSSStyleSheet);
+ assert_true(ruleWithMedia.media.length > 0);
+ assert_equals(ruleWithMedia.media.mediaText, "screen");
+ }, "Values of CSSImportRule attributes");
+
+ test(function() {
+ ruleWithMedia.media = "print";
+ assert_equals(ruleWithMedia.media.mediaText, "print");
+ }, "CSSImportRule : MediaList mediaText attribute should be updated due to [PutForwards]");
+
+ test(function() {
+ var ruleWithPage = ruleList[5];
+ ruleWithPage.style = "margin-top: 10px;"
+ assert_equals(ruleWithPage.style.cssText, "margin-top: 10px;");
+ }, "CSSStyleDeclaration cssText attribute should be updated due to [PutForwards]");
+
+ test(function() {
+ styleSheet.media = "screen";
+ assert_equals(styleSheet.media.mediaText, "screen");
+ }, "StyleSheet : MediaList mediaText attribute should be updated due to [PutForwards]");
+
+ test(function() {
+ assert_idl_attribute(ruleWithSupports, "supportsText");
+ assert_readonly(ruleWithSupports, "supportsText");
+ }, "Existence and writability of CSSImportRule supportsText attribute");
+
+ test(function() {
+ assert_equals(ruleWithSupports.supportsText, "(display: flex) or (display: block)");
+ }, "Value of CSSImportRule supportsText attribute");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html b/testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html
new file mode 100644
index 0000000000..27479f026d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-cssText-serialize.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM Parsing Test: getting cssText must return the result of serializing the CSS declaration blocks.</title>
+ <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface">
+
+ <link rel="source" href="http://trac.webkit.org/export/120528/trunk/LayoutTests/fast/css/cssText-cache.html">
+ <meta name="flags" content="dom">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+ <div id="box"></div>
+ <script>
+ test(function() {
+ var style = document.getElementById('box').style;
+ style.left = "10px";
+ assert_equals(style.cssText, "left: 10px;");
+ style.right = "20px";
+ assert_equals(style.cssText, "left: 10px; right: 20px;");
+ }, 'CSSStyleDeclaration cssText serializes declaration blocks.');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html b/testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html
new file mode 100644
index 0000000000..1fb7ccd324
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-cssstyledeclaration-set.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM: CSSStyleDeclaration on HTMLElement represents inline style changes</title>
+ <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface">
+
+ <link rel="source" href="http://trac.webkit.org/export/120528/trunk/LayoutTests/fast/css/cssText-cache.html">
+ <meta name="flags" content="dom">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+
+ <div id="box"></div>
+
+ <script>
+
+ var style = document.getElementById('box').style;
+
+ test(function(){
+
+ style.left = "10px";
+ assert_equals(style.left, "10px", 'left property set on element\'s CSSStyleDeclaration Object');
+ style.left = "20px";
+ assert_equals(style.left, "20px", 'left property set on element\'s CSSStyleDeclaration Object');
+
+ }, 'CSSStyleDeclaration on HTMLElement represents inline style changes');
+
+ </script>
+
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html b/testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html
new file mode 100644
index 0000000000..ed1cab257d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-fontfacerule-constructors.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM Parsing Test: @font-face rules toString() as valid interfaces</title>
+ <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
+ <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 -->
+ <link rel="help" href="https://drafts.csswg.org/css-fonts-4/#om-fontface">
+
+ <meta name="flags" content="dom">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+
+
+ <style id="teststyles">
+ @font-face {
+ src: url(http://foo/bar/font.ttf);
+ }
+ @font-face {
+ font-family: STIXGeneral;
+ src: local(STIXGeneral), url(/stixfonts/STIXGeneral.otf);
+ unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF;
+ }
+ @font-face {
+ font-family: MainText;
+ src: url(http://example.com/font.ttf);
+ font-variant: oldstyle-nums proportional-nums styleset(1,3);
+ }
+
+ @font-face {
+ font-family: BodyText;
+ src: local("HiraMaruPro-W4");
+ font-variant: proportional-width;
+ font-feature-settings: "ital"; /* Latin italics within CJK text feature */
+ }
+ </style>
+
+
+ <script>
+ var validRules = document.getElementById('teststyles').sheet.cssRules;
+
+ test(function(){
+ for (var i = 0; i < validRules.length; ++i) {
+ assert_equals(validRules.item(i).toString(), '[object CSSFontFaceRule]');
+ }
+ }, '@font-face declarations are instances of CSSFontFaceRule')
+
+
+ test(function(){
+ for (var i = 0; i < validRules.length; ++i) {
+ assert_equals(validRules.item(i).style.toString(), '[object CSSStyleDeclaration]');
+ }
+ }, 'The style attribute must return a CSSStyleDeclaration block')
+
+ </script>
+
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssom-fontfacerule.html b/testing/web-platform/tests/css/cssom/cssom-fontfacerule.html
new file mode 100644
index 0000000000..9a06808e3a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-fontfacerule.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM Parsing Test: @font-face rules parsed into CSSOM CSSFontFaceRules</title>
+ <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
+ <link rel="help" href="https://drafts.csswg.org/css-fonts-4/#om-fontface">
+
+ <meta name="flags" content="dom">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+
+
+ <style id="teststyles">
+ @font-face {
+ src: url(http://foo/bar/font.ttf);
+ }
+ @font-face {
+ font-family: STIXGeneral;
+ src: local(STIXGeneral), url(/stixfonts/STIXGeneral.otf);
+ unicode-range: U+000-49F, U+2000-27FF, U+2900-2BFF, U+1D400-1D7FF;
+ }
+ @font-face {
+ font-family: MainText;
+ src: url(http://example.com/font.ttf);
+ font-variant: oldstyle-nums proportional-nums styleset(1,3);
+ }
+
+ @font-face {
+ font-family: BodyText;
+ src: local("HiraMaruPro-W4");
+ font-variant: proportional-width;
+ font-feature-settings: "ital"; /* Latin italics within CJK text feature */
+ }
+ </style>
+
+
+ <script>
+ var validRules = document.getElementById('teststyles').sheet.cssRules;
+
+ test(function(){
+
+ assert_equals(validRules[0].style.src, 'url("http://foo/bar/font.ttf")');
+ assert_equals(validRules[1].style.fontFamily, 'STIXGeneral');
+
+ /* unimplemented @font-face properties are not represented in CSSOM */
+
+ }, 'CSSStyleDeclaration values are represented within CSSFontFaceRule')
+
+ </script>
+
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html b/testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html
new file mode 100644
index 0000000000..27d802cd3d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-getPropertyValue-common-checks.html
@@ -0,0 +1,223 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Common serialization checks for all properties</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue">
+
+<div id="element"></div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const element = document.getElementById("element");
+const { style } = element;
+const computedStyle = getComputedStyle(element);
+const cssProperties = new Set();
+const cssShorthands = new Map();
+const cssShorthandsForLonghand = new Map();
+const cssLonghands = new Set();
+const cssAliases = new Map();
+const initialValues = new Map();
+
+setup(function() {
+ for (let obj = style; obj; obj = Reflect.getPrototypeOf(obj)) {
+ for (let name of Object.getOwnPropertyNames(obj)) {
+ const property = name.replace(/[A-Z]/g, c => "-" + c.toLowerCase());
+ if (CSS.supports(property, "initial")) {
+ cssProperties.add(property);
+ }
+ }
+ }
+ for (let property of cssProperties) {
+ style.cssText = "";
+ style.setProperty(property, "initial");
+ if (style.length > 1) {
+ cssShorthands.set(property, [...style]);
+ for (let longhand of style) {
+ if (cssShorthandsForLonghand.has(longhand)) {
+ cssShorthandsForLonghand.get(longhand).add(property);
+ } else {
+ cssShorthandsForLonghand.set(longhand, new Set([property]));
+ }
+ }
+ } else if (style.length === 1) {
+ if (property === style[0]) {
+ cssLonghands.add(property);
+ } else {
+ cssAliases.set(property, style[0]);
+ }
+ }
+ }
+});
+
+test(function() {
+ const bad = [];
+ for (let property of cssProperties) {
+ style.cssText = "";
+ style.setProperty(property, "initial");
+ const result = style.getPropertyValue(property);
+ if (result !== "initial") {
+ bad.push([property, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All properties can serialize 'initial'");
+
+test(function() {
+ for (let longhand of cssLonghands) {
+ element.style.setProperty(longhand, "initial");
+ }
+ const bad = [];
+ for (let property of cssProperties) {
+ const result = computedStyle.getPropertyValue(property);
+ if (CSS.supports(property, result)) {
+ initialValues.set(property, result);
+ } else if (property !== "all") {
+ bad.push([property, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All properties (except 'all') can serialize their initial value (computed)");
+
+test(function() {
+ const bad = [];
+ for (let [property, value] of initialValues) {
+ style.cssText = "";
+ style.setProperty(property, value);
+ const result = style.getPropertyValue(property);
+ if (!CSS.supports(property, result) && property !== "all") {
+ bad.push([property, value, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All properties (except 'all') can serialize their initial value (specified)");
+
+test(function() {
+ const bad = [];
+ for (let [shorthand, longhands] of cssShorthands) {
+ style.cssText = "";
+ for (let longhand of longhands) {
+ style.setProperty(longhand, "initial");
+ }
+ const result = style.getPropertyValue(shorthand);
+ if (result !== "initial") {
+ bad.push([shorthand, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All shorthands can serialize their longhands set to 'initial'");
+
+test(function() {
+ const bad = [];
+ outerloop:
+ for (let [shorthand, longhands] of cssShorthands) {
+ style.cssText = "";
+ for (let longhand of longhands) {
+ if (!initialValues.has(longhand)) {
+ continue outerloop;
+ }
+ style.setProperty(longhand, initialValues.get(longhand));
+ }
+ const result = style.getPropertyValue(shorthand);
+ if (!CSS.supports(shorthand, result) && shorthand !== "all") {
+ bad.push([shorthand, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All shorthands (except 'all') can serialize their longhands set to their initial value");
+
+test(function() {
+ const bad = [];
+ for (let [alias, target] of cssAliases) {
+ style.cssText = "";
+ style.setProperty(target, "initial");
+ const result = style.getPropertyValue(alias);
+ if (result !== "initial") {
+ bad.push([alias, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All aliases can serialize target property set to 'initial'");
+
+test(function() {
+ const bad = [];
+ for (let [alias, target] of cssAliases) {
+ if (!initialValues.has(target)) {
+ continue;
+ }
+ style.cssText = "";
+ style.setProperty(target, initialValues.get(target));
+ const result = style.getPropertyValue(alias);
+ if (!CSS.supports(alias, result)) {
+ bad.push([alias, result]);
+ }
+ }
+ assert_array_equals(bad, []);
+}, "All aliases can serialize target property set to its initial value");
+
+test(function() {
+ const bad = [];
+ for (let [shorthand, longhands] of cssShorthands) {
+ for (let longhand of longhands) {
+ style.cssText = "";
+ style.setProperty(shorthand, "initial");
+ style.setProperty(longhand, "inherit");
+ const result = style.getPropertyValue(shorthand);
+ if (result !== "") {
+ bad.push([shorthand, longhand, result]);
+ }
+ }
+ }
+ assert_array_equals(bad, []);
+}, "Can't serialize shorthand when longhands are set to different css-wide keywords");
+
+test(function() {
+ const bad = [];
+ for (let [shorthand, longhands] of cssShorthands) {
+ for (let longhand of longhands) {
+ style.cssText = "";
+ style.setProperty(shorthand, "initial");
+ style.setProperty(longhand, "initial", "important");
+ const result = style.getPropertyValue(shorthand);
+ if (result !== "") {
+ bad.push([shorthand, longhand, result]);
+ }
+ }
+ }
+ assert_array_equals(bad, []);
+}, "Can't serialize shorthand when longhands have different priority");
+
+test(function() {
+ const bad = [];
+ for (let [shorthand, longhands] of cssShorthands) {
+ for (let longhand of longhands) {
+ style.cssText = "";
+ style.setProperty(shorthand, "initial");
+ style.removeProperty(longhand);
+ const result = style.getPropertyValue(shorthand);
+ if (result !== "") {
+ bad.push([shorthand, longhand, result]);
+ }
+ }
+ }
+ assert_array_equals(bad, []);
+}, "Can't serialize shorthand set to 'initial' when some longhand is missing");
+
+test(function() {
+ const bad = [];
+ for (let [shorthand, longhands] of cssShorthands) {
+ if (initialValues.has(shorthand)) {
+ for (let longhand of longhands) {
+ style.cssText = "";
+ style.setProperty(shorthand, initialValues.get(shorthand));
+ style.removeProperty(longhand);
+ const result = style.getPropertyValue(shorthand);
+ if (result !== "") {
+ bad.push([shorthand, longhand, result]);
+ }
+ }
+ }
+ }
+ assert_array_equals(bad, []);
+}, "Can't serialize shorthand set to initial value when some longhand is missing");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssom-pagerule.html b/testing/web-platform/tests/css/cssom/cssom-pagerule.html
new file mode 100644
index 0000000000..994257b245
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-pagerule.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<title>CSSOM: CSSPageRule tests</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-csspagerule-interface" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @page {}
+</style>
+<script>
+ const sheet = document.styleSheets[0];
+ const rule = sheet.cssRules[0];
+
+ test(() => {
+ assert_true(!!rule);
+ assert_equals(rule.type, CSSRule.PAGE_RULE);
+ }, "Sanity checks");
+
+ test(() => {
+ assert_equals(rule.selectorText, "");
+ }, "Page selector is initially the empty string");
+
+ test(() => {
+ rule.selectorText = ":left";
+ assert_equals(rule.selectorText, ":left");
+ }, "Set selectorText to :left pseudo page");
+
+ test(() => {
+ rule.selectorText = "named";
+ assert_equals(rule.selectorText, "named");
+ }, "Set selectorText to named page");
+
+ test(() => {
+ rule.selectorText = "named:first";
+ assert_equals(rule.selectorText, "named:first");
+ }, "Set selectorText to named page with :first pseudo page");
+
+ test(() => {
+ rule.selectorText = "named:First";
+ assert_equals(rule.selectorText, "named:first");
+ }, "Set selectorText to named page with case insensitive :first pseudo page");
+
+ test(() => {
+ rule.selectorText = "named:first:first";
+ assert_equals(rule.selectorText, "named:first:first");
+ }, "Set selectorText to named page with two :first pseudo page");
+
+ test(() => {
+ rule.selectorText = "named:first:left:right:first";
+ assert_equals(rule.selectorText, "named:first:left:right:first");
+ }, "Set selectorText to named page with pseudo pages of " +
+ ":first, :left, :right, :first in order.");
+
+ test(() => {
+ rule.selectorText = "";
+ rule.selectorText = "named :first";
+ assert_equals(rule.selectorText, "");
+ }, "Cannot set selectorText to named page with pseudo, whitespace between");
+
+ test(() => {
+ rule.selectorText = "";
+ rule.selectorText = ":first :left";
+ assert_equals(rule.selectorText, "");
+ }, "Cannot set selectorText to two pseudos, whitespace between");
+
+ test(() => {
+ rule.selectorText = "";
+ rule.selectorText = ":notapagepseudo";
+ assert_equals(rule.selectorText, "");
+ }, "Cannot set selectorText to invalid pseudo page");
+
+ test(() => {
+ assert_equals(rule.parentStyleSheet, sheet);
+ sheet.deleteRule(0);
+ assert_equals(rule.parentStyleSheet, null);
+ rule.selectorText = "pagename";
+ }, "Set selectorText to named page after rule was removed");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html b/testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html
new file mode 100644
index 0000000000..19aab35357
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-ruleTypeAndOrder.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<head>
+ <title>CSS OM: CSS Rule Types and Order</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom-1/#the-cssrule-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="Testing Serialization of Style Rules">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style id="s-0">
+ @namespace svg "http://www.w3.org/2000/svg";
+ </style>
+ <style id="s-1">
+ @import url('main.css');
+ </style>
+ <style id="s-2">
+ h1 { background: indianred; }
+ </style>
+ <style id="s-3">
+ @namespace svg "http://www.w3.org/2000/svg";
+ svg|a { color: white; }
+ </style>
+ <style id="s-4">
+ @font-face {
+ font-family: 'Megalopolis';
+ src: url('fonts/megalopolisextra-webfont.eot');
+ src: url('fonts/megalopolisextra-webfont.eot?#iefix') format('embedded-opentype'),
+ url('fonts/megalopolisextra-webfont.woff') format('woff'),
+ url('fonts/megalopolisextra-webfont.ttf') format('truetype'),
+ url('fonts/megalopolisextra-webfont.svg#MEgalopolisExtraRegular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+ }
+ </style>
+ <style id="s-5">
+ @media (min-width: 200px) {
+ h1 { background: aliceblue; }
+ }
+ </style>
+ <style id="s-6">
+ @page :first {
+ h1 { color: #444; }
+ }
+ </style>
+</head>
+<body>
+ <div id="log"></div>
+ <script type="text/javascript">
+ "use strict";
+
+ var stylesheets = document.styleSheets;
+
+ var expectedOrderOfTypes = [10, 3, 1, 1, 5, 4, 6];
+
+ var typesText = {
+ 1: 'style rule',
+ 3: '@import rule',
+ 4: 'media rule',
+ 5: '@font-face rule',
+ 6: 'page rule',
+ 10: 'namespace rule'
+ };
+
+ for (var i = 0; i < stylesheets.length; i++) {
+ test( function () {
+ if (i === 3) {
+ var cssType = stylesheets[i].cssRules[1].type;
+ } else {
+ var cssType = stylesheets[i].cssRules[0].type;
+ }
+ assert_equals(typesText[cssType], typesText[expectedOrderOfTypes[i]]);
+ }, 'Type of #s-' + i + ' is expected to be ' + typesText[expectedOrderOfTypes[i]]);
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html b/testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html
new file mode 100644
index 0000000000..4c0f9a2f93
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssom-setProperty-shorthand.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM: CSSStyleDeclaration (set|remove)PropertyValue sets/removes shorthand properties</title>
+ <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface">
+
+ <link rel="source" href="http://trac.webkit.org/export/120528/trunk/LayoutTests/fast/css/cssom-remove-shorthand-property.html">
+ <meta name="flags" content="dom">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+
+ <div id="box"></div>
+
+ <script>
+ shorthandProperties = [
+ "font",
+ "border-top",
+ "border-right",
+ "border-bottom",
+ "border-left",
+ "border",
+ "border-color",
+ "border-style",
+ "border-width",
+ "background-position",
+ "background-repeat",
+ "border-spacing",
+ "list-style",
+ "margin",
+ "outline",
+ "padding",
+ "background",
+ "overflow",
+ "border-radius"
+ ];
+
+ element = document.createElement('span');
+
+ function canSetProperty(propertyName, priority) {
+ element.style.setProperty(propertyName, 'initial', priority);
+ return element.style.getPropertyValue(propertyName) == 'initial';
+ }
+
+ function canRemoveProperty(propertyName) {
+ element.style.removeProperty(propertyName);
+ return element.style.getPropertyValue(propertyName) != 'initial';
+ }
+
+ for (i = 0; i < shorthandProperties.length; ++i) {
+ var propertyName = shorthandProperties[i];
+
+ test(function(){
+ assert_true(canSetProperty(propertyName, ''), 'can setPropertyValue with shorthand');
+ }, 'shorthand ' + propertyName + ' can be set with setProperty');
+
+ test(function(){
+ assert_true(canRemoveProperty(propertyName), 'can setPropertyValue with shorthand');
+ }, 'shorthand ' + propertyName + ' can be removed with removeProperty');
+
+ test(function(){
+ assert_true(canSetProperty(propertyName, 'important'), 'can setPropertyValue with shorthand');
+ }, 'shorthand ' + propertyName + ' can be set with setProperty and priority !important');
+
+ test(function(){
+ assert_true(canRemoveProperty(propertyName), 'can setPropertyValue with shorthand');
+ }, 'shorthand ' + propertyName + ' can be removed with removeProperty even when set with !important');
+
+ }
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html
new file mode 100644
index 0000000000..2efab276c5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-cssfontrule.tentative.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSStyleDeclaration for a CSSFontRule</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface">
+<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#cssfontfacerule">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/5649#issuecomment-755796005">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+@font-face {}
+</style>
+<script>
+
+const fontFaceRule = document.styleSheets[0].cssRules[0];
+
+test(() => {
+ assert_true("unicode-range" in fontFaceRule.style);
+ assert_idl_attribute(fontFaceRule.style, "unicode-range");
+}, "a CSSStyleDeclaration for a CSSFontRule contains a unicode-range attribute");
+
+
+test(() => {
+ assert_true("flex-direction" in fontFaceRule.style);
+ assert_idl_attribute(fontFaceRule.style, "flex-direction");
+}, "a CSSStyleDeclaration for a CSSFontRule contains a flex-direction attribute");
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html
new file mode 100644
index 0000000000..3b408f4cd6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-all-shorthand.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>CSSOM test: serialization of the 'all' shorthand in cssText</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-csstext">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const style = document.createElement("div").style;
+
+test(function() {
+ style.cssText = "all: inherit";
+ assert_equals(style.cssText, "all: inherit;");
+}, "'all' shorthand alone");
+
+test(function() {
+ style.cssText = "width: 100px; all: inherit; height: inherit";
+ assert_equals(style.cssText, "all: inherit;");
+}, "'all' shorthand with 'width' and 'height'");
+
+test(function() {
+ style.cssText = "direction: ltr; all: inherit; unicode-bidi: plaintext";
+ assert_equals(style.cssText, "direction: ltr; all: inherit; unicode-bidi: plaintext;");
+}, "'all' shorthand with 'direction' and 'unicode-bidi'");
+
+test(function() {
+ style.cssText = "width: 100px; --a: a; all: inherit; --b: b; height: inherit";
+ assert_equals(style.cssText, "--a: a; all: inherit; --b: b;");
+}, "'all' shorthand with 'width', 'height' and custom properties");
+
+test(function() {
+ let cssText = "all: inherit; ";
+ for (let longhand of getComputedStyle(document.documentElement)) {
+ cssText += longhand + ": inherit; ";
+ }
+ style.cssText = cssText;
+ assert_equals(style.cssText, "all: inherit; direction: inherit; unicode-bidi: inherit;");
+}, "'all' shorthand with all longhands");
+
+test(function() {
+ style.cssText = "--foo: bar; all: initial";
+ assert_true(style.cssText.includes("--foo: bar"), "cssText serialization includes custom property");
+}, "all:initial should not exclude custom from cssText");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html
new file mode 100644
index 0000000000..01b0a32c3f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-final-delimiter.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM - CSSStyleDeclaration - Text - Serialization - Delimiters</title>
+ <link rel="author" title="Glenn Adams" href="mailto:glenn@skynav.com"/>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface"/>
+ <meta name="flags" content="dom"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="box"></div>
+ <script>
+ var style = document.getElementById('box').style;
+
+ test(function(){
+ style.cssText = "";
+ assert_equals(style.cssText, "");
+ }, 'inline style - text - delimiters - zero declarations');
+
+ test(function(){
+ style.cssText = "left: 10px";
+ assert_equals(style.cssText, "left: 10px;");
+ }, 'inline style - text - delimiters - one declaration');
+
+ test(function(){
+ style.cssText = "left: 10px; right: 20px";
+ assert_equals(style.cssText, "left: 10px; right: 20px;");
+ }, 'inline style - text - delimiters - two declarations');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html
new file mode 100644
index 0000000000..c12faf63a4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-important.html
@@ -0,0 +1,12 @@
+<title>CSSOM test: setting a property with cssText and !important</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-csstext">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="box"></div>
+<script>
+ var style = document.getElementById('box').style;
+ test(function(){
+ style.cssText = "padding: 10px !important; padding-left: 20px;";
+ assert_equals(style.getPropertyValue("padding-left"), "10px");
+ }, "padding-left should be taken from the !important property");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html
new file mode 100644
index 0000000000..686684e614
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM Test: CSSStyleDeclaration.cssText Test</title>
+ <link rel="author" title="kkoichi" href="mailto:coarse.ground@gmail.com">
+ <link rel="reviewer" title="Simon Pieters" href="mailto:simonp@opera.com"><!-- 06-27-2013 -->
+ <link rel="help" href="https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-csstext">
+ <meta name="assert" content="CSS declarations is serialized as expected">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function newElm() {
+ return document.body.appendChild(document.createElement('div'));
+ }
+
+ test(function(){
+ var style = newElm().style;
+ style.COLOR = 'red';
+
+ assert_equals(style.cssText, '');
+
+ }, 'uppercase property');
+
+ test(function(){
+ var style = newElm().style;
+ style.color = 'RED';
+
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29317
+ assert_any(assert_equals, style.cssText, ['color: red;', 'color: RED;']);
+
+ }, 'uppercase value');
+
+ test(function(){
+ var style = newElm().style;
+
+ style.color = 'red';
+
+ style.color = 'unknown color';
+
+ assert_equals(style.cssText, 'color: red;');
+
+ }, 'overwriting with invalid value');
+
+ test(function(){
+ var style = newElm().style;
+ style.color = 'rgb(255, 0, 0)';
+
+ assert_equals(style.cssText, 'color: rgb(255, 0, 0);');
+
+ }, 'use rgb');
+
+ test(function(){
+ var e = newElm();
+ var style = e.style;
+
+ style.color = 'red';
+ style.fontSize = '10pt';
+ style.fontWeight = 'bold';
+
+ assert_equals(style.cssText, 'color: red; font-size: 10pt; font-weight: bold;');
+
+ }, 'cssText order');
+
+ test(function(){
+ var e = newElm();
+ var style = e.style;
+
+ style.fontWeight = 'bold';
+ style.color = 'red';
+ style.fontSize = '10pt';
+
+ assert_equals(style.cssText, 'font-weight: bold; color: red; font-size: 10pt;');
+
+ }, 'another cssText order (non-alphabetical order)');
+
+ test(function(){
+ var style = newElm().style;
+
+ style.color = ' red';
+ style.fontSize = '10pt ';
+
+ assert_equals(style.cssText, 'color: red; font-size: 10pt;');
+
+ }, 'whitespaces in value');
+
+ test(function(){
+ var style = newElm().style;
+
+ style.color = 'red';
+ style.unknown = 'unknown';
+ style.fontSize = '10pt';
+ assert_equals(style.cssText, 'color: red; font-size: 10pt;');
+
+ }, 'invalid property does not appear');
+
+ test(function(){
+ var style = newElm().style;
+ style.cssText = "margin: 10px; margin-inline: 10px; margin-block: 10px; margin-inline-end: 10px; margin-bottom: 10px;";
+ assert_equals(style.cssText, "margin-top: 10px; margin-right: 10px; margin-left: 10px; margin-inline-start: 10px; margin-block: 10px; margin-inline-end: 10px; margin-bottom: 10px;");
+ }, 'Shorthands aren\'t serialized if there are other properties with different logical groups in between');
+
+ test(function(){
+ var style = newElm().style;
+ style.cssText = "margin-top: 10px; margin-left: 10px; margin-right: 10px; margin-bottom: 10px; margin-inline-start: 10px; margin-inline-end: 10px; margin-block-start: 10px; margin-block-end: 10px;";
+ assert_equals(style.cssText, "margin: 10px; margin-inline: 10px; margin-block: 10px;");
+ }, 'Shorthands _are_ serialized if there are no other properties with different logical groups in between');
+
+ // https://github.com/w3c/csswg-drafts/issues/1033
+ test(function() {
+ var elm = newElm();
+ var style = elm.style;
+
+ style.color = 'red';
+ style.unknown = 'unknown';
+ style.fontSize = '10pt';
+
+ assert_not_equals(getComputedStyle(elm).length, 0, "Should have a style");
+ assert_equals(getComputedStyle(elm).cssText, "", "cssText is empty");
+ }, 'cssText on computed style declaration returns the empty string');
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html
new file mode 100644
index 0000000000..92cf2c847d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-custom-properties.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>CSS Test: computed style declaration includes custom properties.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/1316">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div style="--foo:bar"></div>
+<div></div>
+<script>
+ test(function() {
+ let withCustom = getComputedStyle(document.querySelector("div"));
+ let withoutCustom = getComputedStyle(document.querySelector("div + div"));
+ assert_equals(withCustom.getPropertyValue("--foo"), "bar", "Should be returned from getPropertyValue");
+ assert_equals(withCustom.length, withoutCustom.length + 1, "Should show up in .length");
+ assert_equals(withCustom[withCustom.length - 1], "--foo", "Should be after all the non-custom properties");
+ }, "Custom properties are included in computed style");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html
new file mode 100644
index 0000000000..ce5b598ca6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutability.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM: CSSStyleDeclaration is mutable and immutable in various settings</title>
+ <link rel="author" title="Paul Irish" href="mailto:paul.irish@gmail.com">
+ <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 -->
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstyledeclaration-interface">
+
+ <meta name="flags" content="dom">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+
+ <body>
+ <div id="log"></div>
+ <div id="box"></box>
+ <div id="box2"></box>
+ <style id="teststyles">
+ #box2 { width: 15px; }
+ </style>
+ <script>
+
+ test(function(){
+ var elem = document.getElementById('box');
+
+ elem.style.width = '10px';
+ assert_equals(elem.style.width, '10px', 'setting via style property');
+ elem.style.width = '';
+
+ elem.style.cssText = 'width: 10px';
+ assert_equals(elem.style.width, '10px', 'setting via cssText');
+ elem.style.width = '';
+
+ }, 'HTMLElement\'s CSSStyleDeclaration is mutable')
+
+
+ test(function(){
+ var elem = document.getElementById('box');
+ var style = getComputedStyle(elem);
+
+ assert_throws_dom('NO_MODIFICATION_ALLOWED_ERR', function(){
+ style.width = '10px';
+ });
+
+ }, 'getComputedStyle\'s CSSStyleDeclaration is not mutable')
+
+
+ test(function(){
+
+ var style = document.getElementById('teststyles').sheet.cssRules[0].style;
+
+ assert_equals(style.width, '15px', 'width value is correct');
+
+ style.width = '25px';
+
+ assert_equals(style.width, '25px', 'width value is mutable');
+
+ var gCSstyle = getComputedStyle(document.getElementById('box2'));
+
+ assert_equals(gCSstyle.width, '25px', 'styleSheet change is live and accesible via getComputedStyle');
+
+ }, 'StyleSheet\'s CSSStyleDeclaration is mutable');
+
+
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html
new file mode 100644
index 0000000000..ba4d926b15
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-001.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: CSSStyleDeclaration.setPropertyValue queues a mutation record when changed</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue">
+<link rel="help" href="https://drafts.csswg.org/cssom/#update-style-attribute-for">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ document.documentElement.style.top = "0px";
+
+ let test = async_test("CSSStyleDeclaration.setPropertyValue queues a mutation record when serialization is changed");
+ let m = new MutationObserver(function(r) {
+ assert_equals(r.length, 1);
+ test.done();
+ });
+
+ m.observe(document.documentElement, { attributes: true });
+ document.documentElement.style.top = "1px";
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html
new file mode 100644
index 0000000000..38ce249a31
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-002.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record for invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ let test = async_test("CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record when setting invalid values");
+ let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record"));
+ m.observe(document.documentElement, { attributes: true });
+
+ document.documentElement.style.width = "-100px";
+ requestAnimationFrame(() => test.done());
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html
new file mode 100644
index 0000000000..7a99dfc645
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-003.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed, invoked from setPropertyValue</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ let test = async_test("CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed, invoked from setPropertyValue");
+ document.documentElement.style.top = "0";
+ let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record"));
+ m.observe(document.documentElement, { attributes: true });
+
+ document.documentElement.style.width = "";
+ requestAnimationFrame(() => test.done());
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html
new file mode 100644
index 0000000000..55956dfdaa
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-004.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ let test = async_test("CSSStyleDeclaration.removeProperty doesn't queue a mutation record when not actually removed");
+ document.documentElement.style.top = "0";
+ let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record"));
+ m.observe(document.documentElement, { attributes: true });
+
+ document.documentElement.style.removeProperty("width");
+ requestAnimationFrame(() => test.done());
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html
new file mode 100644
index 0000000000..26574033ce
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-mutationrecord-005.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html style="color: inherit">
+<meta charset="utf-8">
+<title>CSSOM: CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record for invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ let test = async_test("CSSStyleDeclaration.setPropertyValue doesn't queue a mutation record when setting invalid values");
+ let m = new MutationObserver(test.unreached_func("shouldn't queue a mutation record"));
+ m.observe(document.documentElement, { attributes: true });
+
+ document.documentElement.style.width = "-100px";
+ requestAnimationFrame(() => test.done());
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html
new file mode 100644
index 0000000000..12382cbcdc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-properties.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>CSS Test: CSSStyleDeclaration properties are defined as WebIDL attributes, not using getOwnPropertyNames()</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(function() {
+ let declaration = document.documentElement.style;
+ assert_true(declaration instanceof CSSStyleDeclaration, "Should be a CSStyleDeclaration");
+ assert_true("color" in declaration, "Should support the color property");
+ assert_false(declaration.hasOwnProperty("color"), "shouldn't have an own property for WebIDL attributes");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html
new file mode 100644
index 0000000000..5aa4ad2532
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-registered-custom-properties.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Computed CSSStyleDeclaration includes registered custom properties</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/1316">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @property --non-inherited-length {
+ syntax: "<length>";
+ inherits: false;
+ initial-value: 0px;
+ }
+ @property --inherited-length {
+ syntax: "<length>";
+ inherits: true;
+ initial-value: 0px;
+ }
+ @property --universal-with-initial {
+ syntax: "*";
+ inherits: false;
+ initial-value: foo;
+ }
+ @property --universal-without-initial {
+ syntax: "*";
+ inherits: false;
+ }
+ #outer { --non-registered-outer: 1px; }
+ #inner { --non-registered-inner: 2px; }
+ #sibling { --universal-without-initial: bar; }
+</style>
+<div id=outer>
+ <div id=inner></div>
+ <div id=sibling></div>
+</div>
+<script>
+ const assert_present = (props, name) => assert_not_equals(props.indexOf(name), -1);
+ const assert_absent = (props, name) => assert_equals(props.indexOf(name), -1);
+
+ test(function() {
+ let props = Array.from(getComputedStyle(inner));
+ assert_present(props, '--non-inherited-length');
+ assert_present(props, '--inherited-length');
+ assert_present(props, '--non-registered-outer');
+ assert_present(props, '--non-registered-inner');
+ assert_present(props, '--universal-with-initial');
+ assert_absent(props, '--universal-without-initial');
+ }, 'Registered custom properties are included in CSSComputedStyleDeclaration');
+
+ test(function() {
+ let props = Array.from(getComputedStyle(sibling));
+ assert_present(props, '--non-inherited-length');
+ assert_present(props, '--inherited-length');
+ assert_present(props, '--non-registered-outer');
+ assert_present(props, '--universal-with-initial');
+ assert_present(props, '--universal-without-initial');
+ assert_absent(props, '--non-registered-inner');
+ }, 'Only relevant custom properties are included');
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html
new file mode 100644
index 0000000000..20837052e3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-attr.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<title>CSSOM test: declaration block after setting via CSSOM</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script>
+test(function() {
+ let element = document.createElement("div");
+ element.style.setProperty("doesntexist", "0");
+ assert_false(element.hasAttribute("style"));
+}, "Setting an invalid property via the declaration setter doesn't create a declaration");
+test(function() {
+ let element = document.createElement("div");
+ element.style.setProperty("width", "-100");
+ assert_false(element.hasAttribute("style"));
+}, "Setting an invalid value via the declaration setter doesn't create a declaration");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html
new file mode 100644
index 0000000000..e66466e7a1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-declarations.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<title>CSSOM test: declaration block after setting via CSSOM</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ function generateCSSDeclBlock(props) {
+ let elem = document.createElement("div");
+ let cssText = props.map(({name, value, priority}) => {
+ let longhand = `${name}: ${value}`;
+ if (priority) {
+ longhand += "!" + priority;
+ }
+ return longhand + ";";
+ }).join(" ");
+ elem.setAttribute("style", cssText);
+ return elem.style;
+ }
+ function compareByName(a, b) {
+ if (a.name < b.name) return -1;
+ if (a.name > b.name) return 1;
+ return 0;
+ }
+ function checkDeclarationsAnyOrder(block, props, msg) {
+ let actual = [];
+ for (let name of block) {
+ let value = block.getPropertyValue(name);
+ let priority = block.getPropertyPriority(name);
+ actual.push({name, value, priority});
+ }
+ actual.sort(compareByName);
+ let expected = Array.from(props);
+ expected.sort(compareByName);
+ assert_object_equals(actual, expected, "Declaration block content should match " + msg);
+ }
+ function longhand(name, value, priority="") {
+ return {name, value, priority};
+ }
+ function* shorthand(name, value, priority="") {
+ for (let subprop of SUBPROPS[name]) {
+ yield longhand(subprop, value, priority);
+ }
+ }
+
+ const SUBPROPS = {
+ "margin": ["margin-top", "margin-right", "margin-bottom", "margin-left"],
+ "padding": ["padding-top", "padding-right", "padding-bottom", "padding-left"],
+ };
+
+ test(function() {
+ let expectedDecls = [
+ longhand("top", "1px"),
+ longhand("bottom", "2px"),
+ longhand("left", "3px", "important"),
+ longhand("right", "4px"),
+ ];
+ let block = generateCSSDeclBlock(expectedDecls);
+ checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+ block.setProperty("top", "5px", "important");
+ expectedDecls[0] = longhand("top", "5px", "important");
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property");
+
+ block.setProperty("bottom", "2px");
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with identical value");
+
+ block.setProperty("left", "3px");
+ expectedDecls[2].priority = "";
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with different priority");
+
+ block.setProperty("float", "none");
+ expectedDecls.push(longhand("float", "none"));
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting non-existing property");
+ }, "setProperty with longhand should update only the declaration being set");
+
+ test(function() {
+ let expectedDecls = [
+ longhand("top", "1px"),
+ longhand("bottom", "2px"),
+ longhand("left", "3px", "important"),
+ longhand("right", "4px"),
+ ];
+ let block = generateCSSDeclBlock(expectedDecls);
+ checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+ block.top = "5px";
+ expectedDecls[0] = longhand("top", "5px");
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property");
+
+ block.bottom = "2px";
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with identical value");
+
+ block.left = "3px";
+ expectedDecls[2].priority = "";
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with different priority");
+
+ block.float = "none";
+ expectedDecls.push(longhand("float", "none"));
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting non-existing property");
+ }, "property setter should update only the declaration being set");
+
+ test(function() {
+ let expectedDecls = [
+ ...shorthand("margin", "1px"),
+ longhand("top", "2px"),
+ ...shorthand("padding", "3px", "important"),
+ ];
+ let block = generateCSSDeclBlock(expectedDecls);
+ checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+ block.setProperty("margin", "4px");
+ for (let i = 0; i < 4; i++) {
+ expectedDecls[i].value = "4px";
+ }
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand");
+
+ block.setProperty("margin", "4px");
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with identical value");
+
+ block.setProperty("padding", "3px");
+ for (let i = 5; i < 9; i++) {
+ expectedDecls[i].priority = "";
+ }
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with different priority");
+
+ block.setProperty("margin-bottom", "5px", "important");
+ expectedDecls[2] = longhand("margin-bottom", "5px", "important");
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting a longhand in an existing shorthand");
+ }, "setProperty with shorthand should update only the declarations being set");
+
+ test(function() {
+ let expectedDecls = [
+ ...shorthand("margin", "1px"),
+ longhand("top", "2px"),
+ ...shorthand("padding", "3px", "important"),
+ ];
+ let block = generateCSSDeclBlock(expectedDecls);
+ checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+ block.margin = "4px";
+ for (let i = 0; i < 4; i++) {
+ expectedDecls[i].value = "4px";
+ }
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand");
+
+ block.margin = "4px";
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with identical value");
+
+ block.padding = "3px";
+ for (let i = 5; i < 9; i++) {
+ expectedDecls[i].priority = "";
+ }
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with different priority");
+
+ block.marginBottom = "5px";
+ expectedDecls[2] = longhand("margin-bottom", "5px");
+ checkDeclarationsAnyOrder(block, expectedDecls, "after setting a longhand in an existing shorthand");
+ }, "longhand property setter should update only the decoarations being set");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html
new file mode 100644
index 0000000000..ae556ed1cb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-form-controls.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<title>CSSOM test: no side effects from setting "height"</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=107380">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<p>
+Historically, the Apple Safari web browser has added an "intrinsic margin" to
+form controls which do not specify a `height`. It removed this margin following
+modification of the `height`. <a
+href="https://bugs.webkit.org/show_bug.cgi?id=107380">This non-standard
+behavior was identified as a source of confusion for web developers.</a>
+</p>
+
+<script>
+function makeElement(tagName) {
+ var element = document.createElement(tagName);
+ document.body.appendChild(element);
+ return element;
+}
+function makeInputElement(type) {
+ var element = makeElement('input');
+ element.setAttribute('type', type);
+ return element;
+}
+function measure(element) {
+ var computed = getComputedStyle(element);
+ return [computed.marginTop, computed.marginBottom];
+}
+
+test(function() {
+ var element = makeInputElement('text');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'text input element');
+
+test(function() {
+ var element = makeInputElement('button');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'button input element');
+
+test(function() {
+ var element = makeInputElement('submit');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'submit input element');
+
+test(function() {
+ var element = makeInputElement('radio');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'radio input element');
+
+test(function() {
+ var element = makeInputElement('checkbox');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'checkbox input element');
+
+test(function() {
+ var element = makeElement('textarea');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'textarea element');
+
+test(function() {
+ var element = makeElement('select');
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'select element');
+
+test(function() {
+ var element = makeElement('button')
+ var initial = measure(element);
+
+ element.style.setProperty('height', '12px');
+
+ assert_array_equals(measure(element), initial);
+}, 'button element');
+</script>
diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html
new file mode 100644
index 0000000000..13d68e9a70
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-setter-logical.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<title>CSSOM test: declaration block after setting via CSSOM</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test"></div>
+<script>
+ test(function() {
+ const PHYSICAL_PROPS = ["padding-top", "padding-right",
+ "padding-bottom", "padding-left"];
+ const LOGICAL_PROPS = ["padding-block-start", "padding-block-end",
+ "padding-inline-start", "padding-inline-end"];
+ let elem = document.getElementById("test");
+ let decls = new Map;
+
+ {
+ let css = []
+ let i = 0;
+ for (let name of [...PHYSICAL_PROPS, ...LOGICAL_PROPS]) {
+ let value = `${i++}px`;
+ css.push(`${name}: ${value};`);
+ decls.set(name, value);
+ }
+ elem.setAttribute("style", css.join(" "));
+ }
+
+ let style = elem.style;
+ function indexOfProperty(prop) {
+ return Array.prototype.indexOf.apply(style, [prop]);
+ }
+ function assertPropAfterProps(prop, props, msg) {
+ let index = indexOfProperty(prop);
+ for (let p of props) {
+ assert_less_than(indexOfProperty(p), index, `${prop} should be after ${p} ${msg}`);
+ }
+ }
+ // We are not changing any value in this test, just order.
+ function assertValueUnchanged() {
+ for (let [name, value] of decls.entries()) {
+ assert_equals(style.getPropertyValue(name), value,
+ `value of ${name} shouldn't be changed`);
+ }
+ }
+
+ assertValueUnchanged();
+ // Check that logical properties are all after physical properties
+ // at the beginning.
+ for (let p of LOGICAL_PROPS) {
+ assertPropAfterProps(p, PHYSICAL_PROPS, "initially");
+ }
+
+ // Try setting a longhand.
+ style.setProperty("padding-top", "0px");
+ assertValueUnchanged();
+ // Check that padding-top is after logical properties, but other
+ // physical properties are still before logical properties.
+ assertPropAfterProps("padding-top", LOGICAL_PROPS, "after setting padding-top");
+ for (let p of LOGICAL_PROPS) {
+ assertPropAfterProps(p, PHYSICAL_PROPS.filter(prop => prop != "padding-top"),
+ "after setting padding-top");
+ }
+
+ // Try setting a shorthand.
+ style.setProperty("padding", "0px 1px 2px 3px");
+ assertValueUnchanged();
+ // Check that all physical properties are now after logical properties.
+ for (let p of PHYSICAL_PROPS) {
+ assertPropAfterProps(p, LOGICAL_PROPS, "after setting padding shorthand");
+ }
+ }, "newly set declaration should be after all declarations " +
+ "in the same logical property group but have different logical kind");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/declaration-block-all-crash.html b/testing/web-platform/tests/css/cssom/declaration-block-all-crash.html
new file mode 100644
index 0000000000..2d781e5dcf
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/declaration-block-all-crash.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1711189">
+<div></div>
+<script>
+ let div = document.querySelector("div");
+ let cs = getComputedStyle(div);
+ for (let i = 0; i < cs.length; ++i)
+ div.style.setProperty(cs[i], cs.getPropertyValue(cs[i]));
+ div.style.cssText
+</script>
diff --git a/testing/web-platform/tests/css/cssom/delete-namespace-rule-when-child-rule-exists.html b/testing/web-platform/tests/css/cssom/delete-namespace-rule-when-child-rule-exists.html
new file mode 100644
index 0000000000..5118b67cd1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/delete-namespace-rule-when-child-rule-exists.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>Deleting a @namespace rule when list contains anything other than @import or @namespace rules should throw InvalidStateError.</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#remove-a-css-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @namespace a url();
+</style>
+<script>
+ test(function () {
+ let styleSheet = document.styleSheets[0];
+ styleSheet.cssRules[0];
+ styleSheet.insertRule(`b {}`, 1);
+ assert_throws_dom("InvalidStateError", () => styleSheet.deleteRule(0));
+ }, "Deleting a @namespace rule when list contains anything other than @import or @namespace rules should throw InvalidStateError.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/escape.html b/testing/web-platform/tests/css/cssom/escape.html
new file mode 100644
index 0000000000..895da59c49
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/escape.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS#escape</title>
+<link rel=help href=https://drafts.csswg.org/cssom-1/#the-css.escape()-method>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ assert_equals(CSS.escape.length, 1);
+ assert_throws_js(TypeError, function() { CSS.escape(); });
+}, "Number of arguments");
+
+test(function() {
+ assert_equals(CSS.escape(true), 'true');
+ assert_equals(CSS.escape(false), 'false');
+ assert_equals(CSS.escape(null), 'null');
+ assert_equals(CSS.escape(''), '');
+}, "String conversion");
+
+test(function() {
+ assert_equals(CSS.escape('\0'), '\uFFFD');
+ assert_equals(CSS.escape('a\0'), 'a\uFFFD');
+ assert_equals(CSS.escape('\0b'), '\uFFFDb');
+ assert_equals(CSS.escape('a\0b'), 'a\uFFFDb');
+}, "Null bytes");
+
+test(function() {
+ assert_equals(CSS.escape('\uFFFD'), '\uFFFD');
+ assert_equals(CSS.escape('a\uFFFD'), 'a\uFFFD');
+ assert_equals(CSS.escape('\uFFFDb'), '\uFFFDb');
+ assert_equals(CSS.escape('a\uFFFDb'), 'a\uFFFDb');
+}, "Replacement character");
+
+test(function() {
+ assert_equals(CSS.escape('0a'), '\\30 a');
+ assert_equals(CSS.escape('1a'), '\\31 a');
+ assert_equals(CSS.escape('2a'), '\\32 a');
+ assert_equals(CSS.escape('3a'), '\\33 a');
+ assert_equals(CSS.escape('4a'), '\\34 a');
+ assert_equals(CSS.escape('5a'), '\\35 a');
+ assert_equals(CSS.escape('6a'), '\\36 a');
+ assert_equals(CSS.escape('7a'), '\\37 a');
+ assert_equals(CSS.escape('8a'), '\\38 a');
+ assert_equals(CSS.escape('9a'), '\\39 a');
+}, "Number prefix");
+
+test(function() {
+ assert_equals(CSS.escape('a0b'), 'a0b');
+ assert_equals(CSS.escape('a1b'), 'a1b');
+ assert_equals(CSS.escape('a2b'), 'a2b');
+ assert_equals(CSS.escape('a3b'), 'a3b');
+ assert_equals(CSS.escape('a4b'), 'a4b');
+ assert_equals(CSS.escape('a5b'), 'a5b');
+ assert_equals(CSS.escape('a6b'), 'a6b');
+ assert_equals(CSS.escape('a7b'), 'a7b');
+ assert_equals(CSS.escape('a8b'), 'a8b');
+ assert_equals(CSS.escape('a9b'), 'a9b');
+}, "Letter number prefix");
+
+test(function() {
+ assert_equals(CSS.escape('-0a'), '-\\30 a');
+ assert_equals(CSS.escape('-1a'), '-\\31 a');
+ assert_equals(CSS.escape('-2a'), '-\\32 a');
+ assert_equals(CSS.escape('-3a'), '-\\33 a');
+ assert_equals(CSS.escape('-4a'), '-\\34 a');
+ assert_equals(CSS.escape('-5a'), '-\\35 a');
+ assert_equals(CSS.escape('-6a'), '-\\36 a');
+ assert_equals(CSS.escape('-7a'), '-\\37 a');
+ assert_equals(CSS.escape('-8a'), '-\\38 a');
+ assert_equals(CSS.escape('-9a'), '-\\39 a');
+}, "Dash number prefix");
+
+test(function() {
+ assert_equals(CSS.escape('--a'), '--a');
+}, "Double dash prefix");
+
+test(function() {
+ assert_equals(CSS.escape('\x01\x02\x1E\x1F'), '\\1 \\2 \\1e \\1f ');
+
+ assert_equals(CSS.escape('\x80\x2D\x5F\xA9'), '\x80\x2D\x5F\xA9');
+ assert_equals(CSS.escape('\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F'), '\\7f \x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F');
+ assert_equals(CSS.escape('\xA0\xA1\xA2'), '\xA0\xA1\xA2');
+ assert_equals(CSS.escape('a0123456789b'), 'a0123456789b');
+ assert_equals(CSS.escape('abcdefghijklmnopqrstuvwxyz'), 'abcdefghijklmnopqrstuvwxyz');
+ assert_equals(CSS.escape('ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
+
+ assert_equals(CSS.escape("hello\\world"), "hello\\\\world", "Backslashes get backslash-escaped");
+ assert_equals(CSS.escape("hello\u{1234}world"), "hello\u{1234}world", "Code points greater than U+0080 are preserved");
+ assert_equals(CSS.escape("-"), "\\-", "CSS.escape: Single dash escaped");
+
+ assert_equals(CSS.escape('\x20\x21\x78\x79'), '\\ \\!xy');
+}, "Various tests");
+
+test(function() {
+ // astral symbol (U+1D306 TETRAGRAM FOR CENTRE)
+ assert_equals(CSS.escape('\uD834\uDF06'), '\uD834\uDF06');
+ // lone surrogates
+ assert_equals(CSS.escape('\uDF06'), '\uDF06');
+ assert_equals(CSS.escape('\uD834'), '\uD834');
+}, "Surrogates");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/flex-serialization.html b/testing/web-platform/tests/css/cssom/flex-serialization.html
new file mode 100644
index 0000000000..350877c3ce
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/flex-serialization.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - Flex serialization</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-value">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ div { flex: initial; }
+ div { flex: 0; }
+ div { flex: initial; flex-basis: initial; flex-shrink: initial; }
+ div { flex: initial; flex-shrink: 0; }
+ div { flex: initial; flex-basis: 0; flex-shrink: 2;}
+ </style>
+
+ <script>
+ var styleSheet = document.styleSheets[0]
+ test(function () {
+ assert_equals(styleSheet.cssRules[0].style.cssText, "flex: initial;")
+ }, "Single value flex with CSS-wide keyword should serialize correctly.")
+ test(function () {
+ assert_in_array(styleSheet.cssRules[1].style.cssText, ["flex: 0px;", "flex: 0 1 0px;"])
+ }, "Single value flex with non-CSS-wide value should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[2].style.cssText, "flex: initial;")
+ }, "Multiple values flex with CSS-wide keyword should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[3].style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;")
+ }, "Multiple values flex with CSS-wide keywords and non-CSS-wide value should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[4].style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;")
+ }, "Multiple values flex with CSS-wide and two non-CSS-wide-keyword values should serialize correctly.")
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/font-family-serialization-001.html b/testing/web-platform/tests/css/cssom/font-family-serialization-001.html
new file mode 100644
index 0000000000..436ce7f5c1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/font-family-serialization-001.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<title>Serialization of font-family</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block">
+<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-family-prop">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-fonts/support/font-family-keywords.js"></script>
+<div id="target"></div>
+<script>
+ function SetFontFamilyAndSerialize(fontFamilyValue) {
+ var target = document.getElementById('target');
+ target.setAttribute("style", `font-family: ${fontFamilyValue}`);
+ return window.getComputedStyle(target).getPropertyValue('font-family');
+ }
+ test(function() {
+ kGenericFontFamilyKeywords.forEach(keyword => {
+ assert_equals(SetFontFamilyAndSerialize(keyword), keyword);
+ });
+ }, "Serialization of <generic-family>");
+
+ test(function() {
+ kGenericFontFamilyKeywords.forEach(keyword => {
+ var quoted_keyword = `"${keyword}"`;
+ assert_equals(SetFontFamilyAndSerialize(quoted_keyword), quoted_keyword);
+ });
+ }, "Serialization of quoted \"<generic-family>\"");
+
+ test(function() {
+ kGenericFontFamilyKeywords.forEach(keyword => {
+ var prefixed_keyword = `-webkit-${keyword}`;
+ assert_equals(SetFontFamilyAndSerialize(prefixed_keyword), prefixed_keyword);
+ });
+ }, "Serialization of prefixed -webkit-<generic-family>");
+
+ test(function() {
+ kNonGenericFontFamilyKeywords.forEach(keyword => {
+ assert_equals(SetFontFamilyAndSerialize(keyword), keyword);
+ });
+ }, `Serialization of ${kNonGenericFontFamilyKeywords}`);
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/font-shorthand-serialization.html b/testing/web-platform/tests/css/cssom/font-shorthand-serialization.html
new file mode 100644
index 0000000000..29082f83ea
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/font-shorthand-serialization.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Serialization of font shorthand</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<div id="target" style="font: 10px/1 Ahem;"></div>
+<script>
+ test(function() {
+ var target = document.getElementById('target');
+ assert_equals(target.style.cssText, 'font: 10px / 1 Ahem;');
+ assert_equals(target.style.font, '10px / 1 Ahem');
+ }, "The font shorthand should be serialized just like any other shorthand.");
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html b/testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html
new file mode 100644
index 0000000000..0e4651cdc0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/font-variant-shorthand-serialization.html
@@ -0,0 +1,131 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Serialization of font-variant shorthand</title>
+<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#serialize-a-css-declaration-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<div id="target"></div>
+<script>
+ const cssWideKeywords = ["initial", "inherit", "unset", "revert", "revert-layer"];
+ function test_serialization_set(expected) {
+ for (const [property, value] of Object.entries(expected)) {
+ if (!CSS.supports(`${property}: initial`))
+ continue;
+ assert_equals(target.style[property], value, `${property} was set`);
+ }
+ }
+ function setWithValue(value) {
+ return {
+ "font-variant-ligatures": value,
+ "font-variant-caps": value,
+ "font-variant-alternates": value,
+ "font-variant-numeric": value,
+ "font-variant-east-asian": value,
+ "font-variant-position": value,
+ "font-variant-emoji": value,
+ "font-variant": value
+ };
+ }
+ const emptySet = setWithValue("");
+ const normalSet = setWithValue("normal");
+ const nonDefaultValues = {
+ "font-variant-ligatures": "common-ligatures discretionary-ligatures",
+ "font-variant-caps": "small-caps",
+ "font-variant-alternates": "historical-forms",
+ "font-variant-numeric": "oldstyle-nums stacked-fractions",
+ "font-variant-east-asian": "ruby",
+ "font-variant-position": "sub",
+ "font-variant-emoji": "emoji",
+ };
+ test(function(t) {
+ target.style.fontVariant = "normal";
+ t.add_cleanup(() => target.removeAttribute("style"));
+
+ test_serialization_set(normalSet);
+ }, "font-variant: normal serialization");
+
+ test(function(t) {
+ target.style.fontVariant = "normal";
+ target.style.fontVariantLigatures = "none";
+ t.add_cleanup(() => target.removeAttribute("style"));
+
+ const expected = Object.assign({}, normalSet);
+ expected["font-variant-ligatures"] = "none";
+ expected["font-variant"] = "none";
+
+ test_serialization_set(expected);
+ }, "font-variant: none serialization");
+
+ test(function(t) {
+ t.add_cleanup(() => target.removeAttribute("style"));
+ for (const [property, value] of Object.entries(nonDefaultValues)) {
+ if (property == "font-variant-ligatures" || !CSS.supports(`${property}: initial`))
+ continue;
+ target.style.fontVariant = "normal";
+ target.style.fontVariantLigatures = "none";
+ target.style[property] = value;
+
+ const expected = Object.assign({}, normalSet);
+ expected["font-variant-ligatures"] = "none";
+ expected["font-variant"] = "";
+ expected[property] = value;
+
+ test_serialization_set(expected);
+ target.removeAttribute("style");
+ }
+ }, "font-variant-ligatures: none serialization with non-default value for another longhand");
+
+ test(function(t) {
+ t.add_cleanup(() => target.removeAttribute("style"));
+
+ for (const [property, value] of Object.entries(nonDefaultValues)) {
+ if (!CSS.supports(`${property}: initial`))
+ continue;
+ target.style.fontVariant = "normal";
+ target.style[property] = value;
+
+ const expected = Object.assign({}, normalSet);
+ expected[property] = value;
+ expected["font-variant"] = value;
+ test_serialization_set(expected);
+
+ target.removeAttribute("style");
+ }
+ }, "font-variant: normal with non-default longhands");
+
+ test(function(t) {
+ t.add_cleanup(() => target.removeAttribute("style"));
+ for (const keyword of cssWideKeywords) {
+ target.style.fontVariant = "normal";
+ target.style.fontVariantLigatures = keyword;
+
+ const expected = Object.assign({}, normalSet);
+ expected["font-variant-ligatures"] = keyword;
+ expected["font-variant"] = "";
+ test_serialization_set(expected);
+ target.removeAttribute("style");
+ }
+ }, "CSS-wide keyword in one longhand");
+
+ test(function(t) {
+ t.add_cleanup(() => target.removeAttribute("style"));
+
+ for (const keyword of cssWideKeywords) {
+ target.style.fontVariant = keyword;
+ test_serialization_set(setWithValue(keyword));
+ target.removeAttribute("style");
+ }
+ }, "CSS-wide keyword in shorthand");
+
+ test(function(t) {
+ target.style.fontVariant = "normal";
+ target.style.font = "menu";
+ t.add_cleanup(() => target.removeAttribute("style"));
+
+ test_serialization_set(emptySet);
+ }, "font: menu serialization");
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html b/testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html
new file mode 100644
index 0000000000..47198803a0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-animations-replaced-into-ib-split.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>getComputedStyle() returns the right style for animating nodes that have been just inserted into the document, and that have an ancestor whose layout tree was recreated (like an IB-split)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1585882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ @keyframes my-animation {
+ from { color: green; }
+ to { color: green; }
+ }
+ div {
+ color: red;
+ animation: my-animation 1s infinite linear paused;
+ }
+</style>
+<span>
+ <div></div>
+</span>
+<script>
+test(() => {
+ let oldDiv = document.querySelector("div");
+ window.unused = oldDiv.getBoundingClientRect(); // update layout
+
+ assert_equals(getComputedStyle(oldDiv).color, "rgb(0, 128, 0)", "Should take color from the animation");
+
+ let newDiv = document.createElement("div");
+ oldDiv.replaceWith(newDiv);
+
+ assert_equals(getComputedStyle(newDiv).color, "rgb(0, 128, 0)", "Should take color from the animation (just inserted into the document)");
+}, "getComputedStyle() should return animation styles for nodes just inserted into the document, even if they're in an IB-split");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html b/testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html
new file mode 100644
index 0000000000..211d512b28
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-detached-subtree.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle returns no style for elements not in the tree</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="host">
+ <div id="non-slotted">
+ <div id="non-slotted-descendant"></div>
+ </div>
+</div>
+<iframe srcdoc="<html></html>" style="display: none"></iframe>
+<script>
+function testNoComputedStyle(element, description, global) {
+ test(function() {
+ assert_true(!!element);
+ let style = (global ? global : window).getComputedStyle(element);
+ assert_true(!!style);
+ // Note that we avoid assert_equals below to get a stable failure message.
+ assert_true(style.length == 0);
+ assert_equals(style.color, "");
+ }, `getComputedStyle returns no style for ${description}`);
+}
+
+let detached = document.createElement('div');
+testNoComputedStyle(detached, "detached element");
+
+testNoComputedStyle(document.querySelector('iframe').contentDocument.documentElement,
+ "element in non-rendered iframe (display: none)");
+
+testNoComputedStyle(document.querySelector('iframe').contentDocument.documentElement,
+ "element in non-rendered iframe (display: none) from iframe's window",
+ document.querySelector('iframe').contentWindow);
+
+host.attachShadow({ mode: "open" });
+testNoComputedStyle(document.getElementById('non-slotted'),
+ "element outside the flat tree");
+
+testNoComputedStyle(document.getElementById('non-slotted-descendant'),
+ "descendant outside the flat tree");
+
+let shadowRoot = detached.attachShadow({ mode: "open" });
+shadowRoot.innerHTML = `
+ <div id="detached-shadow-tree-descendant"></div>
+`;
+testNoComputedStyle(shadowRoot.getElementById('detached-shadow-tree-descendant'),
+ "shadow tree outside of flattened tree");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html
new file mode 100644
index 0000000000..5a356f15a4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle gets invalidated for display: none elements (inheritance)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=186882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#undisplayed, #host {
+ display: none;
+ color: red;
+}
+</style>
+<div id="undisplayed"><div id="child"></div></div>
+<div id="host"></div>
+<script>
+ test(function() {
+ let undisplayed_style = getComputedStyle(undisplayed);
+ let undisplayed_child_style = getComputedStyle(child);
+ assert_equals(undisplayed_style.color, "rgb(255, 0, 0)");
+ assert_equals(undisplayed_child_style.color, "rgb(255, 0, 0)");
+ undisplayed.style.color = "green";
+ assert_equals(undisplayed_style.color, "rgb(0, 128, 0)");
+ assert_equals(undisplayed_child_style.color, "rgb(0, 128, 0)");
+ }, "getComputedStyle gets invalidated in display: none subtrees due to inherited changes to an ancestor");
+ test(function() {
+ host.attachShadow({ mode: 'open' }).innerHTML = `
+ <div></div>
+ `;
+ let host_style = getComputedStyle(host);
+ let shadow_style = getComputedStyle(host.shadowRoot.firstElementChild);
+ assert_equals(host_style.color, "rgb(255, 0, 0)");
+ assert_equals(shadow_style.color, "rgb(255, 0, 0)");
+ host.style.color = "green";
+ assert_equals(host_style.color, "rgb(0, 128, 0)");
+ assert_equals(shadow_style.color, "rgb(0, 128, 0)");
+ }, "getComputedStyle gets invalidated in display: none subtrees due to inherited changes to an ancestor shadow host");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html
new file mode 100644
index 0000000000..12fb7fc3f8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle gets invalidated for display: none elements (rules)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=186882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#undisplayed {
+ display: none;
+ color: red;
+}
+.sibling + #undisplayed {
+ color: green;
+}
+
+.sibling + #undisplayed > div {
+ color: yellow;
+}
+</style>
+<div id="helper"></div>
+<div id="undisplayed"><div id="child"></div></div>
+<script>
+ test(function() {
+ let undisplayed_style = getComputedStyle(undisplayed);
+ let undisplayed_child_style = getComputedStyle(child);
+ assert_equals(undisplayed_style.color, "rgb(255, 0, 0)");
+ assert_equals(undisplayed_child_style.color, "rgb(255, 0, 0)");
+ helper.classList.add("sibling");
+ assert_equals(undisplayed_style.color, "rgb(0, 128, 0)");
+ assert_equals(undisplayed_child_style.color, "rgb(255, 255, 0)");
+ }, "getComputedStyle gets invalidated in display: none subtrees due to rule matching changes");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html
new file mode 100644
index 0000000000..7ea4505588
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-003.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>style is invalidated properly as a result of attribute changes in display: none subtrees</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+div {
+ color: red
+}
+.foo {
+ color: lime;
+}
+</style>
+<div style="display: none">
+ <div id="undisplayed"></div>
+</div>
+<script>
+ test(function() {
+ let undisplayed_style = getComputedStyle(undisplayed);
+ assert_equals(undisplayed_style.color, "rgb(255, 0, 0)");
+ undisplayed.classList.add("foo");
+ assert_equals(undisplayed_style.color, "rgb(0, 255, 0)");
+ }, "getComputedStyle gets invalidated in display: none subtrees due to attribute mutations");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html b/testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html
new file mode 100644
index 0000000000..aa49dc33ef
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-dynamic-subdoc.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle cross-doc properly reflects media query changes</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe id="frm" style="width: 100px; height: 100px"></iframe>
+<script>
+test(function() {
+ let frm = document.getElementById('frm');
+ let frmDoc = frm.contentWindow.document;
+ frmDoc.open();
+ frmDoc.write('<style>body { color: red } @media all and (min-width: 101px) { body { color: green } }</style><body>Should be green</body>');
+ frmDoc.close();
+
+ document.body.offsetTop;
+
+ assert_equals(
+ getComputedStyle(frmDoc.body).color,
+ "rgb(255, 0, 0)",
+ "Initial color should be red"
+ );
+
+ frm.style.width = "200px";
+
+ assert_equals(
+ getComputedStyle(frmDoc.body).color,
+ "rgb(0, 128, 0)",
+ "style should have been updated to account for media query changes"
+ );
+}, "getComputedStyle cross-doc properly reflects media query changes");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html b/testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html
new file mode 100644
index 0000000000..e61241e570
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-getter-v-properties.tentative.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSStyleDeclaration index getter v. attributes</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2529">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="testElement"></div>
+<script>
+
+/* per spec, the indexed getter gives all supported longhand properties, whereas
+ attributes are created for all supported properties; this implies the indexed
+ getter gives a subset of the attributes */
+
+const decl = window.getComputedStyle(document.getElementById("testElement"));
+const declItems = Array.from(decl).sort();
+
+const shorthands = ['border-top', 'border-right', 'border-bottom', 'border-left', 'border', 'font'];
+const non_shorthands = ['margin-top', 'font-size', 'max-width', 'width'];
+
+for (const prop of shorthands) {
+ test(() => {
+ assert_true(prop in decl, "getComputedStyle attribute");
+ assert_false(declItems.includes(prop), "getComputedStyle indexed getter");
+ }, prop);
+}
+
+for (const prop of non_shorthands) {
+ test(() => {
+ assert_true(prop in decl, "getComputedStyle attribute");
+ assert_true(declItems.includes(prop), "getComputedStyle indexed getter");
+ }, prop);
+}
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-crash.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-crash.html
new file mode 100644
index 0000000000..e28828bab1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-crash.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<title>Chromium bug: getComputedStyle() crashes with subtree layout of out-of-flow node</title>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1458561">
+
+<style>
+.container {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ background: lime;
+}
+
+.oof {
+ position: absolute;
+ width: 30px;
+ height: 30px;
+ top: 0;
+ left: 0;
+ overflow: hidden;
+ background: hotpink;
+}
+</style>
+
+<div class="container">
+ <div id="target1" class="oof">hi</div>
+</div>
+<script>
+document.body.offsetTop;
+target1.innerText = 'boom';
+getComputedStyle(target1).top; // Shouldn't crash
+</script>
+
+<div class="container">
+ <div id="target2" class="oof"><div>hi</div></div>
+</div>
+<script>
+document.body.offsetTop;
+target2.firstChild.innerText = 'boom';
+getComputedStyle(target2).top; // Shouldn't crash
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-logical-crash.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-logical-crash.html
new file mode 100644
index 0000000000..1e3ef8ab12
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-logical-crash.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Chromium bug: getComputedStyle() crashes with logical inset properties</title>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1478578">
+
+<style>
+.target {
+ position: absolute;
+}
+</style>
+
+<div class="target" id="target1"></div>
+<script>getComputedStyle(target1).insetInlineStart</script>
+
+<div class="target" id="targetr2"></div>
+<script>getComputedStyle(target2).insetInlineEnd</script>
+
+<div class="target" id="target3"></div>
+<script>getComputedStyle(target3).insetBlockStart</script>
+
+<div class="target" id="target4"></div>
+<script>getComputedStyle(target4).insetBlockEnd</script>
+
+<div class="target" id="target5"></div>
+<script>getComputedStyle(target5).insetInline</script>
+
+<div class="target" id="target6"></div>
+<script>getComputedStyle(target6).insetBlock</script>
+
+<div class="target" id="target7"></div>
+<script>getComputedStyle(target7).inset</script>
+
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-roundtrip.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-roundtrip.html
new file mode 100644
index 0000000000..d89a22d866
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute-roundtrip.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Chromium bug: getComputedStyle() fixed-length inset values don't roundtrip</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1482703">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.target {
+ position: absolute;
+}
+</style>
+
+<div class="target" id="target1" style="left: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target1).left, '20.7px');
+}, 'Fixed-length left property value should roundtrip');
+</script>
+
+<div class="target" id="target2" style="right: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target2).right, '20.7px');
+}, 'Fixed-length right property value should roundtrip');
+</script>
+
+<div class="target" id="target3" style="top: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target3).top, '20.7px');
+}, 'Fixed-length top property value should roundtrip');
+</script>
+
+<div class="target" id="target4" style="bottom: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target4).bottom, '20.7px');
+}, 'Fixed-length bottom property value should roundtrip');
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html
new file mode 100644
index 0000000000..fae0a84a7b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-absolute.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for absolute positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForAbspos} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: absolute",
+ containingBlockElement: containerForAbspos,
+ containingBlockArea: "padding",
+ preservesPercentages: false,
+ preservesAuto: false,
+ canStretchAutoSize: true,
+ staticPositionY: 1 + 2 + 4 + 8,
+ staticPositionX: 2 + 4 + 8 + 16,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html
new file mode 100644
index 0000000000..ff80d2fc50
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-fixed.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for fixed positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForFixed} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: fixed",
+ containingBlockElement: containerForFixed,
+ containingBlockArea: "padding",
+ preservesPercentages: false,
+ preservesAuto: false,
+ canStretchAutoSize: true,
+ staticPositionY: 1 + 2 + 4 + 8 + 16 + 32 + 64,
+ staticPositionX: 2 + 4 + 8 + 16 + 32 + 64 + 128,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-grid.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-grid.html
new file mode 100644
index 0000000000..08b55733a1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-grid.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Position absolute getComputedStyle left (for display: grid)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=867616">
+<link rel="author" href="mailto:francois.remy.dev@outlook.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+
+span { display: grid; grid-template-columns: 100px 100px; }
+span { position: absolute; grid-column: 2; }
+
+</style>
+
+<span>abc<span>def</span></span>
+
+<script>
+var test_description = document.title;
+promise_test(t => {
+ return new Promise(test => addEventListener('load', e=>test()))
+ .then(test => assert_equals(
+ getComputedStyle(document.querySelector("body > span > span"))['left'], "0px",
+ "Invalid gCS($(\"body > span > span\"))['left'];"));
+}, test_description);
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-multicol-absolute-crash.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-multicol-absolute-crash.html
new file mode 100644
index 0000000000..81b0b88be1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-multicol-absolute-crash.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>Chromium bug: getComputedStyle() crashes with inset properties on abspos in multicol</title>
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1473508">
+<style>
+html {
+ column-count:2;
+}
+body {
+ transform: scale(1);
+}
+div#test {
+ column-count:2;
+ position:absolute;
+}
+</style>
+
+<div id="test">
+ <svg xmlns="http://www.w3.org/2000/svg"></svg>
+ <div style="transform: scale(1)">
+ <ruby style="position: absolute">
+ <rt>foo</rt>
+ </ruby>
+ </div>
+</div>
+
+<script>
+getComputedStyle(test).right;
+</script>
+
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html
new file mode 100644
index 0000000000..236abf17f9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-nobox.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties when the element generates no box</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "display: none",
+ containingBlockElement: null,
+ preservesPercentages: true,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html
new file mode 100644
index 0000000000..1abfc62c01
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relative.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for relative positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: relative",
+ containingBlockElement: containerForInflow,
+ containingBlockArea: "content",
+ preservesPercentages: false,
+ preservesAuto: false,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relpos-inline.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relpos-inline.html
new file mode 100644
index 0000000000..d5f197ee9b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-relpos-inline.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<title>getComputedStyle OOF inset resolved against relpos inline container</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.ifc {
+ position: relative;
+ width: max-content;
+ font: 20px/1 Ahem;
+ margin-bottom: 2em;
+}
+
+.relpos {
+ position: relative;
+ background: yellow;
+ color: yellow;
+}
+
+.target {
+ position: absolute;
+ background: green;
+ width: 5em;
+ height: 1em;
+ top: 1em;
+}
+
+.fix-start {
+ inset-inline-start: 0;
+}
+
+.fix-end {
+ inset-inline-end: 0;
+}
+</style>
+
+<div class="ifc">
+ Lorem
+ <span class="relpos">
+ ipsum dolor
+ <div class="target fix-start" id="target1"></div>
+ <div class="target fix-end" id="target2"></div>
+ </span>
+ sit amet
+</div>
+<script>
+test(() => {
+ let style = getComputedStyle(target1);
+ assert_equals(style.left, '0px');
+ assert_equals(style.right, '140px');
+}, 'OOF with left fixed right auto in relpos inline container');
+
+test(() => {
+ let style = getComputedStyle(target2);
+ assert_equals(style.left, '140px');
+ assert_equals(style.right, '0px');
+}, 'OOF with left auto right fixed in relpos inline container');
+</script>
+
+<div class="ifc">
+ Lorem
+ <span class="relpos" dir="rtl">
+ ipsum dolor
+ <div class="target fix-start" id="target3" dir="ltr"></div>
+ <div class="target fix-end" id="target4" dir="ltr"></div>
+ </span>
+ sit amet
+</div>
+<script>
+test(() => {
+ let style = getComputedStyle(target3);
+ assert_equals(style.left, '0px');
+ assert_equals(style.right, '140px');
+}, 'OOF with left fixed right auto in relpos inline container with mixed directions');
+
+test(() => {
+ let style = getComputedStyle(target4);
+ assert_equals(style.left, '140px');
+ assert_equals(style.right, '0px');
+}, 'OOF with left auto right fixed in relpos inline container with mixed directions');
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html
new file mode 100644
index 0000000000..aa7dbee956
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-static.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for static positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: static",
+ containingBlockElement: containerForInflow,
+ containingBlockArea: "content",
+ preservesPercentages: true,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html
new file mode 100644
index 0000000000..7f5ec30648
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky-container-for-abspos.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for sticky positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#sticky-pos">
+<link rel="author" title="Joonghun Park" href="mailto:pjh0718@gmail.com">
+<style>
+ #container-for-abspos {
+ height: 200px;
+ width: 400px;
+ overflow: hidden;
+ }
+</style>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForAbspos} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: sticky;",
+ containingBlockElement: containerForAbspos,
+ containingBlockArea: "content",
+ preservesPercentages: false,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html
new file mode 100644
index 0000000000..6b23fabcb1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-insets-sticky.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the inset properties for sticky positioning</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://drafts.csswg.org/css-position/#pos-sch">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<style>
+ #container-for-inflow { overflow: hidden; }
+</style>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script type="module">
+import {runTests, containerForInflow} from "./support/getComputedStyle-insets.js";
+runTests({
+ style: "position: sticky;",
+ containingBlockElement: containerForInflow,
+ containingBlockArea: "content",
+ preservesPercentages: false,
+ preservesAuto: true,
+ canStretchAutoSize: false,
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html
new file mode 100644
index 0000000000..83482c5f5c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-removed-ib-sibling.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>getComputedStyle() returns the right style for layout-dependent properties for nodes that have had an IB sibling removed</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1585882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ div {
+ width: 100%;
+ }
+</style>
+<span>
+ <div></div>
+ <div></div>
+</span>
+<script>
+test(() => {
+ let first = document.querySelector("div");
+ let second = document.querySelector("div + div");
+
+ let oldWidth = getComputedStyle(second).width;
+ assert_true(oldWidth.indexOf("px") !== -1, "Should return the used value for width");
+
+ first.remove();
+
+ assert_equals(getComputedStyle(second).width, oldWidth, "Should return the used value for width (after sibling removal)");
+}, "getComputedStyle() should return the correct used value for nodes that have had an IB-split sibling removed");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html
new file mode 100644
index 0000000000..98771cebc7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>getComputedStyle() returns the right style for layout-dependent properties for nodes that have been just inserted into the document, and that have an ancestor whose layout tree was recreated (like an IB-split)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1585882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ div {
+ width: 100%;
+ }
+</style>
+<span>
+ <div></div>
+</span>
+<script>
+test(() => {
+ let oldDiv = document.querySelector("div");
+ window.unused = oldDiv.getBoundingClientRect(); // update layout
+
+ let oldWidth = getComputedStyle(oldDiv).width;
+ assert_true(oldWidth.indexOf("px") !== -1, "Should return the used value for width");
+
+ let newDiv = document.createElement("div");
+ oldDiv.replaceWith(newDiv);
+
+ assert_equals(getComputedStyle(newDiv).width, oldWidth, "Should return the used value for width (just inserted into the document)");
+}, "getComputedStyle() should return used value correctly for nodes just inserted into the document, even if they're in an IB-split");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html b/testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html
new file mode 100644
index 0000000000..089a6d8485
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-line-height.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: line-height resolved value</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3749">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ div { font-size: 16px; }
+</style>
+<div style="line-height: normal" data-expected="normal"></div>
+<div style="line-height: 1" data-expected="16px"></div>
+<div style="line-height: 10px" data-expected="10px"></div>
+<div style="line-height: 10%" data-expected="1.6px"></div>
+<script>
+for (const e of document.querySelectorAll("div")) {
+ const specified = e.style.lineHeight;
+ test(function() {
+ const expected = e.getAttribute("data-expected");
+ assert_equals(getComputedStyle(e).lineHeight, expected, specified + " should compute to " + expected);
+ }, "line-height: " + specified);
+}
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html b/testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html
new file mode 100644
index 0000000000..02093120f2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-logical-enumeration.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle enumeration</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=210695">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1072180">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+ test(function() {
+ let longhand = false;
+ let shorthand = false;
+ let env = false;
+ for (let property of getComputedStyle(document.documentElement)) {
+ if (property == "block-size")
+ longhand = true;
+ if (property == "padding-block")
+ shorthand = true;
+ if (property == "safe-area-inset-top")
+ env = true;
+ }
+ assert_true(longhand, "Logical longhands should be enumerated in computed style");
+ assert_false(shorthand, "Logical shorthands should not be enumerated in computed style");
+ assert_false(env, "Environment variables should not be enumerated in computed style");
+ }, "Logical properties in enumeration of computed style")
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-margins-roundtrip.html b/testing/web-platform/tests/css/cssom/getComputedStyle-margins-roundtrip.html
new file mode 100644
index 0000000000..8913be012d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-margins-roundtrip.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<title>Chromium bug: getComputedStyle() fixed-length inset values don't roundtrip</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1482703">
+<link rel="help" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="target1" style="margin-left: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target1).marginLeft, '20.7px');
+}, 'Fixed-length margin-left property value should roundtrip');
+</script>
+
+<div id="target2" style="margin-right: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target2).marginRight, '20.7px');
+}, 'Fixed-length margin-right property value should roundtrip');
+</script>
+
+<div id="target3" style="margin-top: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target3).marginTop, '20.7px');
+}, 'Fixed-length margin-top property value should roundtrip');
+</script>
+
+<div id="target4" style="margin-bottom: 20.7px"></div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target4).marginBottom, '20.7px');
+}, 'Fixed-length margin-bottom property value should roundtrip');
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html b/testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html
new file mode 100644
index 0000000000..f52700eb94
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-property-order.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle property order</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<meta name="assert" content="This test checks that in a computed style, properties are sorted in lexicographical order." />
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(function() {
+ const properties = [...getComputedStyle(document.documentElement)];
+ const sorted = properties.slice().sort(function(a,b) {
+ if (a === b) {
+ assert_unreached("There shouldn't be repeated properties");
+ return 0;
+ }
+ // Sort vendor-prefixed properties after standard ones
+ if (a.startsWith("-") != b.startsWith("-")) {
+ return b.startsWith("-") ? -1 : 1;
+ }
+ // Sort with lexicographical order
+ return a < b ? -1 : 1;
+ });
+ assert_array_equals(properties, sorted);
+}, "Computed style properties should be sorted lexicographically");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html b/testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html
new file mode 100644
index 0000000000..c07347a197
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-pseudo.html
@@ -0,0 +1,294 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: Correct resolution of resolved value for display-affected pseudo-elements</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Karl Dubost" href="https://github.com/karlcow">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#test { width: 100px; }
+
+#contents {
+ display: contents;
+ border: 10px solid red;
+}
+
+#test::before,
+#test::after,
+#contents::before,
+#contents::after,
+#flex::before,
+#flex::after {
+ content: " ";
+ width: 50%;
+ height: 10px;
+ display: block;
+}
+#none {
+ display: none;
+}
+#none::before,
+#none::after {
+ content: "Foo";
+}
+#flex {
+ display: flex;
+}
+#flex-no-pseudo {
+ display: flex;
+}
+#contents-pseudos::before,
+#contents-pseudos::after {
+ display: contents;
+ content: "foo";
+ position: absolute;
+}
+#contents-pseudos-dynamic::before,
+#contents-pseudos-dynamic::after {
+ display: block;
+ content: "foo";
+ position: absolute;
+}
+#contents-pseudos-dynamic.contents::before,
+#contents-pseudos-dynamic.contents::after {
+ display: contents;
+}
+#pseudo-invalid::backdrop {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::file-selector-button {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::grammar-error {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::highlight(name) {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::marker {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::placeholder {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::spelling-error {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::view-transition {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::view-transition-image-pair(name) {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::view-transition-group(name) {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::view-transition-old(name) {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid::view-transition-new(name) {
+ color: rgb(0, 128, 0);
+}
+#pseudo-invalid {
+ color: rgb(255, 0, 0)
+}
+</style>
+<div id="test">
+ <div id="contents"></div>
+ <div id="none"></div>
+ <div id="flex"></div>
+ <div id="flex-no-pseudo"></div>
+ <div id="contents-pseudos"></div>
+ <div id="contents-pseudos-dynamic"></div>
+ <ul><li id="pseudo-invalid">Item</li></ul>
+</div>
+<script>
+test(() => {
+ const div = document.getElementById('test');
+ ["before", "after"].forEach(pseudo => {
+ assert_equals(getComputedStyle(div, pseudo).width, "100px");
+ });
+}, "Resolution of width is correct when pseudo-element argument is ignored (due to no colon)");
+
+test(() => {
+ const div = document.getElementById('test');
+ [
+ ":before ",
+ "::before ",
+ "::before\t",
+ "::before\f",
+ "::before\n",
+ "::before,",
+ "::before,::after",
+ "::before@after",
+ "::before#after",
+ "::\"before\"",
+ "::before\u0000",
+ "::before-->",
+ "::before0",
+ ].forEach(pseudo => {
+ assert_equals(getComputedStyle(div, pseudo).width, "", pseudo);
+ });
+}, "Resolution of width is correct when pseudo-element argument is invalid (due to a trailing token)");
+
+test(() => {
+ const div = document.getElementById('test');
+ [":before", ":after"].forEach(pseudo => {
+ assert_equals(getComputedStyle(div, pseudo).width, "50px");
+ });
+}, "Resolution of width is correct for ::before and ::after pseudo-elements (single-colon)");
+
+test(() => {
+ const div = document.getElementById('test');
+ ["::before", "::after"].forEach(pseudo => {
+ assert_equals(getComputedStyle(div, pseudo).width, "50px");
+ });
+}, "Resolution of width is correct for ::before and ::after pseudo-elements (double-colon)");
+
+test(function() {
+ const div = document.getElementById('test');
+ [":bef\\oRE", "::\\000041fter"].forEach(pseudo => {
+ assert_equals(getComputedStyle(div, pseudo).width, "50px");
+ });
+}, "Pseudo-elements can use the full range of CSS syntax");
+
+test(function() {
+ var contents = document.getElementById('contents');
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(contents, pseudo).width, "50px");
+ });
+}, "Resolution of width is correct for ::before and ::after pseudo-elements of display: contents elements");
+
+test(function() {
+ var has_no_pseudos = document.body;
+ has_no_pseudos.style.position = "relative";
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(has_no_pseudos, pseudo).position, "static",
+ "Nonexistent " + pseudo + " pseudo-element shouldn't claim to have " +
+ "the same style as the originating element");
+ assert_equals(getComputedStyle(has_no_pseudos, pseudo).width, "auto",
+ "Nonexistent " + pseudo + " pseudo-element shouldn't claim to have " +
+ "definite size");
+ });
+}, "Resolution of nonexistent pseudo-element styles");
+
+test(function() {
+ var none = document.getElementById('none');
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(none, pseudo).content, "\"Foo\"",
+ "Pseudo-styles of display: none elements should be correct");
+ });
+}, "Resolution of pseudo-element styles in display: none elements");
+
+test(function() {
+ var flex = document.getElementById('flex');
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(flex, pseudo).display, "block",
+ "Pseudo-styles of display: flex elements should get blockified");
+ });
+}, "Item-based blockification of pseudo-elements");
+
+test(function() {
+ var flexNoPseudo = document.getElementById('flex-no-pseudo');
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(flexNoPseudo, pseudo).display, "block",
+ "Pseudo-styles of display: flex elements should get blockified");
+ });
+}, "Item-based blockification of nonexistent pseudo-elements");
+
+test(function() {
+ var contentsPseudos = document.getElementById('contents-pseudos');
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(contentsPseudos, pseudo).display, "contents",
+ "display: contents in " + pseudo + " should get reflected on CSSOM");
+ assert_equals(getComputedStyle(contentsPseudos, pseudo).width, "auto",
+ pseudo + " with display: contents should have no box");
+ assert_equals(getComputedStyle(contentsPseudos, pseudo).position, "absolute",
+ "display: contents in " + pseudo + " should reflect other non-inherited properties in CSSOM");
+ });
+}, "display: contents on pseudo-elements");
+
+test(function() {
+ var contentsPseudosDynamic = document.getElementById('contents-pseudos-dynamic');
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).display, "block",
+ "Check that display for " + pseudo + " is block before change");
+ });
+ contentsPseudosDynamic.className = "contents";
+ [":before", ":after"].forEach(function(pseudo) {
+ assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).display, "contents",
+ "display: contents in " + pseudo + " should get reflected on CSSOM");
+ assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).width, "auto",
+ pseudo + " with display: contents should have no box");
+ assert_equals(getComputedStyle(contentsPseudosDynamic, pseudo).position, "absolute",
+ "display: contents in " + pseudo + " should reflect other non-inherited properties in CSSOM");
+ });
+}, "Dynamically change to display: contents on pseudo-elements");
+
+test(() => {
+ const div = document.getElementById('test');
+ // Note that these assertions deliberately avoid assert_[not_]equals to
+ // avoid gCS().length in the failure output.
+ assert_true(
+ getComputedStyle(div, "totallynotapseudo").length != 0,
+ "Should return the element's style for unknown pseudo-elements that don't start with a colon");
+ assert_true(
+ getComputedStyle(div, "::totallynotapseudo").length == 0,
+ "Should return an empty style for unknown pseudo-elements starting with double-colon");
+ assert_true(
+ getComputedStyle(div, ":totallynotapseudo").length == 0,
+ "Should return an empty style for unknown pseudo-elements starting with colon");
+}, "Unknown pseudo-elements");
+
+test(() => {
+ const div = document.getElementById('test');
+
+ const style1 = getComputedStyle(div, "totallynotapseudo");
+ assert_throws_dom("NoModificationAllowedError", () => style1.color = "1");
+ assert_throws_dom("NoModificationAllowedError", () => style1.margin = "10px");
+
+ const style2 = getComputedStyle(div, "::totallynotapseudo");
+ assert_throws_dom("NoModificationAllowedError", () => style2.color = "1");
+ assert_throws_dom("NoModificationAllowedError", () => style2.margin = "10px");
+
+ const style3 = getComputedStyle(div, ":totallynotapseudo");
+ assert_throws_dom("NoModificationAllowedError", () => style3.color = "1");
+ assert_throws_dom("NoModificationAllowedError", () => style3.margin = "10px");
+}, "CSSStyleDeclaration is immutable");
+
+// If you add a pseudo-element identifier here, don't forget to add the corresponding style rule in
+// <style> above.
+[
+ "backdrop",
+ "file-selector-button",
+ "grammar-error",
+ "highlight(name)",
+ "marker",
+ "placeholder",
+ "spelling-error",
+ "view-transition",
+ "view-transition-image-pair(name)",
+ "view-transition-group(name)",
+ "view-transition-old(name)",
+ "view-transition-new(name)"
+].forEach(pseudoIdentifier => {
+ test(() => {
+ assert_implements_optional(CSS.supports(`selector(::${pseudoIdentifier})`), `::${pseudoIdentifier}`);
+ const li = document.querySelector('li');
+ assert_true(
+ getComputedStyle(li, `:${pseudoIdentifier}`).length == 0,
+ `Should return an empty style for :${pseudoIdentifier}`);
+ assert_true(
+ getComputedStyle(li, pseudoIdentifier).length != 0,
+ `Should return the element style for ${pseudoIdentifier}`);
+ assert_equals(
+ getComputedStyle(li, pseudoIdentifier).color, "rgb(255, 0, 0)",
+ `Should return the element style for ${pseudoIdentifier}`);
+ assert_equals(
+ getComputedStyle(li, `::${pseudoIdentifier}`).color, "rgb(0, 128, 0)",
+ `Should return the ::${pseudoIdentifier} style`);
+ }, `Unknown pseudo-element with a known identifier: ${pseudoIdentifier}`);
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html
new file mode 100644
index 0000000000..da4fddbc24
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-colors.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of certain color properties are used values</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #target {
+ background-color: Menu;
+ border: 1px solid Menu;
+ box-shadow: 1px 1px Menu;
+ caret-color: Menu;
+ color: Menu;
+ outline-color: Menu;
+ }
+</style>
+<div id="target"></div>
+<script>
+ const properties_to_test = [
+ "background-color",
+ "border-block-end-color",
+ "border-block-start-color",
+ "border-bottom-color",
+ "border-inline-end-color",
+ "border-inline-start-color",
+ "border-left-color",
+ "border-right-color",
+ "border-top-color",
+ "box-shadow",
+ "caret-color",
+ "color",
+ "outline-color",
+ ];
+
+ for (const property of properties_to_test) {
+ test(function() {
+ let resolved_value =
+ window.getComputedStyle(document.getElementById("target")).getPropertyValue(property);
+ assert_regexp_match(resolved_value, /^rgb[a]?\(/);
+ }, "The resolved value for '" + property + "' is the used value");
+ }
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html
new file mode 100644
index 0000000000..e630377c33
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-resolved-min-max-clamping.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: resolved values of the width and height when the element generates no box or are a non-replaced inline aren't clamped by min-width / max-width</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<span id="non-replaced-inline"></span>
+<div id="display-none" style="display: none"></div>
+<div id="display-contents" style="display: contents"></div>
+<script>
+test(function() {
+ for (const e of document.querySelectorAll("[id]")) {
+ const cs = getComputedStyle(e);
+ for (const prop of ["width", "height"]) {
+ e.style.setProperty("min-" + prop, "10px");
+ e.style.setProperty("max-" + prop, "50px");
+
+ e.style.setProperty(prop, "10%");
+ assert_equals(cs[prop], "10%", `${e.id}: ${prop} with percentages returns percentages`);
+
+ e.style.setProperty(prop, "15px");
+ assert_equals(cs[prop], "15px", `${e.id}: ${prop} with value in range returns computed value`);
+
+ e.style.setProperty(prop, "1px");
+ assert_equals(cs[prop], "1px", `${e.id}: ${prop} with value out of range isn't clamped by min-${prop}`);
+
+ e.style.setProperty(prop, "60px");
+ assert_equals(cs[prop], "60px", `${e.id}: ${prop} with value out of range isn't clamped by max-${prop}`);
+
+ e.style.removeProperty(prop);
+ e.style.removeProperty("min-" + prop);
+ e.style.removeProperty("max-" + prop);
+ }
+ }
+}, "Resolved value of width / height when there's no used value isn't clamped by min/max properties");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html b/testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html
new file mode 100644
index 0000000000..12ad5e8965
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-sticky-pos-percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>CSS Test: resolve top percentage value against proper box</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3115" />
+<link rel="help" href="https://drafts.csswg.org/css-position/#sticky-pos" />
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-values" />
+<meta name="assert" content="Test that the sticky percentage insets are
+resolved against the right ancestor, i.e. the nearest scrollport." />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="height: 500px; background: purple; overflow: hidden;">
+ <div style="height: 400px; background: yellow;">
+ <div id="target" style="height: 100px; position: sticky; left: 0; top: 50%; background: blue;">
+ </div>
+ </div>
+</div>
+<script>
+test(() => {
+ assert_equals(getComputedStyle(target).top, '250px');
+}, "Sticky element's top property percentage value should be resolved against the div with overflow: hidden");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html b/testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html
new file mode 100644
index 0000000000..8f629c7016
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-width-scroll.tentative.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>getComputedStyle() round-trips in presence of scrollbars.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/cssom/#resolved-value">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div style="width: 100px; height: 100px; overflow: scroll"></div>
+<script>
+// NOTE(emilio): This is a .tentative.html because the spec is not clear on
+// what the used value of `width` or `height` is, but I think this behavior
+// should be uncontroversial.
+test(function() {
+ let e = document.querySelector("div");
+ let cs = getComputedStyle(e);
+
+ let originalWidth = cs.width;
+ let originalHeight = cs.height;
+
+ e.style.width = originalWidth;
+ e.style.height = originalHeight;
+
+ assert_equals(cs.width, originalWidth, "width round-trips");
+ assert_equals(cs.height, originalHeight, "height round-trips");
+}, "getComputedStyle() round-trips in presence of scrollbars")
+</script>
diff --git a/testing/web-platform/tests/css/cssom/historical.html b/testing/web-platform/tests/css/cssom/historical.html
new file mode 100644
index 0000000000..44f3f6c8f1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/historical.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>Historical features</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#changes-from-5-december-2013">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+[
+ "selectedStyleSheetSet",
+ "lastStyleSheetSet",
+ "preferredStyleSheetSet",
+ "styleSheetSets",
+ "enableStyleSheetsForSet",
+ "selectedStylesheetSet",
+ "preferredStylesheetSet",
+].forEach(function(name) {
+ test(function() {
+ assert_false(name in document);
+
+ var doc = document.implementation.createDocument(null, null, null);
+ assert_false(name in doc);
+ }, "Historical Document member: " + name);
+});
+
+[
+ "Rect",
+ "RGBColor",
+ "CSSValue",
+ "CSSPrimitiveValue",
+ "CSSValueList",
+].forEach(function(name) {
+ test(function() {
+ assert_false(name in window);
+ }, "Historical interface: " + name);
+});
+
+[
+ "getPropertyCSSValue",
+ "setPropertyValue",
+ "setPropertyPriority",
+].forEach(function(name) {
+ test(function() {
+ assert_false(name in document.body.style);
+ }, "Historical CSSStyleDeclaration member: " + name);
+});
+
+[
+ "cascadedStyle",
+ "defaultStyle",
+ "rawComputedStyle",
+ "usedStyle",
+].forEach(function(name) {
+ test(function() {
+ assert_false(name in document.body);
+ assert_false(name in document.createElement("test"));
+ }, "Historical Element member: " + name);
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/idlharness.html b/testing/web-platform/tests/css/cssom/idlharness.html
new file mode 100644
index 0000000000..e27629c03b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/idlharness.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<meta charset=utf-8>
+<!-- WARNING: These tests are preliminary and probably partly incorrect. -->
+<title>CSSOM automated IDL tests</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#idl-index">
+<meta name="timeout" content="long">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+<!--
+Provide some objects to test.
+Use a non-empty style attribute to get a non-empty CSSStyleDeclaration.
+-->
+<style id="styleElement" style="z-index: 0;">
+@import url("data:text/css,");
+@namespace x "y";
+@page { @top-left {} }
+@media all {}
+#test { color: green; }
+</style>
+
+<svg id="svgElement" style="height: 0;"></svg>
+
+<iframe id="xmlssPiIframe" src="support/xmlss-pi.xhtml" style="display: none;"></iframe>
+
+<h1>CSSOM IDL tests</h1>
+<div id=log></div>
+
+<script>
+'use strict';
+
+const waitForLoad = new Promise(r => { addEventListener('load', r); });
+
+idl_test(
+ ['cssom'],
+ ['SVG', 'uievents', 'html', 'dom', 'mathml-core'],
+ async idlArray => {
+ idlArray.add_objects({
+ Document: ['document', 'new Document()'],
+ StyleSheetList: ['document.styleSheets'],
+ CSSStyleSheet: ['sheet'],
+ MediaList: ['sheet.media'],
+ CSSRuleList: ['sheet.cssRules'],
+ CSSImportRule: ['sheet.cssRules[0]'],
+ CSSNamespaceRule: ['sheet.cssRules[1]'],
+ CSSPageRule: ['sheet.cssRules[2]'],
+ CSSMarginRule: ['sheet.cssRules[2].cssRules[0]'],
+ CSSMediaRule: ['sheet.cssRules[3]'],
+ CSSStyleRule: ['sheet.cssRules[4]'],
+ CSSStyleDeclaration: [
+ 'sheet.cssRules[4].style', // CSSStyleRule
+ 'sheet.cssRules[2].style', // CSSPageRule
+ 'sheet.cssRules[2].cssRules[0].style', // CSSMarginRule
+ 'style_element.style', // ElementCSSInlineStyle for HTMLElement
+ 'svg_element.style', // ElementCSSInlineStyle for SVGElement
+ 'getComputedStyle(svg_element)'
+ ],
+ ProcessingInstruction: ['xmlss_pi'],
+ Window: ['window'],
+ HTMLElement: [
+ 'style_element',
+ 'document.createElement("unknownelement")'
+ ],
+ SVGElement: ['svg_element'],
+ });
+
+ await waitForLoad;
+ self.style_element = document.getElementById('styleElement');
+ self.sheet = style_element.sheet;
+ self.svg_element = document.getElementById('svgElement');
+ self.xmlss_pi = document.getElementById('xmlssPiIframe').contentDocument.firstChild;
+ }
+);
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/inline-style-001.html b/testing/web-platform/tests/css/cssom/inline-style-001.html
new file mode 100644
index 0000000000..d4bf3b92ab
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/inline-style-001.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: Inline CSSStyleDeclaration</title>
+ <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#elementcssinlinestyle">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="Inline CSSStyleDeclaration is properly initialized and can be modified through its interface">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+ <div id="test" style="margin-left: 5px;"></div>
+ <script type="text/javascript">
+ // Can access CSSStyleDeclaration through style property
+ test(function() {
+ var test = document.getElementById("test");
+ assert_idl_attribute(test, "style");
+ declaration = test.style;
+ }, "CSSStyleDeclaration_accessible");
+
+ // initial property values are correct
+ test(function() {
+ assert_equals(declaration.cssText, "margin-left: 5px;");
+ assert_equals(declaration.getPropertyValue("margin-left"), "5px");
+ }, "read");
+
+ // setting cssText adds new properties
+ // setting cssText removes existing properties
+ // properties set through cssText are reflected in the computed style
+ test(function() {
+ declaration.cssText = "margin-left: 10px; padding-left: 10px;";
+ assert_equals(declaration.cssText, "margin-left: 10px; padding-left: 10px;");
+ assert_equals(declaration.length, 2);
+ assert_equals(declaration.item(0), "margin-left");
+ assert_equals(declaration.item(1), "padding-left");
+ assert_equals(declaration.getPropertyValue("margin-left"), "10px");
+ assert_equals(declaration.getPropertyValue("padding-left"), "10px");
+
+ var computedStyle = window.getComputedStyle(document.getElementById("test"));
+ assert_equals(computedStyle.getPropertyValue("margin-left"), "10px");
+ assert_equals(computedStyle.getPropertyValue("padding-left"), "10px");
+ }, "csstext_write");
+
+ // setProperty adds new properties
+ // properties set through setProperty are reflected in the computed style
+ test(function() {
+ while(declaration.length > 0)
+ declaration.removeProperty(declaration.item(0));
+ declaration.setProperty("margin-left", "15px");
+ declaration.setProperty("padding-left", "15px");
+
+ assert_equals(declaration.length, 2);
+ assert_equals(declaration.item(0), "margin-left");
+ assert_equals(declaration.item(1), "padding-left");
+ assert_equals(declaration.getPropertyValue("margin-left"), "15px");
+ assert_equals(declaration.getPropertyValue("padding-left"), "15px");
+
+ var computedStyle = window.getComputedStyle(document.getElementById("test"));
+ assert_equals(computedStyle.getPropertyValue("margin-left"), "15px");
+ assert_equals(computedStyle.getPropertyValue("padding-left"), "15px");
+ }, "property_write");
+
+ // shorthand property is expanded
+ test(function() {
+ while(declaration.length > 0)
+ declaration.removeProperty(declaration.item(0));
+ declaration.cssText = "margin: 20px";
+ assert_equals(declaration.getPropertyValue("margin-top"), "20px");
+ assert_equals(declaration.getPropertyValue("margin-right"), "20px");
+ assert_equals(declaration.getPropertyValue("margin-bottom"), "20px");
+ assert_equals(declaration.getPropertyValue("margin-left"), "20px");
+ }, "shorthand_properties");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/insert-dir-rule-crash.html b/testing/web-platform/tests/css/cssom/insert-dir-rule-crash.html
new file mode 100644
index 0000000000..6cffa0c62a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insert-dir-rule-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Chrome crash inserting :dir rule</title>
+<link rel="help" href="https://crbug.com/1486351">
+<p>PASS if no crash.</p>
+<style id="sheet"> </style>
+<div id="host"></div>
+<script>
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = "<slot></slot>";
+ host.offsetTop;
+ host.innerHTML = "<span>Green</span>";
+ sheet.sheet.insertRule("html:dir(ltr) { color: green; }");
+ document.fonts.size();
+</script>
diff --git a/testing/web-platform/tests/css/cssom/insert-dir-rule-in-iframe-crash.html b/testing/web-platform/tests/css/cssom/insert-dir-rule-in-iframe-crash.html
new file mode 100644
index 0000000000..4c9d7530dc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insert-dir-rule-in-iframe-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Chrome crash inserting :dir rule in iframe</title>
+<link rel="help" href="https://crbug.com/1486351">
+<p>PASS if no crash.</p>
+<iframe srcdoc='<!DOCTYPE html>
+ <style id="sheet"> </style>
+ <div id="host"></div>
+ <script>
+ let root = host.attachShadow({mode:"open"});
+ root.innerHTML = "<slot></slot>";
+ host.offsetTop;
+ host.innerHTML = "<span>Green</span>";
+ sheet.sheet.insertRule("html:dir(ltr) { color: green; }");
+ getComputedStyle(host).color;
+ </script>
+ '>
+</iframe>
diff --git a/testing/web-platform/tests/css/cssom/insert-invalid-where-rule-crash.html b/testing/web-platform/tests/css/cssom/insert-invalid-where-rule-crash.html
new file mode 100644
index 0000000000..752f7043b4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insert-invalid-where-rule-crash.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Chrome crash keeping rule with empty selector list</title>
+<link rel="help" href="https://crbug.com/1499358">
+<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
+<p id="elem">PASS if no crash.</p>
+<style id="sheet">.x, :where("something invalid") {} </style>
+<script>
+ elem.offsetTop;
+ sheet.sheet.insertRule("p { color: green; }", 0);
+</script>
diff --git a/testing/web-platform/tests/css/cssom/insertRule-across-context.html b/testing/web-platform/tests/css/cssom/insertRule-across-context.html
new file mode 100644
index 0000000000..a8327e31ac
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-across-context.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: CSSOM StyleSheet insertRule across context</title>
+<link rel="author" title="Kagami Sascha Rosylight" href="mailto:saschanaz@outlook.com">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/cssom/">
+<script src="/resources/testharness.js" type="text/javascript"></script>
+<script src="/resources/testharnessreport.js" type="text/javascript"></script>
+
+<iframe id="iframe"></iframe>
+<script>
+ function createSheet() {
+ const tempStyleElement = iframe.contentDocument.createElement('style');
+ iframe.contentDocument.head.append(tempStyleElement);
+
+ const tempStyle = tempStyleElement.sheet;
+ tempStyleElement.remove();
+ return tempStyle;
+ }
+
+ test(() => {
+ const sheet = createSheet();
+ sheet.insertRule(".kaoru {}");
+ assert_equals(sheet.rules[0].constructor, iframe.contentWindow.CSSStyleRule);
+ }, "The constructor of inserted rule object must be from iframe");
+
+ test(() => {
+ const sheet = new iframe.contentWindow.CSSStyleSheet();
+ sheet.insertRule(".kaoru {}");
+ assert_equals(sheet.rules[0].constructor, iframe.contentWindow.CSSStyleRule);
+ }, "The constructor of inserted rule object must be from iframe for new CSSStyleSheet()");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html
new file mode 100644
index 0000000000..16f2358dc2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-charset-no-index.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS Test: CSSOM StyleSheet insertRule with charset and omitted second argument</title>
+ <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom/">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <link rel="stylesheet" type="text/css" href="support/import-charset.css" id="linkElement" >
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+ var sheet = document.getElementById("linkElement").sheet;
+ test(function() {
+ assert_equals(sheet.cssRules.length, 0);
+ sheet.insertRule("p { color: green; }");
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules.item(0).cssText, "p { color: green; }");
+ }, "insertRule with charset and omitted index argument");
+
+ test(function() {
+ assert_equals(sheet.cssRules.length, 1);
+ sheet.insertRule("p { color: yellow; }", undefined);
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules.item(0).cssText, "p { color: yellow; }");
+ }, "insertRule with charset and undefined index argument");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html b/testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html
new file mode 100644
index 0000000000..2fa45526a6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-from-script-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<head>
+ <meta charset="utf-8">
+ <title>insertrule @import test reference</title>
+ <link rel="stylesheet" href="support/black.css">
+</head> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/insertRule-from-script.html b/testing/web-platform/tests/css/cssom/insertRule-from-script.html
new file mode 100644
index 0000000000..c364afc282
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-from-script.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<head>
+ <meta charset="utf-8">
+ <title>insertrule @import test</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <link rel="match" href="insertRule-from-script-ref.html">
+ <style></style>
+</head>
+<body>
+ <script>document.styleSheets[0].insertRule("@import url(\"support/black.css\");");</script>
+</body> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/insertRule-import-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-import-no-index.html
new file mode 100644
index 0000000000..45481e71f4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-import-no-index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS Test: CSSOM StyleSheet insertRule with import and omitted second argument</title>
+ <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom/">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style id="styleElement">
+ @import url("support/a-green.css");
+ </style>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+ var sheet = document.getElementById("styleElement").sheet;
+ test(function() {
+ assert_equals(sheet.cssRules.length, 1);
+ assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: green; }"); });
+ assert_equals(sheet.cssRules.length, 1);
+ }, "insertRule with import and omitted index argument");
+
+ test(function() {
+ assert_equals(sheet.cssRules.length, 1);
+ assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: yellow; }", undefined); });
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules.item(0).cssText, "@import url(\"support/a-green.css\");");
+ }, "insertRule with import and undefined index argument");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html
new file mode 100644
index 0000000000..3265159736
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-namespace-no-index.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS Test: CSSOM StyleSheet insertRule with namespace and omitted second argument</title>
+ <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom/">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style id="styleElement">
+ @namespace svg url(http://servo);
+ @namespace url(http://servo1);
+ @namespace svg url("http://servo2");
+ </style>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+ var sheet = document.getElementById("styleElement").sheet;
+ test(function() {
+ assert_equals(sheet.cssRules.length, 3);
+ assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: green; }"); });
+ assert_equals(sheet.cssRules.length, 3);
+ }, "insertRule with namespace and omitted index argument");
+
+ test(function() {
+ assert_equals(sheet.cssRules.length, 3);
+ assert_throws_dom("HierarchyRequestError", function() { sheet.insertRule("p { color: yellow; }", undefined); });
+ assert_equals(sheet.cssRules.length, 3);
+ }, "insertRule with namespace and undefined index argument");
+
+ test(function() {
+ assert_equals(sheet.cssRules.length, 3);
+ sheet.insertRule("@import url(\"support/a-green.css\");");
+ assert_equals(sheet.cssRules.length, 4);
+ }, "insertRule with namespace and omitted index argument should insert import");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/insertRule-no-index.html b/testing/web-platform/tests/css/cssom/insertRule-no-index.html
new file mode 100644
index 0000000000..b4370bd080
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-no-index.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS Test: CSSOM StyleSheet insertRule omitted second argument</title>
+ <link rel="author" title="Sendil Kumar N" href="mailto:sendilkumarn.opensource@gmail.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom/">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style id="styleElement">
+ /* An initial style rule to test where the new rule is inserted relative to this one */
+ nosuchelement { color: red; }
+ </style>
+</head>
+<body>
+<div id="log"></div>
+<script type="text/javascript">
+ var sheet = document.getElementById("styleElement").sheet;
+ test(function() {
+ assert_equals(sheet.cssRules.length, 1);
+ sheet.insertRule("p { color: green; }");
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules.item(0).cssText, "p { color: green; }");
+ }, "insertRule with omitted index argument");
+
+ test(function() {
+ assert_equals(sheet.cssRules.length, 2);
+ sheet.insertRule("p { color: yellow; }", undefined);
+ assert_equals(sheet.cssRules.length, 3);
+ assert_equals(sheet.cssRules.item(0).cssText, "p { color: yellow; }");
+ }, "insertRule with undefined index argument");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html b/testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html
new file mode 100644
index 0000000000..4461a9a943
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/insertRule-syntax-error-01.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-1/#insert-a-css-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+p { color: green; }
+</style>
+<p id="test">This text should be green.</p>
+<script>
+ test(function() {
+ assert_throws_dom("SyntaxError", () => document.styleSheets[0].insertRule("p { color: red; } garbage", 1));
+ assert_equals(getComputedStyle(document.getElementById("test")).color, "rgb(0, 128, 0)");
+ }, "A syntax error in insertRule should throw and not affect the style of the page");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/invalid-pseudo-elements.html b/testing/web-platform/tests/css/cssom/invalid-pseudo-elements.html
new file mode 100644
index 0000000000..35a452654e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/invalid-pseudo-elements.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: Test that rules with invalid pseudo elements are not found</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ ::part {}
+ ::slotted {}
+ ::highlight {}
+</style>
+<script>
+ test(() => {
+ assert_equals(document.styleSheets[0].cssRules[0]?.selectorText, undefined, "No associated selectorText");
+ assert_equals(document.styleSheets[0].cssRules[0], undefined, "No invalid pseudo element rule should be found");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html b/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html
new file mode 100644
index 0000000000..9715b5acb8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+Should not be red.
diff --git a/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html b/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html
new file mode 100644
index 0000000000..ab9c134568
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-dynamic-001.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: Dynamic changes to the stylesheet media attributes via CSSOM get reflected</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="match" href="medialist-dynamic-001-ref.html">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-medialist-mediatext">
+<style media="all">* { color: red }</style>
+Should not be red.
+<script>
+ document.body.offsetTop;
+ document.styleSheets[0].media.mediaText = "not all";
+</script>
diff --git a/testing/web-platform/tests/css/cssom/medialist-interfaces-001.html b/testing/web-platform/tests/css/cssom/medialist-interfaces-001.html
new file mode 100644
index 0000000000..f436177fb8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-interfaces-001.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: CSSOM Media Query List Serialization</title>
+ <link rel="author" title="Ben Sheldon" href="mailto:ben@codeforamerica.org">
+ <link rel="author" title="Chapman Shoop" href="mailto:chapman.shoop@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-medialist-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#serializing-media-queries">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#serialize-a-media-query-list">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="MediaLists are serialized according to the specification">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+
+ var styleElement;
+ var styleSheet;
+ var mediaList;
+
+ // Setup
+ function setup() {
+ styleElement = document.getElementById("styleElement");
+
+ if (styleElement) {
+ // teardown
+ document.getElementsByTagName("head")[0].removeChild(styleElement);
+ styleElement = undefined;
+ styleSheet = undefined;
+ mediaList = undefined;
+ }
+
+ styleElement = document.createElement("style");
+ styleElement.id = "styleElement";
+ styleElement.type = "text/css";
+ styleElement.media = "all";
+ document.getElementsByTagName("head")[0].appendChild(styleElement);
+ styleSheet = styleElement.sheet;
+ mediaList = styleSheet.media;
+ }
+
+ // MediaList.mediaText equals the 'media' value of the initial 'style' element.
+ test(function() {
+ setup();
+
+ assert_equals(mediaList.mediaText, "all");
+
+ }, "mediatest_medialist_serialize_element");
+
+ // To serialize a comma-separated list concatenate all items of the list in list order while separating them by \",\" (U+002C), followed by a space (U+0020).
+ test(function() {
+ setup();
+
+ mediaList.appendMedium('screen');
+ assert_equals(mediaList.mediaText, "all, screen");
+
+ }, "mediatest_medialist_serialize_comma");
+
+ // If the media query list is empty return the empty string.
+ test(function() {
+ setup();
+
+ mediaList.deleteMedium('all');
+ assert_equals(mediaList.mediaText, "");
+
+ }, "mediatest_medialist_serialize_empty");
+
+ // Each media query should be sorted in the same order as they appear in the list of media queries.
+ test(function() {
+ setup();
+
+ mediaList.appendMedium('screen');
+ mediaList.appendMedium('print');
+ assert_equals(mediaList.mediaText, "all, screen, print");
+
+ }, "mediatest_medialist_serialize_order");
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/medialist-interfaces-002.html b/testing/web-platform/tests/css/cssom/medialist-interfaces-002.html
new file mode 100644
index 0000000000..0d2ca2e9c7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-interfaces-002.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS Test: CSSOM MediaList Interfaces</title>
+ <link rel="author" title="Chapman Shoop" href="mailto:chapman.shoop@gmail.com">
+ <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 -->
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-medialist-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="MediaList object has deleteMedium method and it functions properly.">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+</head>
+
+<body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ function setup() {
+ // Clean out any old style element
+ var old_style_el = document.getElementById('test_style');
+ if (old_style_el) {
+ document.head.removeChild(old_style_el);
+ }
+
+ // Create a fresh style element and return its media attribute
+ var style_el = document.createElement('style');
+ style_el.setAttribute('id', 'test_style');
+ document.head.appendChild(style_el);
+ return style_el.sheet.media;
+ }
+ </script>
+
+ <script type="text/javascript">
+
+ // MediaList.deleteMedium called without argument throws error.
+ test(function() {
+ media_list = setup();
+ assert_throws_js(TypeError, function() { media_list.deleteMedium(); });
+ }, "deleteMedium_called_without_argument");
+
+ // MediaList.deleteMedium removes correct medium and updates corresponding properties.
+ test(function() {
+ media_list = setup();
+
+ media_list.appendMedium("screen");
+ media_list.appendMedium("all");
+
+ media_list.deleteMedium("screen");
+
+ assert_equals(media_list.length, 1);
+ assert_equals(media_list.item(0), "all");
+ assert_equals(media_list.mediaText, "all");
+ }, "deleteMedium_removes_correct_medium");
+
+ // MediaList.deleteMedium doesn't modify MediaList when medium is not found.
+ test(function() {
+ media_list = setup();
+
+ media_list.appendMedium("all");
+
+ assert_throws_dom("NotFoundError", () => media_list.deleteMedium("screen"));
+
+ assert_equals(media_list.length, 1);
+ assert_equals(media_list.item(0), "all");
+ assert_equals(media_list.mediaText, "all");
+ }, "deleteMedium_no_matching_medium_to_remove");
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/medialist-interfaces-004.html b/testing/web-platform/tests/css/cssom/medialist-interfaces-004.html
new file mode 100644
index 0000000000..7cbea37cba
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/medialist-interfaces-004.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSS Test: CSSOM MediaList Interfaces</title>
+ <link rel="author" title="Chapman Shoop" href="mailto:chapman.shoop@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-medialist-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="MediaList object has appendMedium method and it functions properly.">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+</head>
+
+<body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+
+ <script type="text/javascript">
+ function setup() {
+ // Clean out any old style element
+ var old_style_el = document.getElementById('test_style');
+ if (old_style_el) {
+ document.head.removeChild(old_style_el);
+ }
+
+ // Create a fresh style element and return its media attribute
+ var style_el = document.createElement('style');
+ style_el.setAttribute('id', 'test_style');
+ document.head.appendChild(style_el);
+ return style_el.sheet.media;
+ }
+ </script>
+
+ <script type="text/javascript">
+
+ // MediaList.appendMedium correctly adds medium to empty MediaList.
+ test(function() {
+ media_list = setup();
+
+ media_list.appendMedium("all");
+
+ assert_equals(media_list.length, 1);
+ assert_equals(media_list.item(0), "all");
+ assert_equals(media_list.mediaText, "all");
+ }, "appendMedium_correctly_appends_medium_to_empty_MediaList");
+
+ // MediaList.appendMedium correctly adds medium to a MediaList that already has a medium.
+ test(function() {
+ media_list = setup();
+
+ media_list.appendMedium("screen");
+ media_list.appendMedium("all");
+
+ // The ordering of array items should be different from that of the mediaText element.
+ // "all" should be appended after "screen" in the array, but "mediaText" should be
+ // "all, screen" due to lexicographical sorting.
+ assert_equals(media_list.length, 2);
+ assert_equals(media_list.item(0), "screen");
+ assert_equals(media_list.item(1), "all");
+ assert_equals(media_list.mediaText, "screen, all");
+ }, "appendMedium_correctly_appends_medium_to_nonempty_MediaList");
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html b/testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html
new file mode 100644
index 0000000000..0ae78d0300
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/mediaquery-sort-dedup.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM test: Media query serialization quirks</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#serializing-media-queries">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(function() {
+ assert_equals(matchMedia("(min-width: 10px) and (min-height: 10px)").media, "(min-width: 10px) and (min-height: 10px)");
+} , "Media queries are not sorted lexicographically");
+test(function() {
+ assert_equals(matchMedia("(color) and (color)").media, "(color) and (color)");
+}, "Media queries are not deduplicated");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/overflow-serialization.html b/testing/web-platform/tests/css/cssom/overflow-serialization.html
new file mode 100644
index 0000000000..2e4bd97d5c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/overflow-serialization.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - Overflow shorthand serialization</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-value">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ div { overflow: inherit; }
+ div { overflow: hidden; }
+ div { overflow-x: initial; overflow-y: initial; }
+ div { overflow-x: scroll; overflow-y: scroll; }
+ div { overflow-x: scroll; overflow-y: hidden; }
+ </style>
+
+ <script>
+ var styleSheet = document.styleSheets[0]
+ var div = document.createElement('div')
+ test(function () {
+ assert_equals(styleSheet.cssRules[0].style.cssText, "overflow: inherit;")
+ }, "Single value overflow with CSS-wide keyword should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[1].style.cssText, "overflow: hidden;")
+ }, "Single value overflow with non-CSS-wide keyword should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[2].style.cssText, "overflow: initial;")
+ }, "Overflow-x/y longhands with same CSS-wide keyword should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[3].style.cssText, "overflow: scroll;")
+ }, "Overflow-x/y longhands with same non-CSS-wide keyword should serialize correctly.")
+ test(function () {
+ assert_equals(styleSheet.cssRules[4].style.cssText, "overflow: scroll hidden;")
+ }, "Overflow-x/y longhands with different keywords should serialize correctly.")
+ test(function () {
+ div.style.overflow = "inherit"
+ assert_equals(div.style.overflow, "inherit")
+ }, "Single value overflow on element with CSS-wide keyword should serialize correctly.")
+ test(function () {
+ div.style.overflow = "hidden"
+ assert_equals(div.style.overflow, "hidden")
+ }, "Single value overflow on element with non-CSS-wide keyword should serialize correctly.")
+ test(function () {
+ div.style.overflow = ""
+ div.style.overflowX = "initial"
+ div.style.overflowY = "initial"
+ assert_equals(div.style.overflow, "initial")
+ }, "Overflow-x/y longhands on element with same CSS-wide keyword should serialize correctly.")
+ test(function () {
+ div.style.overflowX = "scroll"
+ div.style.overflowY = "scroll"
+ assert_equals(div.style.overflow, "scroll")
+ }, "Overflow-x/y longhands on element with same non-CSS-wide keyword should serialize correctly.")
+ test(function () {
+ div.style.overflowX = "scroll"
+ div.style.overflowY = "hidden"
+ assert_equals(div.style.overflow, "scroll hidden")
+ }, "Overflow-x/y longhands on element with different keywords should serialize correctly.")
+ </script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/page-descriptors.html b/testing/web-platform/tests/css/cssom/page-descriptors.html
new file mode 100644
index 0000000000..5cf0a6a703
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/page-descriptors.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <link rel="author" title="Mozilla" href="https://mozilla.org">
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface">
+ <title>Page descriptors shouldn't be exposed to CSS Style declarations</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+'use strict';
+
+let element = document.getElementById("target");
+let computedStyle = window.getComputedStyle(element);
+let style = element.style;
+
+test(t => {
+ assert_equals(computedStyle.size, undefined,
+ "computed style should not have size property");
+ assert_equals(computedStyle.getPropertyValue("size"), "",
+ "computed style getPropertyValue(\"size\") should be empty");
+
+ assert_equals(style.size, undefined,
+ "style should not have size property");
+ assert_equals(style.getPropertyValue("size"), "",
+ "style getPropertyValue(\"size\") should be empty");
+ for(const val of ["initial", "auto", "100px"]){
+ style.setProperty("size", val);
+ assert_false(CSS.supports("size", val));
+ assert_equals(style.size, undefined,
+ "style should not have size property after assigning size=" + val);
+ assert_equals(style.getPropertyValue("size"), "",
+ "style getPropertyValue(\"size\") should be empty after assigning size=" + val);
+ }
+});
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html b/testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html
new file mode 100644
index 0000000000..dc990131ab
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/preferred-stylesheet-order.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom/#add-a-css-style-sheet">
+<link rel="help" href="https://drafts.csswg.org/cssom/#create-a-css-style-sheet">
+<link rel="help" href="https://html.spec.whatwg.org/#update-a-style-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="t1">This text should be green</div>
+<script>
+function createStyleElement(text, title) {
+ var elm = document.createElement("style");
+ elm.setAttribute("title", title);
+ elm.appendChild(document.createTextNode(text));
+ return elm;
+}
+
+test(function() {
+ document.head.appendChild(createStyleElement("#t1 {color:green}", "preferred"));
+ document.head.appendChild(createStyleElement("#t1 {color:red}", "notpreferred"));
+
+ assert_equals(getComputedStyle(t1).color, "rgb(0, 128, 0)");
+}, "Preferred stylesheet where insertion order is reversed tree order");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html b/testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html
new file mode 100644
index 0000000000..ff3a8b0901
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/preferred-stylesheet-reversed-order.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom/#add-a-css-style-sheet">
+<link rel="help" href="https://drafts.csswg.org/cssom/#create-a-css-style-sheet">
+<link rel="help" href="https://html.spec.whatwg.org/#update-a-style-block">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="t1">This text should be green</div>
+<script>
+function createStyleElement(text, title) {
+ var elm = document.createElement("style");
+ elm.setAttribute("title", title);
+ elm.appendChild(document.createTextNode(text));
+ return elm;
+}
+
+test(function() {
+ document.head.insertBefore(createStyleElement("#t1 {color:green}", "preferred"), document.head.firstChild);
+ document.head.insertBefore(createStyleElement("#t1 {color:red}", "notpreferred"), document.head.firstChild);
+
+ assert_equals(getComputedStyle(t1).color, "rgb(0, 128, 0)");
+}, "Preferred stylesheet where insertion order is tree order");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/property-accessors.html b/testing/web-platform/tests/css/cssom/property-accessors.html
new file mode 100644
index 0000000000..87c9df0b32
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/property-accessors.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>Accessing properties via CSSStyleDeclaration</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface">
+<!-- this is really a crash test, but let's claim it's a testharness test -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+@font-face {}
+</style>
+<div id="testElement"></div>
+<script>
+// Goal here is to test a cross-section of prefixed, properties, and descriptors.
+const properties = [
+ "-apple-color-filter", "-apple-pay-button-style", "-epub-writing-mode",
+ "-webkit-flex", "gap", "grid-gap", "overscroll-behavior",
+ "src", "unicode-range",
+];
+
+const el = document.getElementById("testElement");
+const decls = [window.getComputedStyle(el), el.style, document.styleSheets[0].cssRules[0].style];
+
+for (const prop of properties) {
+ test(() => {
+ for (const decl of decls) {
+ let _;
+
+ _ = decl[prop];
+ try {
+ decl[prop] = "nonsense";
+ } catch {
+ assert_equals(decl, decls[0]);
+ }
+
+ _ = decl.cssText;
+ try {
+ decl.cssText = `${prop}: nonsense`;
+ } catch {
+ assert_equals(decl, decls[0]);
+ }
+
+ _ = decl.getPropertyValue(prop);
+ _ = decl.getPropertyPriority(prop);
+
+ if ("getPropertyCSSValue" in decl) {
+ _ = decl.getPropertyCSSValue(prop);
+ }
+
+ try {
+ decl.setProperty(prop, "nonsense", "");
+ } catch {
+ assert_equals(decl, decls[0]);
+ }
+
+ try {
+ decl.removeProperty(prop);
+ } catch {
+ assert_equals(decl, decls[0]);
+ }
+ }
+ }, prop);
+}
+</script>
diff --git a/testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html b/testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html
new file mode 100644
index 0000000000..a83f43d5fa
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/removerule-invalidation-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>CSSOM - removeRule doesn't assert when removing a particular set of rules</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#cssstylesheet">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1659718">
+<style>
+@media all {
+ * { color: red; }
+
+ #foobar { color: blue }
+}
+</style>
+<script>
+ document.documentElement.getBoundingClientRect();
+ document.styleSheets[0].deleteRule(0);
+</script>
diff --git a/testing/web-platform/tests/css/cssom/rule-restrictions.html b/testing/web-platform/tests/css/cssom/rule-restrictions.html
new file mode 100644
index 0000000000..ce2dee37a0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/rule-restrictions.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM Should correctly honor property restrictions</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1665816">
+<link rel="help" href="https://drafts.csswg.org/css-page-3/#conform-partial">
+<link rel="help" href="https://drafts.csswg.org/css-animations/#keyframes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @page {
+ margin-top: 10px;
+ transform: scale(1);
+ }
+
+ @keyframes foo {
+ from {
+ margin-top: 10px;
+ animation-name: none;
+ }
+ }
+</style>
+<script>
+test(function() {
+ let rule = document.styleSheets[0].cssRules[0];
+ assert_equals(rule.type, CSSRule.PAGE_RULE, "Should be a page rule");
+ assert_equals(rule.style.length, 1, "Transform doesn't quite apply to pages");
+ assert_equals(rule.style.marginTop, "10px", "Should have a margin-top declaration");
+ rule.style.setProperty("transform", "scale(1)");
+ assert_equals(rule.style.getPropertyValue("transform"), "", "Shouldn't have been able to set the transform property via setProperty");
+ assert_equals(rule.style.length, 1, "Shouldn't have been able to set transform via setProperty");
+ rule.style.cssText = "margin-bottom: 10px; transform: scale(1);";
+ assert_equals(rule.style.length, 1, "Should not have been able to set transform via cssText");
+ assert_equals(rule.style.marginBottom, "10px", "Should have a margin-bottom declaration");
+}, "@page");
+
+test(function() {
+ let rule = document.styleSheets[0].cssRules[1].cssRules[0];
+ assert_equals(rule.type, CSSRule.KEYFRAME_RULE, "Should be a keyframe rule");
+ assert_equals(rule.style.length, 1, "animation-name doesn't apply to keyframes");
+ assert_equals(rule.style.marginTop, "10px", "Should have a margin-top declaration");
+ rule.style.setProperty("animation-name", "none");
+ assert_equals(rule.style.getPropertyValue("animation-name"), "", "Shouldn't have been able to set the animation-nameproperty via setProperty");
+ assert_equals(rule.style.length, 1, "Shouldn't have been able to set animation-name via setProperty");
+ rule.style.cssText = "margin-bottom: 10px; animation-name: none;";
+ assert_equals(rule.style.length, 1, "Should not have been able to set animation-name via cssText");
+ assert_equals(rule.style.marginBottom, "10px", "Should have a margin-bottom declaration");
+
+}, "@keyframe");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/selectorSerialize.html b/testing/web-platform/tests/css/cssom/selectorSerialize.html
new file mode 100644
index 0000000000..ab48551816
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/selectorSerialize.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM Test: test serialized selector which is only one simple selector in the sequence of simple selectors</title>
+ <link rel="author" title="T.Nishitani" href="mailto:lequinharay@gmail.com">
+ <link rel="reviewer" title="L. David Baron" href="https://dbaron.org/">
+ <link rel="help" href="https://drafts.csswg.org/cssom-1/#serializing-selectors">
+ <meta name="flags" content="dom">
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style id="teststyles">
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ function assert_selector_serializes_to(source, expected_result) {
+ var style_element = document.getElementById("teststyles");
+ style_element.firstChild.data = source + "{ font-size: 1em; }";
+ var sheet = style_element.sheet;
+ assert_equals(sheet.cssRules[sheet.cssRules.length - 1].selectorText, expected_result);
+ }
+
+ function run_tests_on_anplusb_selector(source) {
+ assert_selector_serializes_to(source + '( even )', source + '(2n)');
+ assert_selector_serializes_to(source + '( odd )', source + '(2n+1)');
+ assert_selector_serializes_to(source + '( +10 )', source + '(10)');
+ assert_selector_serializes_to(source + '( -10 )', source + '(-10)');
+ assert_selector_serializes_to(source + '( +4n )', source + '(4n)');
+ assert_selector_serializes_to(source + '( -3n )', source + '(-3n)');
+ assert_selector_serializes_to(source + '( 1n + 5 )', source + '(n+5)');
+ assert_selector_serializes_to(source + '( -1n + 5 )', source + '(-n+5)');
+ assert_selector_serializes_to(source + '( -1n - 5 )', source + '(-n-5)');
+ }
+
+ test(function() {
+ assert_selector_serializes_to(":nth-child( 3n - 0)", ":nth-child(3n)");
+ assert_selector_serializes_to(":nth-child( 1n - 0)", ":nth-child(n)");
+ }, ":nth-child serialization produces canonical form");
+
+
+ /* for universal selecter with default namespace */
+ test(function(){
+ /* this is single universal selector */
+ assert_selector_serializes_to('*', '*');
+ assert_selector_serializes_to(' * ', '*');
+ }, 'single universal selector shows \'*\' when serialized.')
+
+ test(function(){
+ assert_selector_serializes_to('div', 'div');
+ assert_selector_serializes_to(' div ', 'div');
+ }, 'single type (simple) selector in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ assert_selector_serializes_to('.class', '.class');
+ assert_selector_serializes_to(' .class ', '.class');
+ }, 'single class (simple) selector in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ assert_selector_serializes_to('#id', '#id');
+ assert_selector_serializes_to(' #id ', '#id');
+ }, 'single id (simple) selector in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ assert_selector_serializes_to(':hover', ':hover');
+ assert_selector_serializes_to(' :hover ', ':hover');
+ }, 'single pseudo (simple) selector which does not accept arguments in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ assert_selector_serializes_to(':lang(ja)', ':lang(ja)');
+ assert_selector_serializes_to(':lang( ja )', ':lang(ja)');
+ assert_selector_serializes_to(':lang( j\\ a )', ':lang(j\\ a)');
+ }, 'single pseudo (simple) selector "lang" which accepts arguments in the sequence of simple selectors that is not a universal selector')
+
+
+ test(function(){
+ run_tests_on_anplusb_selector(':nth-child');
+ }, 'single pseudo (simple) selector "nth-child" which accepts arguments in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ run_tests_on_anplusb_selector(':nth-last-child');
+ }, 'single pseudo (simple) selector "nth-last-child" which accepts arguments in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ run_tests_on_anplusb_selector(':nth-of-type');
+ }, 'single pseudo (simple) selector "nth-of-child" which accepts arguments in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ run_tests_on_anplusb_selector(':nth-last-of-type');
+ }, 'single pseudo (simple) selector ":nth-last-of-type" which accepts arguments in the sequence of simple selectors that is not a universal selector')
+
+ test(function(){
+ assert_selector_serializes_to(' :not( abc ) ', ':not(abc)');
+ assert_selector_serializes_to(' :not( .head ) ', ':not(.head)');
+ assert_selector_serializes_to(' :not( #head ) ', ':not(#head)');
+ assert_selector_serializes_to(' :not( :hover ) ', ':not(:hover)');
+ }, 'single pseudo (simple) selector ":not" which accepts arguments in the sequence of simple selectors that is not a universal selector')
+
+ const escaped_ns_rule = "@namespace ns\\:odd url(ns);";
+ test(function() {
+ assert_selector_serializes_to("[ns\\:foo]", "[ns\\:foo]");
+ }, "escaped character in attribute name");
+ test(function() {
+ assert_selector_serializes_to("[\\30zonk]", "[\\30 zonk]");
+ }, "escaped character as code point in attribute name");
+ test(function() {
+ assert_selector_serializes_to("[\\@]", "[\\@]");
+ }, "escaped character (@) in attribute name");
+ test(function() {
+ assert_selector_serializes_to("[*|ns\\:foo]", "[*|ns\\:foo]");
+ }, "escaped character in attribute name with any namespace");
+ test(function() {
+ assert_selector_serializes_to(escaped_ns_rule + "[ns\\:odd|foo]", "[ns\\:odd|foo]");
+ }, "escaped character in attribute prefix");
+ test(function() {
+ assert_selector_serializes_to(escaped_ns_rule + "[ns\\:odd|odd\\:name]", "[ns\\:odd|odd\\:name]");
+ }, "escaped character in both attribute prefix and name");
+
+ test(() => {
+ assert_selector_serializes_to("\\\\", "\\\\");
+ }, "escaped character (\\) in element name");
+ test(() => {
+ assert_selector_serializes_to("*|\\\\", "\\\\");
+ }, "escaped character (\\) in element name with any namespace without default");
+ test(() => {
+ assert_selector_serializes_to("@namespace 'blah'; *|\\\\", "*|\\\\");
+ }, "escaped character (\\) in element name with any namespace with default");
+ test(() => {
+ assert_selector_serializes_to("|\\\\", "|\\\\");
+ }, "escaped character (\\) in element name with no namespace");
+
+ const element_escaped_ns_rule = "@namespace x\\* 'blah';";
+ test(() => {
+ assert_selector_serializes_to(element_escaped_ns_rule + "x\\*|test", "x\\*|test");
+ }, "escaped character (*) in element prefix");
+
+ // TODO: https://github.com/w3c/csswg-drafts/issues/8911
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html
new file mode 100644
index 0000000000..74e5807470
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001-ref.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>(Ref #1) CSSOM - CSSStyleRule.selectorText Modification Restyle - Reference #1</title>
+
+<style>
+div {
+ color: green;
+}
+</style>
+
+<body>
+<div>I should be green.</div>
+</body>
diff --git a/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html
new file mode 100644
index 0000000000..43a76d1ef2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-001.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>(Test #1) CSSOM - CSSStyleRule.selectorText Modification Restyle - Test #1</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext">
+<link rel="match" href="selectorText-modification-restyle-001-ref.html">
+
+<style>
+@namespace bogus url(http://example.com/bogus);
+
+bogus|div {
+ color: green;
+}
+</style>
+
+<body>
+<div>I should be green.</div>
+<script>
+// Remove the "bogus" namespace--now it should apply to the div above.
+// We also expect to see a restyle.
+document.querySelector("style").sheet.cssRules[1].selectorText = "div";
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html
new file mode 100644
index 0000000000..a6b37c2cf0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/selectorText-modification-restyle-002.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>CSSOM: Modify selectorText in a shadow tree stylesheet</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #container { color: red }
+ .subtree * { color: pink }
+</style>
+<div id="container">
+ <div id="host"></div>
+</div>
+<script>
+ const root = host.attachShadow({mode:"open"});
+ root.innerHTML = "<style>nomatch { color: green }</style><div>Green</div>";
+ const div = root.querySelector("div");
+
+ test(() => {
+ assert_equals(getComputedStyle(div).color, "rgb(255, 0, 0)", "Color should initial be red.");
+ }, "Check initial color.");
+
+ test(() => {
+ // The combination of the .subtree and CSSOM selector style invalidations
+ // caused the Blink implementation to fail an assertion.
+ container.className = "subtree";
+ root.styleSheets[0].cssRules[0].selectorText = "div";
+ assert_equals(getComputedStyle(div).color, "rgb(0, 128, 0)", "Color should be green after stylesheet change.");
+ }, "Check that color changes correctly after shadow stylesheet selector and #container class is changed.");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html b/testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html
new file mode 100644
index 0000000000..804885a74a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialization-CSSDeclaration-with-important.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>cssom - Serialization of CSS declaration with "important" flag</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="noWhitespace" style="display: inline !important;"></div>
+<div id="whitespace" style="background-color: blue !important; color: red ! important;"></div>
+<div id="dinamicallyStyle"></div>
+<script>
+ test(function () {
+ var css_style = document.querySelector('#noWhitespace').style.cssText;
+ assert_equals(css_style, "display: inline !important;");
+ }, "Inline style declaration without whitespace between '!' and 'important'.");
+
+ test(function () {
+ var css_style = document.querySelector('#whitespace').style.cssText;
+ assert_equals(css_style, "background-color: blue !important; color: red !important;");
+ }, "Inline style declaration with whitespace between '!' and 'important'.");
+
+ test(function () {
+ document.querySelector('#dinamicallyStyle').style.cssText = "color: black !important;";
+ var css_style = document.querySelector('#dinamicallyStyle').style.cssText;
+ assert_equals(css_style, "color: black !important;");
+ }, "Style set dynamically via cssText without whitespace between '!' and 'important'.");
+
+ test(function () {
+ document.querySelector('#dinamicallyStyle').style.cssText = "color: black ! important;";
+ var css_style = document.querySelector('#dinamicallyStyle').style.cssText;
+ assert_equals(css_style, "color: black !important;");
+ }, "Style set dynamically via cssText with whitespace between '!' and 'important'.");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/serialize-all-longhands.html b/testing/web-platform/tests/css/cssom/serialize-all-longhands.html
new file mode 100644
index 0000000000..95204b6361
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialize-all-longhands.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Serialize all longhands</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue">
+<meta name="assert" content="Checks that all longhands indexed in a CSSStyleDeclaration can be serialized to a non-empty string when set to their initial value.">
+
+<div id="target"></div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function nonSerializableProperties(style) {
+ const result = [];
+ assert_greater_than(style.length, 0, "Should have longhands");
+ for (let property of style) {
+ if (!style.getPropertyValue(property)) {
+ result.push(property);
+ }
+ }
+ return result;
+}
+
+const target = document.getElementById("target");
+target.style.cssText = "all: initial; direction: initial; unicode-bidi: initial;";
+
+test(function() {
+ const props = nonSerializableProperties(target.style);
+ assert_array_equals(props, []);
+}, "Specified style");
+
+test(function() {
+ const props = nonSerializableProperties(getComputedStyle(target));
+ assert_array_equals(props, []);
+}, "Computed style");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/serialize-custom-props.html b/testing/web-platform/tests/css/cssom/serialize-custom-props.html
new file mode 100644
index 0000000000..cfe96ff0aa
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialize-custom-props.html
@@ -0,0 +1,69 @@
+<link rel=author title="Tab Atkins-Bittner" href="https://www.xanthir.com/contact/">
+<link rel="help" href="https://drafts.csswg.org/css-values/#calc-range">
+<body><!doctype html>
+<title>Serializing Integers Never Uses Scinot</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<link rel=author title="Tab Atkins-Bittner" href="https://www.xanthir.com/contact/">
+<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-component-value">
+<!--
+ Per CSSOM, integers always serialize all their digits out.
+ They never serialize to scinot, regardless of size,
+ because that makes them stop being an integer.
+ This applies to custom properties as well.
+-->
+<body>
+
+<script>
+
+try {
+CSS.registerProperty({
+ name: "--two",
+ value: "<integer>",
+ inherits: true,
+ initial: -1
+});
+}catch(e){}
+
+testIntLength(4);
+testIntLength(6);
+testIntLength(8);
+testIntLength(12);
+// JS starts serializing with scinot at 22 digits...
+testIntLength(25);
+
+function testIntLength(len) {
+ let el = document.body;
+ const val = "1".repeat(len);
+ test(()=>{
+ el.removeAttribute("style");
+ const nullVal = getComputedStyle(el).zIndex;
+ el.style.zIndex=val;
+ assert_not_equals(getComputedStyle(el).zIndex, nullVal)
+ }, `z-index can take a ${len}-digit integer`);
+
+ test(()=>{
+ el.removeAttribute("style");
+ el.style.setProperty("--one", val);
+ assert_equals(getComputedStyle(el).getPropertyValue("--one"), val);
+ }, `An unregistered custom prop can take a ${len}-digit integer`);
+
+ test(()=>{
+ el.removeAttribute("style");
+ el.style.setProperty("--two", val);
+ assert_equals(getComputedStyle(el).getPropertyValue("--two"), val);
+ }, `An <integer> custom prop can take a ${len}-digit integer`);
+
+ test(()=>{
+ el.removeAttribute("style");
+ el.style.zIndex = val;
+ const standardVal = getComputedStyle(el).zIndex;
+ el.removeAttribute("style");
+ el.style.setProperty("--three", val);
+ el.style.zIndex = "var(--three)";
+ assert_equals(getComputedStyle(el).zIndex, standardVal);
+ }, `z-index can take a custom property set to a ${len}-digit integer`);
+}
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom/serialize-media-rule.html b/testing/web-platform/tests/css/cssom/serialize-media-rule.html
new file mode 100644
index 0000000000..90561fdf70
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialize-media-rule.html
@@ -0,0 +1,185 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - Serialization of CSSMediaRule</title>
+ <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>
+</head>
+<body>
+<script>
+function makeSheet(t) {
+ var style = document.createElement('style');
+ document.body.appendChild(style);
+ t.add_cleanup(function() {
+ document.body.removeChild(style);
+ });
+ return style.sheet;
+}
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media {}', 0);
+
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, '@media {\n}');
+}, 'empty media query list');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media all {}');
+ sheet.insertRule('@media print {}');
+ sheet.insertRule('@media screen {}');
+ sheet.insertRule('@media speech {}');
+
+ assert_equals(sheet.cssRules.length, 4);
+ assert_equals(sheet.cssRules[0].cssText, '@media speech {\n}');
+ assert_equals(sheet.cssRules[1].cssText, '@media screen {\n}');
+ assert_equals(sheet.cssRules[2].cssText, '@media print {\n}');
+ assert_equals(sheet.cssRules[3].cssText, '@media all {\n}');
+}, 'type - no features');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media not all {}');
+ sheet.insertRule('@media not print {}');
+ sheet.insertRule('@media not screen {}');
+ sheet.insertRule('@media not speech {}');
+
+ assert_equals(sheet.cssRules.length, 4);
+ assert_equals(sheet.cssRules[0].cssText, '@media not speech {\n}');
+ assert_equals(sheet.cssRules[1].cssText, '@media not screen {\n}');
+ assert_equals(sheet.cssRules[2].cssText, '@media not print {\n}');
+ assert_equals(sheet.cssRules[3].cssText, '@media not all {\n}');
+}, 'type - no features - negation');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media aLL {}');
+ sheet.insertRule('@media pRiNt {}');
+ sheet.insertRule('@media screEN {}');
+ sheet.insertRule('@media spEech {}');
+
+ assert_equals(sheet.cssRules.length, 4);
+ assert_equals(sheet.cssRules[0].cssText, '@media speech {\n}');
+ assert_equals(sheet.cssRules[1].cssText, '@media screen {\n}');
+ assert_equals(sheet.cssRules[2].cssText, '@media print {\n}');
+ assert_equals(sheet.cssRules[3].cssText, '@media all {\n}');
+}, 'type - no features - character case normalization');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media all and (color) {}');
+
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, '@media (color) {\n}');
+}, 'type - omission of all');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media not all and (color) {}');
+
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(sheet.cssRules[0].cssText, '@media not all and (color) {\n}');
+}, 'type - inclusion of negated all');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media screen and (Color) {}');
+ sheet.insertRule('@media screen and (cOLor) {}');
+
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules[0].cssText, '@media screen and (color) {\n}');
+ assert_equals(sheet.cssRules[1].cssText, '@media screen and (color) {\n}');
+}, 'features - character case normalization');
+
+/**
+ * The following test is disabled pending clarification of the intended
+ * behavior: https://github.com/w3c/csswg-drafts/issues/533
+ */
+//test(function(t) {
+// var sheet = makeSheet(t);
+// sheet.insertRule('@media screen and (color) and (color) {}');
+//
+// assert_equals(sheet.cssRules.length, 1);
+// assert_equals(
+// sheet.cssRules[0].cssText,
+// '@media screen and (color) {\n}'
+// );
+//}, 'features - de-duplication');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media print and (max-width: 23px) and (max-width: 45px) {}');
+
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(
+ sheet.cssRules[0].cssText,
+ '@media print and (max-width: 23px) and (max-width: 45px) {\n}'
+ );
+}, 'features - preservation of overspecified features');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media screen and (max-width: 0px) and (color) {}');
+ sheet.insertRule('@media screen and (color) and (max-width: 0px) {}');
+
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(
+ sheet.cssRules[0].cssText,
+ '@media screen and (color) and (max-width: 0px) {\n}'
+ );
+ assert_equals(
+ sheet.cssRules[1].cssText,
+ '@media screen and (max-width: 0px) and (color) {\n}'
+ );
+}, 'features - no lexicographical sorting');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media screen and (max-width: 0px), screen and (color) {}');
+
+ assert_equals(sheet.cssRules.length, 1);
+ assert_equals(
+ sheet.cssRules[0].cssText,
+ '@media screen and (max-width: 0px), screen and (color) {\n}'
+ );
+}, 'media query list');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media print {}');
+
+ assert_equals(sheet.cssRules.length, 1);
+ sheet.cssRules[0].insertRule('#foo { z-index: 23; float: left; }', 0);
+ assert_equals(
+ sheet.cssRules[0].cssText,
+ [
+ '@media print {',
+ ' #foo { z-index: 23; float: left; }',
+ '}'
+ ].join('\n')
+ );
+}, 'one rule');
+
+test(function(t) {
+ var sheet = makeSheet(t);
+ sheet.insertRule('@media print {}');
+
+ assert_equals(sheet.cssRules.length, 1);
+ sheet.cssRules[0].insertRule('#foo { z-index: 23; float: left; }', 0);
+ sheet.cssRules[0].insertRule('#bar { float: none; z-index: 45; }', 0);
+ assert_equals(
+ sheet.cssRules[0].cssText,
+ [
+ '@media print {',
+ ' #bar { float: none; z-index: 45; }',
+ ' #foo { z-index: 23; float: left; }',
+ '}'
+ ].join('\n')
+ );
+}, 'many rules');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html b/testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html
new file mode 100644
index 0000000000..600008c7a9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialize-namespaced-type-selectors.html
@@ -0,0 +1,257 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM Test: test serialization of type selectors and namespace prefixes</title>
+ <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom-1/#serializing-selectors">
+ <meta name="flags" content="dom">
+ <meta charset="utf-8">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style id="teststyles">
+ </style>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script>
+ var ns_rule = "@namespace ns url(ns);";
+ var default_ns_rules = "@namespace url(default_ns); @namespace nsdefault url(default_ns);" + ns_rule;
+
+ function assert_selector_serializes_to(source, expected_result) {
+ var style_element = document.getElementById("teststyles");
+ style_element.firstChild.data = source + "{ font-size: 1em; }";
+ var sheet = style_element.sheet;
+ assert_equals(sheet.cssRules[sheet.cssRules.length - 1].selectorText, expected_result);
+ }
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "e", "e");
+ assert_selector_serializes_to(default_ns_rules + "e", "e");
+ }, "Simple type selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|e", "|e");
+ assert_selector_serializes_to(default_ns_rules + "|e", "|e");
+ }, "Type selector without a namespace");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|e", "e");
+ assert_selector_serializes_to(default_ns_rules + "*|e", "*|e");
+ }, "Type selector with any namespace");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*", "*");
+ assert_selector_serializes_to(default_ns_rules + "*", "*");
+ }, "Universal selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|*", "|*");
+ assert_selector_serializes_to(default_ns_rules + "|*", "|*");
+ }, "Universal selector without a namespace");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|*", "*");
+ assert_selector_serializes_to(default_ns_rules + "*|*", "*|*");
+ }, "Universal selector in any namespace");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|e", "ns|e");
+ assert_selector_serializes_to(default_ns_rules + "ns|e", "ns|e");
+ }, "Type selector with namespace");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|*", "ns|*");
+ assert_selector_serializes_to(default_ns_rules + "ns|*", "ns|*");
+ }, "Universal selector with namespace");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "e.c", "e.c");
+ assert_selector_serializes_to(default_ns_rules + "e.c", "e.c");
+ }, "Simple type selector followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "e#i", "e#i");
+ assert_selector_serializes_to(default_ns_rules + "e#i", "e#i");
+ }, "Simple type selector followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "e:hover", "e:hover");
+ assert_selector_serializes_to(default_ns_rules + "e:hover", "e:hover");
+ }, "Simple type selector followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "e::before", "e::before");
+ assert_selector_serializes_to(default_ns_rules + "e::before", "e::before");
+ }, "Simple type selector followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "e[attr]", "e[attr]");
+ assert_selector_serializes_to(default_ns_rules + "e[attr]", "e[attr]");
+ }, "Simple type selector followed by atttribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|e.c", "|e.c");
+ assert_selector_serializes_to(default_ns_rules + "|e.c", "|e.c");
+ }, "Type selector without a namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|e#i", "|e#i");
+ assert_selector_serializes_to(default_ns_rules + "|e#i", "|e#i");
+ }, "Type selector without a namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|e:hover", "|e:hover");
+ assert_selector_serializes_to(default_ns_rules + "|e:hover", "|e:hover");
+ }, "Type selector without a namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|e::before", "|e::before");
+ assert_selector_serializes_to(default_ns_rules + "|e::before", "|e::before");
+ }, "Type selector without a namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|e[attr]", "|e[attr]");
+ assert_selector_serializes_to(default_ns_rules + "|e[attr]", "|e[attr]");
+ }, "Type selector without a namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|e.c", "e.c");
+ assert_selector_serializes_to(default_ns_rules + "*|e.c", "*|e.c");
+ }, "Type selector with any namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|e#id", "e#id");
+ assert_selector_serializes_to(default_ns_rules + "*|e#id", "*|e#id");
+ }, "Type selector with any namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|e:hover", "e:hover");
+ assert_selector_serializes_to(default_ns_rules + "*|e:hover", "*|e:hover");
+ }, "Type selector with any namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|e::before", "e::before");
+ assert_selector_serializes_to(default_ns_rules + "*|e::before", "*|e::before");
+ }, "Type selector with any namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|e[attr]", "e[attr]");
+ assert_selector_serializes_to(default_ns_rules + "*|e[attr]", "*|e[attr]");
+ }, "Type selector with any namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*.c", ".c");
+ assert_selector_serializes_to(default_ns_rules + "*.c", ".c");
+ }, "Universal selector followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*#i", "#i");
+ assert_selector_serializes_to(default_ns_rules + "*#i", "#i");
+ }, "Universal selector followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*:hover", ":hover");
+ assert_selector_serializes_to(default_ns_rules + "*:hover", ":hover");
+ }, "Universal selector followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*::before", "::before");
+ assert_selector_serializes_to(default_ns_rules + "*::before", "::before");
+ }, "Universal selector followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*[attr]", "[attr]");
+ assert_selector_serializes_to(default_ns_rules + "*[attr]", "[attr]");
+ }, "Universal selector followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|*.c", "|*.c");
+ assert_selector_serializes_to(default_ns_rules + "|*.c", "|*.c");
+ }, "Universal selector without a namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|*#i", "|*#i");
+ assert_selector_serializes_to(default_ns_rules + "|*#i", "|*#i");
+ }, "Universal selector without a namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|*:hover", "|*:hover");
+ assert_selector_serializes_to(default_ns_rules + "|*:hover", "|*:hover");
+ }, "Universal selector without a namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|*::before", "|*::before");
+ assert_selector_serializes_to(default_ns_rules + "|*::before", "|*::before");
+ }, "Universal selector without a namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "|*[attr]", "|*[attr]");
+ assert_selector_serializes_to(default_ns_rules + "|*[attr]", "|*[attr]");
+ }, "Universal selector without a namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|*.c", ".c");
+ assert_selector_serializes_to(default_ns_rules + "*|*.c", "*|*.c");
+ }, "Universal selector in any namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|*#id", "#id");
+ assert_selector_serializes_to(default_ns_rules + "*|*#id", "*|*#id");
+ }, "Universal selector in any namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|*:hover", ":hover");
+ assert_selector_serializes_to(default_ns_rules + "*|*:hover", "*|*:hover");
+ }, "Universal selector in any namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|*::before", "::before");
+ assert_selector_serializes_to(default_ns_rules + "*|*::before", "*|*::before");
+ }, "Universal selector in any namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "*|*[attr]", "[attr]");
+ assert_selector_serializes_to(default_ns_rules + "*|*[attr]", "*|*[attr]");
+ }, "Universal selector in any namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|e.c", "ns|e.c");
+ assert_selector_serializes_to(default_ns_rules + "ns|e.c", "ns|e.c");
+ }, "Type selector with namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|e#i", "ns|e#i");
+ assert_selector_serializes_to(default_ns_rules + "ns|e#i", "ns|e#i");
+ }, "Type selector with namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|e:hover", "ns|e:hover");
+ assert_selector_serializes_to(default_ns_rules + "ns|e:hover", "ns|e:hover");
+ }, "Type selector with namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|e::before", "ns|e::before");
+ assert_selector_serializes_to(default_ns_rules + "ns|e::before", "ns|e::before");
+ }, "Type selector with namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|e[attr]", "ns|e[attr]");
+ assert_selector_serializes_to(default_ns_rules + "ns|e[attr]", "ns|e[attr]");
+ }, "Type selector with namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|*.c", "ns|*.c");
+ assert_selector_serializes_to(default_ns_rules + "ns|*.c", "ns|*.c");
+ }, "Universal selector with namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|*#i", "ns|*#i");
+ assert_selector_serializes_to(default_ns_rules + "ns|*#i", "ns|*#i");
+ }, "Universal selector with namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|*:hover", "ns|*:hover");
+ assert_selector_serializes_to(default_ns_rules + "ns|*:hover", "ns|*:hover");
+ }, "Universal selector with namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|*::before", "ns|*::before");
+ assert_selector_serializes_to(default_ns_rules + "ns|*::before", "ns|*::before");
+ }, "Universal selector with namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(ns_rule + "ns|*[attr]", "ns|*[attr]");
+ assert_selector_serializes_to(default_ns_rules + "ns|*[attr]", "ns|*[attr]");
+ }, "Universal selector with namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|e", "e");
+ }, "Type selector with namespace equal to default namespace");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|*", "*");
+ }, "Universal selector with namespace equal to default namespace");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|e.c", "e.c");
+ }, "Type selector with namespace equal to default namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|e#i", "e#i");
+ }, "Type selector with namespace equal to default namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|e:hover", "e:hover");
+ }, "Type selector with namespace equal to default namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|e::before", "e::before");
+ }, "Type selector with namespace equal to default namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|e[attr]", "e[attr]");
+ }, "Type selector with namespace equal to default namespace followed by attribute selector");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|*.c", ".c");
+ }, "Universal selector with namespace equal to default namespace followed by class");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|*#i", "#i");
+ }, "Universal selector with namespace equal to default namespace followed by id");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|*:hover", ":hover");
+ }, "Universal selector with namespace equal to default namespace followed by pseudo class");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|*::before", "::before");
+ }, "Universal selector with namespace equal to default namespace followed by pseudo element");
+ test(function() {
+ assert_selector_serializes_to(default_ns_rules + "nsdefault|*[attr]", "[attr]");
+ }, "Universal selector with namespace equal to default namespace followed by attribute selector");
+ </script>
+ </body>
+</html>
+
diff --git a/testing/web-platform/tests/css/cssom/serialize-values.html b/testing/web-platform/tests/css/cssom/serialize-values.html
new file mode 100644
index 0000000000..2d20f263f4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialize-values.html
@@ -0,0 +1,614 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSSOM serialize values</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#serializing-css-values">
+<meta name="author" title="Josh Matthews" href="mailto:josh@joshmatthews.net">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <div id="log"></div>
+ <div id="parent"></div>
+ <script>
+ function iterable(values) {
+ var i = 0;
+ return function() {
+ if (i < values.length) {
+ return values[i++];
+ }
+ return null;
+ }
+ }
+
+ function color() {
+ var colors = ['black', 'red', 'rgb(50, 75, 100)', 'rgba(5, 7, 10, 0.5)'];
+ return iterable(colors);
+ }
+
+ function percentage() {
+ var values = ["5%", {actual: ".5%", serialized: "0.5%"}];
+ return iterable(values);
+ }
+
+ function negative_percentage() {
+ var values = ["-5%", {actual: "-.5%", serialized: "-0.5%"}];
+ return iterable(values);
+ }
+
+ function length() {
+ var values = ["0px", "1px", {actual: ".1em", serialized: "0.1em"}];
+ return iterable(values);
+ }
+
+ function negative_length() {
+ var values = [{actual: "-0px", serialized: "0px"},
+ "-1px", {actual: "-.1em", serialized: "-0.1em"}];
+ return iterable(values);
+ }
+
+ function degree() {
+ var values = ["87deg"];
+ return iterable(values);
+ }
+
+ function uri() {
+ var values = ["url(\"http://localhost/\")",
+ {actual: "url(http://localhost/)",
+ serialized: "url(\"http://localhost/\")"}];
+ return iterable(values);
+ }
+
+ function border_style() {
+ var values = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge',
+ 'inset', 'outset'];
+ return iterable(values);
+ }
+
+ function border_style_without_hidden() {
+ var values = ['none', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge',
+ 'inset', 'outset'];
+ return iterable(values);
+ }
+
+ function integer() {
+ var values = ['0', '101', '-51'];
+ return iterable(values);
+ }
+
+ function nonzero_positive_integer() {
+ var values = ['101'];
+ return iterable(values);
+ }
+
+ function shape() {
+ var values = ['rect(1em, auto, 0.5px, 2000em)'];
+ return iterable(values);
+ }
+
+ function string() {
+ var values = ['"string"', {actual: "'string'", serialized: '"string"'}];
+ return iterable(values);
+ }
+
+ function counter() {
+ var values = ['counter(par-num)',
+ { actual: 'counter(par-num, decimal)', serialized: 'counter(par-num)' },
+ 'counter(par-num, upper-roman)'];
+ return iterable(values);
+ }
+
+ function attr() {
+ var values = ['attr(foo-bar)', 'attr(foo_bar)',
+ {actual: "attr(|bar)", serialized: "attr(bar)"},
+ {actual: "attr( |bar )", serialized: "attr(bar)"}];
+ return iterable(values);
+ }
+
+ function attr_fallback() {
+ var values = ['attr(foo-bar, "fallback")', 'attr(foo_bar, "fallback")',
+ {actual: 'attr(|bar, "fallback")', serialized: 'attr(bar, "fallback")'},
+ {actual: 'attr(foo, "")', serialized: 'attr(foo)'},
+ {actual: 'attr( |foo , "" )', serialized: 'attr(foo)'}];
+ return iterable(values);
+ }
+
+ function family_name() {
+ var values = ['Arial', {actual: "'Lucida Grande'", serialized: '"Lucida Grande"'}];
+ return iterable(values);
+ }
+
+ function generic_family() {
+ var values = ['serif', 'sans-serif'];
+ return iterable(values);
+ }
+
+ function absolute_size() {
+ var values = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
+ return iterable(values);
+ }
+
+ function relative_size() {
+ var values = ['larger', 'smaller'];
+ return iterable(values);
+ }
+
+ function number() {
+ var values = ['0', {'actual': '-0', serialized: '0'}, '1000', '-5123', '0.9', '-0.09'];
+ return iterable(values);
+ }
+
+ function positive_number() {
+ var values = ['0', {'actual': '-0', serialized: '0'}, '1000', '0.9'];
+ return iterable(values);
+ }
+
+ function generate_inline_style(name, value) {
+ if (value) {
+ return {'declaration': name + ": " + value.actual,
+ 'value': value.actual,
+ 'result': value.expected};
+ }
+ return null;
+ }
+
+ var minimal_results = {
+ };
+
+ function create_result(propertyName, actual, expected) {
+ var key = propertyName + ": " + expected
+ if (key in minimal_results)
+ expected = minimal_results[key]
+ return {actual: actual, expected: expected}
+ }
+
+ function all_values(propertyName, values) {
+ var results = [];
+ for (var i = 0; i < values.length; i++) {
+ var value = values[i];
+ if (typeof value == "function") {
+ var f = value();
+ var result;
+ while ((result = f()) != null) {
+ if (typeof result == "object" && 'serialized' in result) {
+ results.push(create_result(propertyName, result.actual, result.serialized));
+ } else {
+ results.push(create_result(propertyName, result, result));
+ }
+ }
+ } else if (typeof value == "string") {
+ results.push(create_result(propertyName, value, value));
+ } else if (value instanceof Array) {
+ var subresults = [];
+ for (var j = 0; j < value.length; j++) {
+ var subresult = all_values(propertyName, value[j]);
+ if (!(subresult instanceof Array)) {
+ subresult = [subresult];
+ }
+ subresults.push(subresult);
+ }
+ if (subresults.length > 1) {
+ function choose_slices(vecs) {
+ if (vecs.length == 1) {
+ return vecs[0].map(function(v) { return [v]; });
+ }
+ var slice_results = [];
+ var rest = choose_slices(vecs.slice(1, vecs.length));
+ for (var a = 0; a < vecs[0].length; a++) {
+ for (var b = 0; b < rest.length; b++) {
+ var result = vecs[0][a];
+ slice_results.push([result].concat(rest[b]));
+ }
+ }
+ return slice_results;
+ }
+
+ subresults = choose_slices(subresults).map(function (a) {
+ var actual = a.map(function(a) { return a.actual });
+ var expected = a.map(function(a) { return a.expected });
+ return create_result(propertyName, actual.join(' '), expected.join(' '))
+ });
+ }
+ for (var j = 0; j < subresults.length; j++) {
+ results = results.concat(subresults[j]);
+ }
+ } else if (value instanceof Object && 'serialized' in value) {
+ results.push(create_result(propertyName, value.actual, value.serialized));
+ } else if (typeof value == "number") {
+ results.push(create_result(propertyName, value.toString(), value.toString()));
+ } else {
+ throw "unexpected value type: " + typeof(value);
+ }
+ }
+ return results;
+ }
+
+ function create_value_generator(propertyName, property) {
+ var results = all_values(propertyName, property.values);
+ return iterable(results);
+ }
+
+ function to_idl(property) {
+ return property.replace(/-\w/g, function(x){return x[1].toUpperCase()});
+ }
+
+ function run_individual_test(property, generator, initial) {
+ var elem = document.createElement('div');
+ document.getElementById('parent').appendChild(elem);
+ var test_data = generator();
+ var style = generate_inline_style(property, test_data);
+ if (!style) {
+ return false;
+ }
+ var t = async_test(style.declaration);
+
+ t.add_cleanup(function() {
+ document.getElementById('parent').removeChild(elem);
+ });
+
+ t.step(function() {
+ elem.setAttribute('style', style.declaration);
+ var expected = style.result;
+ var serialized = elem.style[to_idl(property)];
+ assert_equals(serialized, expected, property + ' raw inline style declaration');
+ elem.setAttribute('style', '');
+ elem.style[to_idl(property)] = style.value;
+ assert_equals(elem.style[to_idl(property)], expected, property + ' style property');
+ });
+ t.done();
+ return true;
+ }
+
+ function test_property(property) {
+ var generator = create_value_generator(property[0], property[1]);
+ while (run_individual_test(property[0], generator, property[1].initial)) {
+ }
+ }
+
+ var properties = [
+ ['background-attachment', {
+ 'values': ['scroll', 'fixed', 'inherit'],
+ 'initial': 'scroll',
+ }],
+ ['background-color', {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'transparent',
+ }],
+ ['background-image', {
+ 'values': [uri, 'none', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['background-position', {
+ 'values': [[[percentage, negative_percentage, length, negative_length,
+ 'left', 'center', 'right'],
+ [percentage, negative_percentage, length, negative_length,
+ 'top', 'center', 'bottom']],
+ 'inherit'],
+ 'initial': '0% 0%',
+ }],
+ ['background-repeat', {
+ 'values': ['repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'inherit'],
+ 'initial': 'repeat',
+ }],
+ //background
+ ['border-collapse', {
+ 'values': ['collapse', 'separate', 'inherit'],
+ 'initial': 'separate',
+ }],
+ //border-color
+ ['border-spacing', {
+ 'values': [length, 'inherit'],
+ 'initial': '0',
+ }],
+ //border-style
+ //border-top, border-right, border-bottom, border-left
+ ['border-top-color', {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ }],
+ ['border-right-color', {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ }],
+ ['border-bottom-color', {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ }],
+ ['border-left-color', {
+ 'values': [color, 'transparent', 'inherit'],
+ 'initial': 'black', //FIXME
+ }],
+ ['border-top-style', {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ }],
+ ['border-right-style', {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ }],
+ ['border-bottom-style', {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ }],
+ ['border-left-style', {
+ 'values': [border_style, 'inherit'],
+ 'initial': null,
+ }],
+ ['border-top-width', {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ }],
+ ['border-right-width', {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ }],
+ ['border-bottom-width', {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ }],
+ ['border-left-width', {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ }],
+ //border-width
+ //border
+ ['bottom', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['caption-side', {
+ 'values': ['top', 'bottom', 'inherit'],
+ 'initial': 'top',
+ }],
+ ['clear', {
+ 'values': ['none', 'left', 'right', 'both', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['clip', {
+ 'values': [shape, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['color', {
+ 'values': [color, 'inherit'],
+ 'initial': 'black', //FIXME depends on user agent
+ }],
+ ['content', {
+ 'values': ['normal', 'none', string, uri, counter, attr, attr_fallback, 'inherit'], //FIXME
+ 'initial': 'normal',
+ }],
+ //counter-increment
+ //counter-reset
+ ['cursor', {
+ 'values': [ 'auto', 'crosshair', 'default', 'pointer', 'move', 'e-resize', 'ne-resize',
+ 'nw-resize', 'n-resize', 'se-resize', 'sw-resize', 's-resize', 'w-resize',
+ 'text', 'wait', 'help', 'progress', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['direction', {
+ 'values': ['ltr', 'rtl', 'inherit'],
+ 'initial': 'ltr',
+ }],
+ ['display', {
+ 'values': ['inline', 'block', 'list-item', 'inline-block', 'table', 'inline-table',
+ 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row',
+ 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none',
+ 'inherit'],
+ 'initial': 'inline',
+ }],
+ ['empty-cells', {
+ 'values': ['show', 'hide', 'inherit'],
+ 'initial': 'show',
+ }],
+ ['float', {
+ 'values': ['left', 'right', 'none', 'inherit'],
+ 'initial': 'none',
+ 'property': 'cssFloat',
+ }],
+ ['font-family', {
+ 'values': [family_name, generic_family, 'inherit'],
+ 'initial': 'sans-serif', //FIXME depends on user agent
+ }],
+ ['font-size', {
+ 'values': [absolute_size, relative_size, length, percentage, 'inherit'],
+ 'initial': 'medium',
+ }],
+ ['font-style', {
+ 'values': ['normal', 'italic', 'oblique', 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['font-variant', {
+ 'values': ['normal', 'small-caps', 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['font-weight', {
+ 'values': ['normal', 'bold', 'bolder', 'lighter', 100, 200, 300, 400, 500, 600,
+ 700, 800, 900, 'inherit'],
+ 'initial': 'normal',
+ }],
+ //font
+ ['height', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['left', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['letter-spacing', {
+ 'values': ['normal', length, 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['line-height', {
+ 'values': ['normal', positive_number, length, percentage, 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['list-style-image', {
+ 'values': [uri, 'none', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['list-style-position', {
+ 'values': ['inside', 'outside', 'inherit'],
+ 'initial': 'outside',
+ }],
+ ['list-style-type', {
+ 'values': ['disc', 'circle', 'square', 'disclosure-open', 'disclosure-closed',
+ 'decimal', 'decimal-leading-zero', 'lower-roman',
+ 'upper-roman', 'lower-greek', 'lower-latin', 'upper-latin', 'armenian', 'georgian',
+ 'lower-alpha', 'upper-alpha', 'none', 'inherit'],
+ 'initial': 'disc',
+ }],
+ //list-style
+ ['margin-right', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ }],
+ ['margin-left', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ }],
+ ['margin-top', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ }],
+ ['margin-bottom', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 0,
+ }],
+ //margin
+ ['max-height', {
+ 'values': [length, percentage, 'none', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['max-width', {
+ 'values': [length, percentage, 'none', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['min-height', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ ['min-width', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ ['orphans', {
+ 'values': [nonzero_positive_integer, 'inherit'],
+ 'initial': 2,
+ }],
+ ['outline-color', {
+ 'values': [color, 'invert', 'inherit'],
+ 'initial': 'invert',
+ }],
+ ['outline-style', {
+ 'values': [border_style_without_hidden, 'inherit'],
+ 'initial': 'none',
+ }],
+ ['outline-width', {
+ 'values': ['thin', 'medium', 'thick', length, 'inherit'],
+ 'initial': 'medium',
+ }],
+ //outline
+ ['overflow', {
+ 'values': ['visible', 'hidden', 'scroll', 'auto', 'inherit'],
+ 'initial': 'visible',
+ }],
+ ['padding-top', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ ['padding-right', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ ['padding-bottom', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ ['padding-left', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ //padding
+ ['page-break-after', {
+ 'values': ['auto', 'always', 'avoid', 'left', 'right', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['page-break-before', {
+ 'values': ['auto', 'always', 'avoid', 'left', 'right', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['page-break-inside', {
+ 'values': ['avoid', 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['position', {
+ 'values': ['static', 'relative', 'absolute', 'fixed', 'inherit'],
+ 'initial': 'static',
+ }],
+ //FIXME quotes
+ ['right', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['table-layout', {
+ 'values': ['auto', 'fixed', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['text-align', {
+ 'values': ['left', 'right', 'center', 'justify', 'inherit'],
+ 'initial': null,
+ }],
+ ['text-decoration', {
+ 'values': ['none', 'underline', 'overline', 'line-through', 'blink', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['text-indent', {
+ 'values': [length, percentage, 'inherit'],
+ 'initial': 0,
+ }],
+ ['text-transform', {
+ 'values': ['capitalize', 'uppercase', 'lowercase', 'none', 'inherit'],
+ 'initial': 'none',
+ }],
+ ['top', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['unicode-bidi', {
+ 'values': ['normal', 'embed', 'bidi-override', 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['vertical-align', {
+ 'values': ['baseline', 'sub', 'super', 'top', 'text-top', 'middle', 'bottom', 'text-bottom',
+ percentage, length, 'inherit'],
+ 'initial': 'baseline',
+ }],
+ ['visibility', {
+ 'values': ['visible', 'hidden', 'collapse', 'inherit'],
+ 'initial': 'visible',
+ }],
+ ['white-space', {
+ 'values': ['normal', 'pre', 'nowrap', 'pre-wrap', 'pre-line', 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['widows', {
+ 'values': [nonzero_positive_integer, 'inherit'],
+ 'initial': 2,
+ }],
+ ['width', {
+ 'values': [length, percentage, 'auto', 'inherit'],
+ 'initial': 'auto',
+ }],
+ ['word-spacing', {
+ 'values': ['normal', length, 'inherit'],
+ 'initial': 'normal',
+ }],
+ ['z-index', {
+ 'values': ['auto', integer, 'inherit'],
+ 'initial': 'auto',
+ }],
+ ]
+
+ for (var index = 0; index < properties.length; index++) {
+ test_property(properties[index]);
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom/serialize-variable-reference.html b/testing/web-platform/tests/css/cssom/serialize-variable-reference.html
new file mode 100644
index 0000000000..1d3cbad9e6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/serialize-variable-reference.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM - Serialization with variable preserves original serialization.</title>
+<link rel="help" href="https://drafts.csswg.org/css-variables/#serializing-custom-props">
+<link rel="help" href="https://drafts.csswg.org/css-variables/#variables-in-shorthands">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="longhand-whitespace" style="font-size: var(--a);"></div>
+<div id="shorthand-whitespace" style="font: var(--a);"></div>
+<div id="longhand" style="font-size:var(--a);"></div>
+<div id="shorthand" style="font:var(--a);"></div>
+<script>
+ test(function() {
+ var elem = document.getElementById('longhand-whitespace');
+
+ assert_equals(elem.style.cssText, 'font-size: var(--a);');
+ }, 'Longhand with variable preserves original serialization: with whitespace')
+
+ test(function() {
+ var elem = document.getElementById('shorthand-whitespace');
+
+ assert_equals(elem.style.cssText, 'font: var(--a);');
+ }, 'Shorthand with variable preserves original serialization: with whitespace')
+
+ test(function() {
+ var elem = document.getElementById('longhand');
+
+ assert_equals(elem.style.cssText, 'font-size: var(--a);');
+ }, 'Longhand with variable preserves original serialization but trims whitespace: without whitespace')
+
+ test(function() {
+ var elem = document.getElementById('shorthand');
+
+ assert_equals(elem.style.cssText, 'font: var(--a);');
+ }, 'Shorthand with variable preserves original serialization but trims whitespace: without whitespace')
+</script>
diff --git a/testing/web-platform/tests/css/cssom/setproperty-null-undefined.html b/testing/web-platform/tests/css/cssom/setproperty-null-undefined.html
new file mode 100644
index 0000000000..3de142c0b4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/setproperty-null-undefined.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<html>
+<head>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+var style = document.body.style;
+
+test(function() {
+ style.color = 'white';
+
+ assert_equals(style.color, 'white');
+ style.setProperty('color', undefined);
+ assert_equals(style.color, 'white');
+}, "Verify that setting a CSS property to undefined has no effect.");
+
+test(function() {
+ style.color = 'white';
+
+ assert_equals(style.color, 'white');
+ assert_equals(style.getPropertyPriority('color'), '');
+ style.setProperty('color', 'red', undefined);
+ assert_equals(style.color, 'red');
+ assert_equals(style.getPropertyPriority('color'), '');
+}, "Verify that setting a CSS property priority to undefined is accepted.");
+
+test(function() {
+ style.color = 'white';
+
+ assert_equals(style.color, 'white');
+ style.setProperty('color', null);
+ assert_equals(style.color, '');
+}, "Verify that setting a CSS property to null is treated like empty string.");
+
+test(function() {
+ style.color = 'white';
+
+ assert_equals(style.color, 'white');
+ style.setProperty('color', 'red', null);
+ assert_equals(style.color, 'red');
+}, "Verify that setting a CSS property priority to null is treated like empty string.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/shorthand-serialization.html b/testing/web-platform/tests/css/cssom/shorthand-serialization.html
new file mode 100644
index 0000000000..97e11da8b8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/shorthand-serialization.html
@@ -0,0 +1,86 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Shorthand serialization should be done correctly.</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block">
+ <link rel="help" href="https://drafts.csswg.org/css-variables/#variables-in-shorthands">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="foo1" style="background: red;">foo</div>
+ <div id="foo2" style="background-color: blue; background: red !important; background-color: green;">foo</div>
+ <div id="foo3" style="background-color: blue; background: red; background-color: green !important;">foo</div>
+
+ <div id="foo4" style="margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;">foo</div>
+ <div id="foo5" style="margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;">foo</div>
+ <div id="foo6" style="margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;">foo</div>
+
+ <div id="foo7" style="background:var(--a);">foo</div>
+ <div id="test"></div>
+
+ <script>
+ test(function() {
+ var elem1 = document.getElementById('foo1');
+ var elem2 = document.getElementById('foo2');
+ var elem3 = document.getElementById('foo3');
+
+ assert_false(elem1.style.cssText.endsWith('!important;'));
+ assert_true(elem2.style.cssText.endsWith('!important;'));
+ assert_false(elem3.style.background.endsWith('!important'));
+
+ }, "Shorthand serialization with shorthand and longhands mixed.");
+
+ test(function() {
+ var elem4 = document.getElementById('foo4');
+ var elem5 = document.getElementById('foo5');
+ var elem6 = document.getElementById('foo6');
+
+ assert_equals(elem4.style.cssText, 'margin: 10px;');
+ assert_equals(elem4.style.margin, '10px');
+ assert_equals(elem5.style.cssText, 'margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;');
+ assert_equals(elem5.style.margin, '');
+ assert_equals(elem6.style.cssText, 'margin: 10px !important;');
+ assert_equals(elem6.style.margin, '10px');
+ }, "Shorthand serialization with just longhands.");
+
+ test(function() {
+ var elem7 = document.getElementById('foo7');
+
+ assert_equals(elem7.style.background, 'var(--a)');
+ assert_equals(elem7.style.backgroundPosition, '');
+ }, "Shorthand serialization with variable and variable from other shorthand.");
+
+ test(function() {
+ var testElem = document.getElementById("test");
+ testElem.style.margin = "20px 20px 20px 20px";
+ assert_equals(testElem.style.margin, "20px");
+ assert_equals(testElem.style.cssText, "margin: 20px;")
+ }, "Shorthand serialization after setting");
+
+ test(function() {
+ const testElem = document.getElementById("test");
+ testElem.style.cssText = "margin: initial;";
+ assert_equals(testElem.style.margin, "initial");
+ assert_equals(testElem.style.cssText, "margin: initial;");
+ }, "Shorthand serialization with 'initial' value.");
+
+ test(function() {
+ const testElem = document.getElementById("test");
+ testElem.style.setProperty("margin-top", "initial", "important");
+ assert_equals(testElem.style.margin, "");
+ }, "Shorthand serialization with 'initial' value, one longhand with important flag.");
+
+ test(function() {
+ const testElem = document.getElementById("test");
+ testElem.style.cssText = "";
+ testElem.style.setProperty("margin-top", "initial");
+ testElem.style.setProperty("margin-right", "initial");
+ testElem.style.setProperty("margin-bottom", "initial");
+ testElem.style.setProperty("margin-left", "initial", "important");
+ assert_equals(testElem.style.margin, "");
+ }, "Shorthand serialization with 'initial' value, longhands set individually, one with important flag.");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/shorthand-values.html b/testing/web-platform/tests/css/cssom/shorthand-values.html
new file mode 100644
index 0000000000..f831cba2ca
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/shorthand-values.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<head>
+ <title>CSS OM: CSS Values</title>
+ <link rel="author" title="Divya Manian" href="mailto:manian@adobe.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="Testing Serialization of Shorthand Values">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="test"></div>
+ <script>
+ function test_shorthand_serialization(value, expected) {
+ test(function() {
+ const div = document.getElementById("test");
+ div.style.cssText = value;
+ assert_equals(div.style.cssText, expected);
+ }, "The serialization of " + value + " should be canonical.");
+ }
+
+ var tests = {
+ // specified -> expected
+ 'border: 1px; border-top: 1px;': 'border: 1px;',
+ 'border: 1px solid red;': 'border: 1px solid red;',
+ 'border: 1px red;': 'border: 1px red;',
+ 'border: red;': 'border: red;',
+ 'border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;': 'border: 1px;',
+ 'border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px;': 'border-width: 1px; border-style: none; border-color: currentcolor;',
+ 'border-top: 1px; border-right: 2px; border-bottom: 3px; border-left: 4px;': 'border-width: 1px 2px 3px 4px; border-style: none; border-color: currentcolor;',
+ 'border: 1px; border-top: 2px;': 'border-width: 2px 1px 1px; border-style: none; border-color: currentcolor; border-image: none;',
+ 'border: 1px; border-top: 1px !important;': 'border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;',
+ 'border: 1px; border-top-color: red;': 'border-width: 1px; border-style: none; border-color: red currentcolor currentcolor; border-image: none;',
+ 'border: solid; border-style: dotted': 'border: dotted;',
+ 'border-width: 1px;': 'border-width: 1px;',
+ 'overflow-x: scroll; overflow-y: hidden;': 'overflow: scroll hidden;',
+ 'overflow-x: scroll; overflow-y: scroll;': 'overflow: scroll;',
+ 'outline-width: 2px; outline-style: dotted; outline-color: blue;': 'outline: blue dotted 2px;',
+ 'margin-top: 1px; margin-right: 2px; margin-bottom: 3px; margin-left: 4px;': 'margin: 1px 2px 3px 4px;',
+ 'list-style-type: circle; list-style-position: inside; list-style-image: none;': 'list-style: inside circle;',
+ 'list-style-type: lower-alpha;': 'list-style-type: lower-alpha;',
+ 'font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold;': 'font-family: sans-serif; line-height: 2em; font-size: 3em; font-style: italic; font-weight: bold;',
+ 'padding-top: 1px; padding-right: 2px; padding-bottom: 3px; padding-left: 4px;': 'padding: 1px 2px 3px 4px;'
+ }
+
+ for (let test in tests) {
+ test_shorthand_serialization(test, tests[test]);
+ }
+ </script>
+ </body>
+ </html>
diff --git a/testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html b/testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html
new file mode 100644
index 0000000000..2b8f443520
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/style-attr-update-across-documents.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>CSSStyleDeclaration setter works when a node changes document</title>
+<link rel="author" title="Raphael Kubo da Costa" href="raphael.kubo.da.costa@intel.com">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-camel-cased-attribute">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-webkit-cased-attribute">
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-dashed-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="original-div" style="background-color:green; height: 100px; width: 100px"></div>
+<script>
+ // Create and return a 100x100 red <div>.
+ function createDiv(containerDocument) {
+ const div = containerDocument.createElement("div");
+ div.style.backgroundColor = "red";
+ div.style.height = "100px";
+ div.style.width = "100px";
+ containerDocument.body.appendChild(div);
+ return div;
+ }
+
+ async_test(t => {
+ const iframeElement = document.createElement("iframe");
+ iframeElement.addEventListener("load", t.step_func_done(() => {
+ const originalDiv = document.getElementById("original-div");
+ const newDiv = createDiv(iframeElement.contentDocument);
+
+ assert_not_equals(newDiv.ownerDocument, document,
+ "The new <div> belongs to the iframe, not the main document");
+
+ document.body.insertBefore(newDiv, originalDiv);
+ assert_equals(newDiv.ownerDocument, document,
+ "The new <div> now belongs to the main document");
+
+ newDiv.style.backgroundColor = "blue";
+ assert_equals(window.getComputedStyle(newDiv).getPropertyValue("background-color"),
+ "rgb(0, 0, 255)",
+ "The new <div>'s background-color is blue");
+
+ document.body.removeChild(iframeElement);
+
+ newDiv.style.backgroundColor = "green";
+ assert_equals(window.getComputedStyle(newDiv).getPropertyValue("background-color"),
+ "rgb(0, 128, 0)",
+ "The new <div>'s background-color is green");
+ }));
+ document.body.appendChild(iframeElement);
+ }, "Changing the style of a node that switched documents works");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html
new file mode 100644
index 0000000000..07c4fbb41a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-001.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: CSSOM StyleSheet Initial Values</title>
+ <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com">
+ <link rel="help" href="https://www.w3.org/TR/cssom-1/#css-style-sheets">
+ <link rel="help" href="https://www.w3.org/TR/cssom-1/#the-cssimportrule-interface">
+ <link rel="help" href="https://www.w3.org/TR/cssom-1/#the-linkstyle-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="StyleSheet and CSSStyleSheet objects have the properties specified in their interfaces">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style id="styleElement" type="text/css" media="all" title="internal style sheet" disabled="disabled">
+ @import url('support/a-green.css');
+ * { margin: 0; padding: 0; }
+ </style>
+ <link id="linkElement" rel="stylesheet" href="support/b-green.css">
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+ var styleElement = document.getElementById("styleElement");
+ var linkElement = document.getElementById("linkElement");
+
+ var styleSheet;
+ var linkSheet;
+
+ // styleElement.sheet exists and is a CSSStyleSheet
+ // linkElement.sheet exists and is a CSSStyleSheet
+ test(function() {
+ assert_idl_attribute(styleElement, "sheet");
+ assert_readonly(styleElement, "sheet");
+ styleSheet = styleElement.sheet;
+ assert_true(styleSheet instanceof CSSStyleSheet);
+ assert_idl_attribute(linkElement, "sheet");
+ linkSheet = linkElement.sheet;
+ assert_true(linkSheet instanceof CSSStyleSheet);
+ }, "sheet_property");
+
+ // The `disabled` IDL attribute setter has no effect before the underlying
+ // CSSStyleSheet is created.
+ test(function () {
+ const style = document.createElement("style");
+ style.disabled = true;
+ assert_false(style.disabled);
+ document.head.append(style);
+ const sheet1 = style.sheet;
+ assert_false(style.disabled);
+ assert_false(sheet1.disabled);
+
+ // The `disabled` attribute can be set after the sheet exists.
+ style.disabled = true;
+ assert_true(style.disabled);
+ assert_true(sheet1.disabled);
+
+ // Reset `disabled`.
+ style.disabled = false;
+ assert_false(style.disabled);
+ assert_false(style.disabled);
+
+ // Setting `disabled = true` on the CSSStyleSheet also carries over to the
+ // HTMLStyleElement.
+ sheet1.disabled = true;
+ assert_true(style.disabled);
+ assert_true(sheet1.disabled);
+ }, "disabled attribute getter/setter");
+
+ // The sheet property on LinkStyle should always return the current associated style sheet.
+ test(function () {
+ var style = document.createElement("style");
+ document.querySelector("head").appendChild(style);
+ var sheet1 = style.sheet;
+ assert_equals(sheet1.cssRules.length, 0);
+ style.appendChild(document.createTextNode("a { color: green; }"));
+ assert_equals(style.sheet.cssRules.length, 1);
+ }, "sheet_property_updates");
+
+ // ownerRule, cssRules, insertRule and deleteRule properties exist on CSSStyleSheet
+ // ownerRule, cssRules are read only
+ test(function() {
+ assert_idl_attribute(styleSheet, "ownerRule");
+ assert_idl_attribute(styleSheet, "cssRules");
+ assert_inherits(styleSheet, "insertRule");
+ assert_inherits(styleSheet, "deleteRule");
+
+ assert_readonly(styleSheet, "ownerRule");
+ assert_readonly(styleSheet, "cssRules");
+ }, "CSSStyleSheet_properties");
+
+ var importSheet;
+ // CSSStyleSheet initial property values are correct
+ test(function() {
+ assert_equals(styleSheet.ownerRule, null);
+ assert_true(styleSheet.cssRules.length > 0);
+ assert_true(styleSheet.cssRules.item(0) instanceof CSSImportRule);
+ importSheet = styleSheet.cssRules.item(0).styleSheet;
+ }, "CSSStyleSheet_property_values");
+
+ // type, disabled, ownerNode, parentStyleSheet, href, title, and media properties exist on StyleSheet
+ // type, ownerNode, parentStyleSheet, href, and title properties are read only
+ test(function() {
+ assert_idl_attribute(styleSheet, "type");
+ assert_idl_attribute(styleSheet, "disabled");
+ assert_idl_attribute(styleSheet, "ownerNode");
+ assert_idl_attribute(styleSheet, "parentStyleSheet");
+ assert_idl_attribute(styleSheet, "href");
+ assert_idl_attribute(styleSheet, "title");
+ assert_idl_attribute(styleSheet, "media");
+
+ assert_readonly(styleSheet, "type");
+ assert_readonly(styleSheet, "ownerNode");
+ assert_readonly(styleSheet, "parentStyleSheet");
+ assert_readonly(styleSheet, "href");
+ assert_readonly(styleSheet, "title");
+ }, "StyleSheet_properties");
+
+ // StyleSheet initial property values are correct
+ test(function() {
+ assert_equals(styleSheet.type, "text/css");
+ assert_equals(styleSheet.disabled, false);
+
+ assert_equals(styleSheet.ownerNode, styleElement);
+ assert_equals(linkSheet.ownerNode, linkElement);
+ assert_equals(importSheet.ownerNode, null);
+
+ assert_equals(styleSheet.href, null);
+ assert_regexp_match(linkSheet.href, /support\/b-green.css$/);
+ assert_regexp_match(importSheet.href, /support\/a-green.css$/);
+
+ assert_equals(styleSheet.parentStyleSheet, null);
+ assert_equals(linkSheet.parentStyleSheet, null);
+ assert_equals(importSheet.parentStyleSheet, styleSheet);
+
+ assert_equals(styleSheet.title, "internal style sheet");
+ assert_equals(styleSheet.media.item(0), "all");
+ }, "StyleSheet_property_values");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html
new file mode 100644
index 0000000000..e86a9a16e3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/style-sheet-interfaces-002.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: CSSOM StyleSheet Modify Rule List</title>
+ <link rel="author" title="Bear Travis" href="mailto:betravis@adobe.com">
+ <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 -->
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssstylesheet-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-cssrule-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="StyleSheet and CSSStyleSheet objects have the properties specified in their interfaces">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style id="styleElement" type="text/css" media="all" title="internal style sheet" disabled="disabled">
+ * { margin: 0; padding: 0; }
+ </style>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+ var sheet = document.getElementById("styleElement").sheet;
+ // Initial rule list is of size 1
+ // Can add a rule at first index
+ test(function() {
+ assert_equals(sheet.cssRules.length, 1);
+ sheet.insertRule("p { color: green; }", 0);
+ assert_equals(sheet.cssRules.length, 2);
+ assert_equals(sheet.cssRules.item(0).cssText, "p { color: green; }");
+ }, "add_rule");
+
+ // Can delete rules until rule list is empty
+ test(function() {
+ sheet.deleteRule(0);
+ assert_equals(sheet.cssRules.length, 1);
+ sheet.deleteRule(0);
+ assert_equals(sheet.cssRules.length, 0);
+ }, "delete_rule");
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/stylesheet-dom-mutation-event-crash.html b/testing/web-platform/tests/css/cssom/stylesheet-dom-mutation-event-crash.html
new file mode 100644
index 0000000000..942660c7a5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/stylesheet-dom-mutation-event-crash.html
@@ -0,0 +1,13 @@
+<script>
+function go() {
+ b.innerHTML = undefined
+}
+document.addEventListener("DOMContentLoaded", () => {
+ b.appendChild(document.createTextNode(""))
+ a.addEventListener("DOMNodeRemoved", go)
+ go();
+})
+</script>
+<div id="a">
+<style id="b">
+</style>
diff --git a/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html
new file mode 100644
index 0000000000..bc9cadebf1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic-ref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>(Ref #1) CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@mozilla.com">
+<link rel="author" title="Cheng You Bai" href="mailto:cyb.ai.815@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-characterdata-replacedata">
+<style>.pass { color: green }</style>
+<div class="pass">Should be green</div> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html
new file mode 100644
index 0000000000..3ee5937c95
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/stylesheet-replacedata-dynamic.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>(Test #1) CSS Test: Dynamic changes to the stylesheet contents using replaceData are reflected</title>
+<link rel="match" href="stylesheet-replacedata-dynamic-ref.html">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@mozilla.com">
+<link rel="author" title="Cheng You Bai" href="mailto:cyb.ai.815@gmail.com">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-characterdata-replacedata">
+<style>.fail { color: green }</style>
+<div class="pass">Should be green</div>
+<script>
+ document.body.offsetTop;
+ document.querySelector('style').firstChild.replaceData(1, 4, "pass");
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/stylesheet-same-origin.css b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.css
new file mode 100644
index 0000000000..d61a8dd67c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.css
@@ -0,0 +1,3 @@
+body {
+ padding: 10px;
+}
diff --git a/testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html
new file mode 100644
index 0000000000..6ad55190da
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/stylesheet-same-origin.sub.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSSOM - CSSStylesheet should support origins</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#the-cssstylesheet-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <link id="crossorigin" href="http://www1.{{host}}:{{ports[http][1]}}/css/cssom/stylesheet-same-origin.css" rel="stylesheet">
+ <link id="sameorigin" href="stylesheet-same-origin.css" rel="stylesheet">
+ <link id="sameorigindata" href="data:text/css,.green-text{color:rgb(0, 255, 0)}" rel="stylesheet">
+ <link id="redirect-sameorigin-to-crossorigin"
+ href="/common/redirect.py?location=http://www1.{{host}}:{{ports[http][1]}}/css/cssom/stylesheet-same-origin.css"
+ rel="stylesheet">
+ <link id="redirect-crossorigin-to-sameorigin"
+ href="http://www1.{{host}}:{{ports[http][1]}}/common/redirect.py?location=http://{{host}}:{{ports[http][0]}}/css/cssom/stylesheet-same-origin.css"
+ rel="stylesheet">
+ <link id="loaderror" href="support/malformed-http-response.asis" rel="stylesheet">
+
+ <script>
+ var crossorigin = document.getElementById("crossorigin").sheet;
+ var redirectSameOriginToCrossOrigin = document.getElementById("redirect-sameorigin-to-crossorigin").sheet;
+ var redirectCrossOriginToSameOrigin = document.getElementById("redirect-crossorigin-to-sameorigin").sheet;
+ var loadError = document.getElementById("loaderror").sheet;
+ var sameorigin = document.getElementById("sameorigin").sheet;
+ var sameorigindata = document.getElementById("sameorigindata").sheet;
+
+ function doOriginCleanCheck(sheet, name) {
+ assert_equals(sheet.cssRules.length, 1, name + " stylesheet.cssRules should be accessible.");
+ sheet.insertRule("#test { margin: 10px; }", 1);
+ assert_equals(sheet.cssRules.length, 2, name + " stylesheet.insertRule should be accessible.");
+ sheet.deleteRule(0);
+ assert_equals(sheet.cssRules.length, 1, name + " stylesheet.deleteRule should be accessible.");
+ }
+
+ function doOriginDirtyCheck(sheet) {
+ assert_throws_dom("SecurityError",
+ function () {
+ sheet.cssRules;
+ },
+ 'stylesheet.cssRules should throw SecurityError.');
+ assert_throws_dom("SecurityError",
+ function () {
+ sheet.insertRule("#test { margin: 10px; }", 1);
+ },
+ 'stylesheet.insertRule should throw SecurityError.');
+
+ assert_throws_dom("SecurityError",
+ function () {
+ sheet.deleteRule(0);
+ },
+ 'stylesheet.deleteRule should throw SecurityError.');
+ }
+
+ test(function() {
+ doOriginDirtyCheck(crossorigin);
+ }, "Origin-clean check in cross-origin CSSOM Stylesheets");
+
+ test(function() {
+ doOriginDirtyCheck(redirectSameOriginToCrossOrigin);
+ }, "Origin-clean check in cross-origin CSSOM Stylesheets (redirect from same-origin to cross-origin)");
+
+ test(function() {
+ doOriginDirtyCheck(redirectCrossOriginToSameOrigin);
+ }, "Origin-clean check in cross-origin CSSOM Stylesheets (redirect from cross-origin to same-origin)");
+
+ test(function() {
+ doOriginDirtyCheck(loadError);
+ }, "Origin-clean check in loading error CSSOM Stylesheets");
+
+ test(function() {
+ doOriginCleanCheck(sameorigin, "Same-origin");
+ }, "Origin-clean check in same-origin CSSOM Stylesheets");
+
+ test(function() {
+ doOriginCleanCheck(sameorigindata, "data:css");
+ }, "Origin-clean check in data:css CSSOM Stylesheets");
+
+ </script>
+</head>
+<body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/stylesheet-title.html b/testing/web-platform/tests/css/cssom/stylesheet-title.html
new file mode 100644
index 0000000000..77b1df9677
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/stylesheet-title.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: StyleSheet's title attribute</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://drafts.csswg.org/cssom/#preferred-css-style-sheet-set-name">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-style-title">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style></style>
+<style title=""></style>
+<style title="Preferred">
+ p { color: green; }
+</style>
+<style title="Not preferred">
+ p { color: red; }
+</style>
+<p id="test-element">Should be green</p>
+<script>
+test(function() {
+ assert_equals(
+ getComputedStyle(document.getElementById("test-element")).color,
+ "rgb(0, 128, 0)",
+ "Preferred style should apply"
+ );
+}, "Preferred style sheet name");
+
+test(function() {
+ let sheets = document.styleSheets;
+ let styleElements = Array.from(document.querySelectorAll("style"));
+ assert_equals(sheets.length, styleElements.length);
+ for (let i = 0; i < sheets.length; ++i) {
+ let titleAttr = styleElements[i].getAttribute("title");
+ if (titleAttr === null || titleAttr === "")
+ assert_equals(sheets[i].title, null, "Empty title returns null");
+ else
+ assert_equals(sheets[i].title, titleAttr, "Selected title is properly reflected");
+ }
+}, "StyleSheet.title");
+</script>
diff --git a/testing/web-platform/tests/css/cssom/support/1x1-green.png b/testing/web-platform/tests/css/cssom/support/1x1-green.png
new file mode 100644
index 0000000000..b98ca0ba0a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/1x1-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/1x1-lime.png b/testing/web-platform/tests/css/cssom/support/1x1-lime.png
new file mode 100644
index 0000000000..cb397fb090
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/1x1-lime.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/1x1-maroon.png b/testing/web-platform/tests/css/cssom/support/1x1-maroon.png
new file mode 100644
index 0000000000..3f86b07219
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/1x1-maroon.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/1x1-navy.png b/testing/web-platform/tests/css/cssom/support/1x1-navy.png
new file mode 100644
index 0000000000..9b9a03955b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/1x1-navy.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/1x1-red.png b/testing/web-platform/tests/css/cssom/support/1x1-red.png
new file mode 100644
index 0000000000..6bd73ac101
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/1x1-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/1x1-white.png b/testing/web-platform/tests/css/cssom/support/1x1-white.png
new file mode 100644
index 0000000000..dd43faec54
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/1x1-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/60x60-gg-rr.png b/testing/web-platform/tests/css/cssom/support/60x60-gg-rr.png
new file mode 100644
index 0000000000..84f5b2a4f1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/60x60-gg-rr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/60x60-green.png b/testing/web-platform/tests/css/cssom/support/60x60-green.png
new file mode 100644
index 0000000000..b3c8cf3eb4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/60x60-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/README b/testing/web-platform/tests/css/cssom/support/README
new file mode 100644
index 0000000000..2e5f2ad073
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/README
@@ -0,0 +1,28 @@
+CSS Global Support Directory
+============================
+
+This directory contains common support files (such as images and external
+style sheets). These are sync'ed into the support directories of all our
+test suites. If you have test-suite-specific support files, please add
+them to the appropriate test-suite-specific support/ directory.
+
+If you add to a support/ directory, please run the tools/supportprop.py
+script from the top of the repository to cascade support files into the
+lower-level support directories.
+
+Description of the Common Support File Collection
+-------------------------------------------------
+
+The 1x1-* images are all exactly one pixel.
+
+The swatch-* images all use 15x15 cells.
+
+The square-* images all use 15x15 cells with one pixel borders.
+
+The pattern-* images use cells of various sizes:
+
+ pattern-grg-rgr-grg.png 20x20
+ pattern-rgr-grg-rgr.png 20x20
+ pattern-tr.png 15x15
+ pattern-grg-rrg-rgg.png 15x15
+
diff --git a/testing/web-platform/tests/css/cssom/support/a-green.css b/testing/web-platform/tests/css/cssom/support/a-green.css
new file mode 100644
index 0000000000..b0dbb071d5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/a-green.css
@@ -0,0 +1 @@
+.a { color: green; }
diff --git a/testing/web-platform/tests/css/cssom/support/b-green.css b/testing/web-platform/tests/css/cssom/support/b-green.css
new file mode 100644
index 0000000000..a0473f5ca2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/b-green.css
@@ -0,0 +1 @@
+.b { color: green; } \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/support/black.css b/testing/web-platform/tests/css/cssom/support/black.css
new file mode 100644
index 0000000000..833a0e0092
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/black.css
@@ -0,0 +1,4 @@
+html {
+background-color: black;
+color: white;
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/support/c-red.css b/testing/web-platform/tests/css/cssom/support/c-red.css
new file mode 100644
index 0000000000..d4ba5c64e9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/c-red.css
@@ -0,0 +1 @@
+.c { color: red; } \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/support/cat.png b/testing/web-platform/tests/css/cssom/support/cat.png
new file mode 100644
index 0000000000..85dd732481
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/cat.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/constructable-import.css b/testing/web-platform/tests/css/cssom/support/constructable-import.css
new file mode 100644
index 0000000000..d8ccc9d491
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/constructable-import.css
@@ -0,0 +1,3 @@
+* {
+ color: red;
+}
diff --git a/testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js b/testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js
new file mode 100644
index 0000000000..723990cafb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/getComputedStyle-insets.js
@@ -0,0 +1,375 @@
+export const testEl = document.createElement("div");
+export const containerForInflow = document.createElement("div");
+export const containerForAbspos = document.createElement("div");
+export const containerForFixed = document.createElement("div");
+
+testEl.id = "test";
+containerForInflow.id = "container-for-inflow";
+containerForAbspos.id = "container-for-abspos";
+containerForFixed.id = "container-for-fixed";
+
+containerForInflow.appendChild(testEl);
+containerForAbspos.appendChild(containerForInflow);
+containerForFixed.appendChild(containerForAbspos);
+document.body.appendChild(containerForFixed);
+
+const stylesheet = document.createElement("style");
+stylesheet.textContent = `
+ #container-for-inflow {
+ /* Content area: 100px tall, 200px wide */
+ height: 100px;
+ width: 200px;
+ padding: 1px 2px;
+ border-width: 2px 4px;
+ margin: 4px 8px;
+ }
+ #container-for-abspos {
+ /* Padding area: 200px tall, 400px wide */
+ height: 184px;
+ width: 368px;
+ padding: 8px 16px;
+ border-width: 16px 32px;
+ margin: 32px 64px;
+ position: relative;
+ }
+ #container-for-fixed {
+ /* Padding area: 300px tall, 600px wide */
+ height: 172px;
+ width: 344px;
+ padding: 64px 128px;
+ border-width: 128px 256px;
+ margin: 256px 512px;
+ position: absolute;
+ transform: scale(1);
+ visibility: hidden;
+ }
+ [id ^= container] {
+ border-style: solid;
+ }
+`;
+document.head.prepend(stylesheet);
+
+function runTestsWithWM(data, testWM, cbWM) {
+ const {
+ style,
+ containingBlockElement,
+ containingBlockArea,
+ preservesPercentages,
+ preservesAuto,
+ canStretchAutoSize,
+ staticPositionX,
+ staticPositionY,
+ } = data;
+
+ let cbHeight = containingBlockElement ? containingBlockElement.clientHeight : NaN;
+ let cbWidth = containingBlockElement ? containingBlockElement.clientWidth : NaN;
+ if (containingBlockElement && containingBlockArea == "content") {
+ const cs = getComputedStyle(containingBlockElement);
+ cbHeight -= parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
+ cbWidth -= parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);
+ }
+
+ const staticPositionTop = cbWM.blockStart == "top" || cbWM.inlineStart == "top"
+ ? staticPositionY : cbHeight - staticPositionY;
+ const staticPositionLeft = cbWM.blockStart == "left" || cbWM.inlineStart == "left"
+ ? staticPositionX : cbWidth - staticPositionX;
+ const staticPositionBottom = cbWM.blockStart == "bottom" || cbWM.inlineStart == "bottom"
+ ? staticPositionY : cbHeight - staticPositionY;
+ const staticPositionRight = cbWM.blockStart == "right" || cbWM.inlineStart == "right"
+ ? staticPositionX : cbWidth - staticPositionX;
+
+ function serialize(declarations) {
+ return Object.entries(declarations).map(([p, v]) => `${p}: ${v}; `).join("");
+ }
+
+ function wmName(wm) {
+ return Object.values(wm.style).join(" ");
+ }
+
+ function checkStyle(declarations, expected, msg) {
+ test(function() {
+ testEl.style.cssText = style + "; " + serialize(Object.assign({}, declarations, testWM.style));
+ if (containingBlockElement) {
+ containingBlockElement.style.cssText = serialize(Object.assign({}, cbWM.style));
+ }
+ const cs = getComputedStyle(testEl);
+ for (let [prop, value] of Object.entries(expected)) {
+ assert_equals(cs[prop], value, `'${prop}'`);
+ }
+ }, `${wmName(testWM)} inside ${wmName(cbWM)} - ${msg}`);
+
+ testEl.style.cssText = "";
+ if (containingBlockElement) {
+ containingBlockElement.style.cssText = "";
+ }
+ }
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ }, "Pixels resolve as-is");
+
+ checkStyle({
+ top: "1em",
+ left: "2em",
+ bottom: "3em",
+ right: "4em",
+ "font-size": "10px",
+ }, {
+ top: "10px",
+ left: "20px",
+ bottom: "30px",
+ right: "40px",
+ }, "Relative lengths are absolutized into pixels");
+
+ if (preservesPercentages) {
+ checkStyle({
+ top: "10%",
+ left: "25%",
+ bottom: "50%",
+ right: "75%",
+ }, {
+ top: "10%",
+ left: "25%",
+ bottom: "50%",
+ right: "75%",
+ }, "Percentages resolve as-is");
+ } else {
+ checkStyle({
+ top: "10%",
+ left: "25%",
+ bottom: "50%",
+ right: "75%",
+ }, {
+ top: cbHeight * 10 / 100 + "px",
+ left: cbWidth * 25 / 100 + "px",
+ bottom: cbHeight * 50 / 100 + "px",
+ right: cbWidth * 75 / 100 + "px",
+ }, "Percentages are absolutized into pixels");
+
+ checkStyle({
+ top: "calc(10% - 1px)",
+ left: "calc(25% - 2px)",
+ bottom: "calc(50% - 3px)",
+ right: "calc(75% - 4px)",
+ }, {
+ top: cbHeight * 10 / 100 - 1 + "px",
+ left: cbWidth * 25 / 100 - 2 + "px",
+ bottom: cbHeight * 50 / 100 - 3 + "px",
+ right: cbWidth * 75 / 100 - 4 + "px",
+ }, "calc() is absolutized into pixels");
+ }
+
+ if (canStretchAutoSize) {
+ // Force overconstraintment by setting size or with insets that would result in
+ // negative size. Then the resolved value should be the computed one according to
+ // https://drafts.csswg.org/cssom/#resolved-value-special-case-property-like-top
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ height: "0px",
+ width: "0px",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "3px",
+ right: "4px",
+ }, "Pixels resolve as-is when overconstrained");
+
+ checkStyle({
+ top: "100%",
+ left: "100%",
+ bottom: "100%",
+ right: "100%",
+ }, {
+ top: cbHeight + "px",
+ left: cbWidth + "px",
+ bottom: cbHeight + "px",
+ right: cbWidth + "px",
+ }, "Percentages absolutize the computed value when overconstrained");
+ }
+
+ if (preservesAuto) {
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, "If start side is 'auto' and end side is not, 'auto' resolves as-is");
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, "If end side is 'auto' and start side is not, 'auto' resolves as-is");
+
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, "If opposite sides are 'auto', they resolve as-is");
+ } else if (canStretchAutoSize) {
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: cbHeight - 3 + "px",
+ left: cbWidth - 4 + "px",
+ bottom: "3px",
+ right: "4px",
+ }, "If start side is 'auto' and end side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: cbHeight - 1 + "px",
+ right: cbWidth - 2 + "px",
+ }, "If end side is 'auto' and start side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: staticPositionTop + "px",
+ left: staticPositionLeft + "px",
+ bottom: staticPositionBottom + "px",
+ right: staticPositionRight + "px",
+ }, "If opposite sides are 'auto', they resolve to used value");
+ } else {
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "3px",
+ right: "4px",
+ }, {
+ top: "-3px",
+ left: "-4px",
+ bottom: "3px",
+ right: "4px",
+ }, "If start side is 'auto' and end side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "1px",
+ left: "2px",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "1px",
+ left: "2px",
+ bottom: "-1px",
+ right: "-2px",
+ }, "If end side is 'auto' and start side is not, 'auto' resolves to used value");
+
+ checkStyle({
+ top: "auto",
+ left: "auto",
+ bottom: "auto",
+ right: "auto",
+ }, {
+ top: "0px",
+ left: "0px",
+ bottom: "0px",
+ right: "0px",
+ }, "If opposite sides are 'auto', they resolve to used value");
+ }
+}
+
+const writingModes = [{
+ style: {
+ "writing-mode": "horizontal-tb",
+ "direction": "ltr",
+ },
+ blockStart: "top",
+ blockEnd: "bottom",
+ inlineStart: "left",
+ inlineEnd: "right",
+}, {
+ style: {
+ "writing-mode": "horizontal-tb",
+ "direction": "rtl",
+ },
+ blockStart: "top",
+ blockEnd: "bottom",
+ inlineStart: "right",
+ inlineEnd: "left",
+}, {
+ style: {
+ "writing-mode": "vertical-lr",
+ "direction": "ltr",
+ },
+ blockStart: "left",
+ blockEnd: "right",
+ inlineStart: "top",
+ inlineEnd: "bottom",
+}, {
+ style: {
+ "writing-mode": "vertical-lr",
+ "direction": "rtl",
+ },
+ blockStart: "left",
+ blockEnd: "right",
+ inlineStart: "bottom",
+ inlineEnd: "top",
+}, {
+ style: {
+ "writing-mode": "vertical-rl",
+ "direction": "ltr",
+ },
+ blockStart: "right",
+ blockEnd: "left",
+ inlineStart: "top",
+ inlineEnd: "bottom",
+}, {
+ style: {
+ "writing-mode": "vertical-rl",
+ "direction": "rtl",
+ },
+ blockStart: "right",
+ blockEnd: "left",
+ inlineStart: "bottom",
+ inlineEnd: "top",
+}];
+
+export function runTests(data) {
+ for (let testWM of writingModes) {
+ for (let cbWM of writingModes) {
+ runTestsWithWM(data, testWM, cbWM);
+ }
+ }
+}
diff --git a/testing/web-platform/tests/css/cssom/support/import-charset.css b/testing/web-platform/tests/css/cssom/support/import-charset.css
new file mode 100644
index 0000000000..9f44090c94
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/import-charset.css
@@ -0,0 +1 @@
+@charset "UTF-8";
diff --git a/testing/web-platform/tests/css/cssom/support/import-green.css b/testing/web-platform/tests/css/cssom/support/import-green.css
new file mode 100644
index 0000000000..537104e663
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/import-green.css
@@ -0,0 +1 @@
+.import { color: green; }
diff --git a/testing/web-platform/tests/css/cssom/support/import-red.css b/testing/web-platform/tests/css/cssom/support/import-red.css
new file mode 100644
index 0000000000..9945ef4711
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/import-red.css
@@ -0,0 +1 @@
+.import { color: red; }
diff --git a/testing/web-platform/tests/css/cssom/support/import-rule.css b/testing/web-platform/tests/css/cssom/support/import-rule.css
new file mode 100644
index 0000000000..5edcef7e69
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/import-rule.css
@@ -0,0 +1 @@
+@import "import-red.css";
diff --git a/testing/web-platform/tests/css/cssom/support/malformed-http-response.asis b/testing/web-platform/tests/css/cssom/support/malformed-http-response.asis
new file mode 100644
index 0000000000..bc3c68d46d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/malformed-http-response.asis
@@ -0,0 +1 @@
+HAHAHA THIS IS NOT HTTP AND THE BROWSER SHOULD CONSIDER IT A NETWORK ERROR
diff --git a/testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.png b/testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.png
new file mode 100644
index 0000000000..9b88fbd811
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/pattern-grg-rgr-grg.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.png b/testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.png
new file mode 100644
index 0000000000..fcf4f3fd7d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/pattern-grg-rrg-rgg.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.png b/testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.png
new file mode 100644
index 0000000000..d454e3a630
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/pattern-rgr-grg-rgr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/pattern-tr.png b/testing/web-platform/tests/css/cssom/support/pattern-tr.png
new file mode 100644
index 0000000000..8b4b25364e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/pattern-tr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/ruler-h-50%.png b/testing/web-platform/tests/css/cssom/support/ruler-h-50%.png
new file mode 100644
index 0000000000..cf2eea6b43
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/ruler-h-50%.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/ruler-h-50px.png b/testing/web-platform/tests/css/cssom/support/ruler-h-50px.png
new file mode 100644
index 0000000000..9f46583665
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/ruler-h-50px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/ruler-v-100px.png b/testing/web-platform/tests/css/cssom/support/ruler-v-100px.png
new file mode 100644
index 0000000000..a837eca222
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/ruler-v-100px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/ruler-v-50px.png b/testing/web-platform/tests/css/cssom/support/ruler-v-50px.png
new file mode 100644
index 0000000000..8414102802
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/ruler-v-50px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/square-purple.png b/testing/web-platform/tests/css/cssom/support/square-purple.png
new file mode 100644
index 0000000000..0f522d7872
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/square-purple.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/square-teal.png b/testing/web-platform/tests/css/cssom/support/square-teal.png
new file mode 100644
index 0000000000..e567f51b91
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/square-teal.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/square-white.png b/testing/web-platform/tests/css/cssom/support/square-white.png
new file mode 100644
index 0000000000..5853cbb238
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/square-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/support/README b/testing/web-platform/tests/css/cssom/support/support/README
new file mode 100644
index 0000000000..ea8cb9ef35
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/support/README
@@ -0,0 +1,4 @@
+The swatch-green.png file in this directory is really a RED swatch,
+and the swatch-red.png file is really a green swatch.
+
+This directory is used to test relative URIs. \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom/support/support/swatch-green.png b/testing/web-platform/tests/css/cssom/support/support/swatch-green.png
new file mode 100644
index 0000000000..1caf25c992
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/support/swatch-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/support/swatch-red.png b/testing/web-platform/tests/css/cssom/support/support/swatch-red.png
new file mode 100644
index 0000000000..0aa79b0c86
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/support/swatch-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-blue.png b/testing/web-platform/tests/css/cssom/support/swatch-blue.png
new file mode 100644
index 0000000000..bf2759634d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-blue.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-green.png b/testing/web-platform/tests/css/cssom/support/swatch-green.png
new file mode 100644
index 0000000000..0aa79b0c86
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-lime.png b/testing/web-platform/tests/css/cssom/support/swatch-lime.png
new file mode 100644
index 0000000000..55fd7fdaed
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-lime.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-orange.png b/testing/web-platform/tests/css/cssom/support/swatch-orange.png
new file mode 100644
index 0000000000..d3cd498b52
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-orange.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-red.png b/testing/web-platform/tests/css/cssom/support/swatch-red.png
new file mode 100644
index 0000000000..1caf25c992
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-teal.png b/testing/web-platform/tests/css/cssom/support/swatch-teal.png
new file mode 100644
index 0000000000..0293ce89de
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-teal.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-white.png b/testing/web-platform/tests/css/cssom/support/swatch-white.png
new file mode 100644
index 0000000000..1a7d4323d7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/swatch-yellow.png b/testing/web-platform/tests/css/cssom/support/swatch-yellow.png
new file mode 100644
index 0000000000..1591aa0e2e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/swatch-yellow.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/test-bl.png b/testing/web-platform/tests/css/cssom/support/test-bl.png
new file mode 100644
index 0000000000..904e24e996
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/test-bl.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/test-br.png b/testing/web-platform/tests/css/cssom/support/test-br.png
new file mode 100644
index 0000000000..f413ff5c1a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/test-br.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/test-inner-half-size.png b/testing/web-platform/tests/css/cssom/support/test-inner-half-size.png
new file mode 100644
index 0000000000..e473bf80ef
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/test-inner-half-size.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/test-outer.png b/testing/web-platform/tests/css/cssom/support/test-outer.png
new file mode 100644
index 0000000000..82eeace7fc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/test-outer.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/test-tl.png b/testing/web-platform/tests/css/cssom/support/test-tl.png
new file mode 100644
index 0000000000..f6ac0ef7e8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/test-tl.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/test-tr.png b/testing/web-platform/tests/css/cssom/support/test-tr.png
new file mode 100644
index 0000000000..59843ae54b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/test-tr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml b/testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml
new file mode 100644
index 0000000000..8e22b50b25
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/support/xmlss-pi.xhtml
@@ -0,0 +1 @@
+<?xml-stylesheet href='data:text/css,'?><html xmlns='http://www.w3.org/1999/xhtml'/>
diff --git a/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html
new file mode 100644
index 0000000000..5c2b9e6f46
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-count.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM - Extensions to the Document Interface: StyleSheetList length reflects dynamically loaded and unloaded sheets</title>
+ <link rel="author" title="Jesse Bounds" href="mailto:jesse@codeforamerica.org">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-document-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-stylesheetlist-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-style-sheet-collections">
+ <link rel="stylesheet" href="stylesheet.css" type="text/css">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="The styleSheets length attribute must reflect the number of sheets at page load and after dynamically">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <script>
+
+ // Get the Document's styleSheets attribute
+ var styleSheets = document.styleSheets;
+
+ // Verify that the styleSheets list length is 1 due to "stylesheet.css" loaded in head section
+ test(function() {
+ assert_equals(styleSheets.length, 1, "styleSheets.length is incorrect:");
+ }, "stylesheet.css should be loaded and styleSheets.length === 1");
+
+ // Verify that the styleSheets list length is 0 after removing the loaded sheet from the DOM
+ test(function() {
+
+ // get the one and only sheet loaded
+ var sheet = styleSheets.item(0);
+
+ // remove the sheet from the DOM
+ sheet.ownerNode.parentNode.removeChild(sheet.ownerNode)
+
+ // assert that there are 0 styleSheets in the styleSheets property
+ assert_equals(styleSheets.length, 0, "styleSheets.length is incorrect:");
+
+ }, "stylesheet.css should be unloaded and styleSheets.length === 0");
+
+ // Verify that the styleSheets list length is back to 1 after loading a new sheet
+ async_test(function(t) {
+
+ // create a css file reference
+ var fileReference = document.createElement("link");
+ fileReference.setAttribute("rel", "stylesheet");
+ fileReference.setAttribute("type", "text/css");
+ fileReference.setAttribute("href", "stylesheet-1.css");
+ fileReference.onerror = t.step_func_done(function() {
+ // assert that there is 1 styleSheet in the styleSheets property
+ assert_equals(styleSheets.length, 1, "styleSheets.length is incorrect:");
+ });
+
+ // load the css file reference into the head section
+ var head = document.getElementsByTagName("HEAD")[0];
+ head.appendChild(fileReference);
+ }, "stylesheet-1.css should be loaded and styleSheets.length === 1");
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html
new file mode 100644
index 0000000000..2824401cb2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/ttwf-cssom-doc-ext-load-tree-order.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM - Extensions to the Document Interface: Stylesheet header load order</title>
+ <link rel="author" title="Jesse Bounds" href="mailto:jesse@codeforamerica.org">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-document-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-stylesheetlist-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#css-style-sheet-collections">
+ <style title="aaa" type="text/css">
+ H1 {border-width: 1; border: solid; text-align: center}
+ </style>
+ <link title="zebra" rel="stylesheet" href="zebra.css" type="text/css">
+ <link title="kilo" rel="stylesheet" href="kilo.css" type="text/css">
+ <link title="alpha" rel="stylesheet" href="alpha.css" type="text/css">
+ <link title="zebra" rel="stylesheet" href="/directory01/zebra.css" type="text/css">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="Document's style sheets created from HTTP Link headers are first in list and loaded in header order">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <script>
+ onload = function() {
+ // Get the Document's styleSheets attribute
+ var styleSheets = document.styleSheets;
+
+ // Verify that the styleSheets list length is 5
+ test(function() {
+ assert_equals(styleSheets.length, 5, "styleSheets.length is incorrect:");
+ }, "styleSheets.length must be 5");
+
+ // Verify that titles of loaded sheets are as expected (in the correct order)
+ test(function() {
+ assert_equals(styleSheets.item(0).title, "aaa", "title for item 1 is incorrect:");
+ }, "styleSheets item 0 title must be aaa");
+ // Verify that titles of loaded sheets are as expected (in the correct order)
+ test(function() {
+ assert_equals(styleSheets.item(1).title, "zebra", "title for item 1 is incorrect:");
+ }, "styleSheets item 1 title must be zebra");
+ // Verify that titles of loaded sheets are as expected (in the correct order)
+ test(function() {
+ assert_equals(styleSheets.item(2).title, "kilo", "title for item 1 is incorrect:");
+ }, "styleSheets item 0 title must be kilo");
+ // Verify that titles of loaded sheets are as expected (in the correct order)
+ test(function() {
+ assert_equals(styleSheets.item(3).title, "alpha", "title for item 1 is incorrect:");
+ }, "styleSheets item 0 title must be alpha");
+ // Verify that titles of loaded sheets are as expected (in the correct order)
+ test(function() {
+ assert_equals(styleSheets.item(4).title, "zebra", "title for item 1 is incorrect:");
+ }, "styleSheets item 0 title must be zebra");
+ };
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html b/testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html
new file mode 100644
index 0000000000..0c9116dc4b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/ttwf-cssom-document-extension.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM - Extensions to the Document Interface: StyleSheetList length is 0 when no sheets loaded</title>
+ <link rel="author" title="Jesse Bounds" href="mailto:jesse@codeforamerica.org">
+ <link rel="reviewer" title="Ms2ger" href="mailto:ms2ger@gmail.com"> <!-- 2012-06-17 -->
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#extensions-to-the-document-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#the-stylesheetlist-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="The styleSheets attribute must return a StyleSheetList sequence representing the document style sheets.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="log"></div>
+ <script>
+ // Get the Document's styleSheets attribute
+ var styleSheets = document.styleSheets;
+ // Verify that the styleSheets list length is 0 because no stylesheets have been loaded
+ test(function() {
+ assert_equals(styleSheets.length, 0, "styleSheets.length is incorrect:");
+ })
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom/variable-names.html b/testing/web-platform/tests/css/cssom/variable-names.html
new file mode 100644
index 0000000000..5591411e6e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/variable-names.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests for handling of CSS Custom Property names</title>
+<link rel="help" href="https://drafts.csswg.org/css-variables/#serializing-custom-props">
+<meta name="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+
+// Valid custom property names, before and after CSS escaping.
+var valid_names = [
+ ["--a", "--a"],
+ ["--a;b", "--a\\;b"],
+ ["---", "---"],
+ ["--\\", "--\\\\"],
+ ["--ab", "--\\61 b"],
+ ["--0", "--\\30 "],
+];
+
+valid_names.forEach(function(t) {
+ var name = t[0];
+ var escaped_name = t[1];
+
+ test(function() {
+ var e = document.createElement("span");
+ e.style = escaped_name + ":value";
+
+ for (var after_refeeding = 0; after_refeeding <= 1; ++after_refeeding) {
+ var desc_suffix = (after_refeeding ? " (after " : " (before ") +
+ "serialization/re-parsing)";
+
+ assert_equals(e.style.length, 1,
+ "appears on specified style" + desc_suffix);
+
+ assert_equals(e.style[0], name,
+ "name returned correctly from specified " +
+ "style indexed getter" + desc_suffix);
+
+ assert_equals(e.style.getPropertyValue(name), "value",
+ "property value returned correctly from " +
+ "specified style getPropertyValue" + desc_suffix);
+
+ e.style = e.style.cssText;
+ }
+ }, "custom property '" + name + "'");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom/xml-stylesheet-pi-in-doctype.xhtml b/testing/web-platform/tests/css/cssom/xml-stylesheet-pi-in-doctype.xhtml
new file mode 100644
index 0000000000..59911eb52c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/xml-stylesheet-pi-in-doctype.xhtml
@@ -0,0 +1,17 @@
+<!DOCTYPE html [<?xml-stylesheet href="data:text/css,html{z-index: 1}"?>]>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>xml-stylesheet processing instruction in doctype internal subset</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom/#prolog"/>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <script>
+ promise_test(async () => {
+ await new Promise(resolve => window.onload = resolve);
+ assert_equals(getComputedStyle(document.documentElement).zIndex, "auto");
+ });
+ </script>
+ </body>
+</html>