Skip to content
Permalink
Browse files
Fix CSS.supports behaviour with regards to !important and whitespace
Fix CSS.supports behaviour with regards to !important and whitespace
https://bugs.webkit.org/show_bug.cgi?id=248450

Reviewed by Darin Adler.

This patch is to align Webkit with Blink / Chromium, Gecko / Firefox and Web-Specification.

Web-Spec:

[1] https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface
[2] https://drafts.csswg.org/cssom-1/#dom-cssstyledeclaration-setpropertyvalue

Merge - https://chromium.googlesource.com/chromium/blink/+/36890765b05b48d8065737682b4e878645b601b2

This patch changes some small issues in CSS.supports:

1) Whitespace in CSS properties are not allowed. This aligns with FF and
the spec.
2) !important in a value is not removed. The !important flag is not part
of a CSS value, so something like CSS.supports("left","10px !important")
should return false. This is aligned with Web-Spec:
https://drafts.csswg.org/css-conditional-3/#the-css-namespace

>> Note: !important flags are not part of property grammars, and will cause value to parse
as invalid, just as they would in the value argument to element.style.setProperty().

More Details - w3c/csswg-drafts#5692

3) Newlines are not turned into spaces. These are not equivalent in CSS,
as newlines will cause strings to become bad string tokens.

* Source/WebCore/css/DOMCSSNamespace.cpp:
(valeWithoutImportant): Removed
(DOMCSSNamespace::supports): Update to remove "whitespace" and return 'value' rather than 'normalizedValue'
* LayoutTests/css3/supports-dom-api.html: Rebaselined
* LayoutTests/css3/supports-dom-api-expected.txt: Rebaselined

Canonical link: https://commits.webkit.org/257313@main
  • Loading branch information
Ahmad-S792 authored and darinadler committed Dec 3, 2022
1 parent bf5f525 commit 30880fe8446d7f010017a987501ef31e4643b2a5
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 43 deletions.
@@ -4,13 +4,13 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE


PASS CSS.supports("display: none") is true
PASS CSS.supports("(display: none)") is true
PASS CSS.supports(" display: none ") is true
PASS CSS.supports("(display: none)") is true
PASS CSS.supports("(display: deadbeef)") is false
PASS CSS.supports("display: deadbeef") is false
PASS CSS.supports("(display: none) and ((display: block) or (display: inline))") is true
PASS CSS.supports("(not (display: deadbeef)) and (display: block)") is true
PASS CSS.supports("top: -webkit-calc(80% - 20px)") is true
PASS CSS.supports("top: calc(80% - 20px)") is true
PASS CSS.supports("background-color: rgb(0, 128, 0)") is true
PASS CSS.supports("background: url('/blah')") is true
PASS CSS.supports("background: invalid('/blah')") is false
@@ -39,7 +39,6 @@ PASS CSS.supports("(display: none)or(-webkit-transition: all 1s)") is false
PASS CSS.supports("(display: none) or(-webkit-transition: all 1s )") is false
PASS CSS.supports("(((((((display: none)))))))") is true
PASS CSS.supports("(!important)") is false
PASS CSS.supports("!important") is false
PASS CSS.supports("not not not not (display: none)") is false
PASS CSS.supports("(top: -webkit-calc(80% - 20px))") is true
PASS CSS.supports("(background-color: rgb(0, 128, 0))") is true
@@ -52,18 +51,19 @@ PASS CSS.supports("top", "20") is false
PASS CSS.supports("dis\nplay", "none") is false
PASS CSS.supports("display", "rainbow") is false
PASS CSS.supports("display", "url(rainbow.png)") is false
PASS CSS.supports(" background ", "url(rainbow.png)") is true
PASS CSS.supports(" background ", "url(rainbow.png)") is false
PASS CSS.supports("background", "invalid(rainbow.png)") is false
PASS CSS.supports("background-color", "#000") is true
PASS CSS.supports("background-color", "rgba(0,0,0,0)") is true
PASS CSS.supports("background-color", "inherit") is true
PASS CSS.supports("display", "none !important") is true
PASS CSS.supports("display", "none!important") is true
PASS CSS.supports("font-family", "'\n'") is false
PASS CSS.supports("display", "none !important") is false
PASS CSS.supports("display", "none!important") is false
PASS CSS.supports("display", "!important none") is false
PASS CSS.supports("display", "none !important ! important ") is false
PASS CSS.supports("display", "none ! important") is true
PASS CSS.supports("display", "none ! important") is true
PASS CSS.supports("display", "none ! \nimportant") is true
PASS CSS.supports("display", "none ! important") is false
PASS CSS.supports("display", "none ! important") is false
PASS CSS.supports("display", "none ! \nimportant") is false
PASS CSS.supports("", "") is false
PASS CSS.supports(true, "") is false
PASS CSS.supports([], "none") is false
@@ -73,6 +73,65 @@ PASS CSS.supports("z-index", 1) is true
PASS CSS.supports("content", []) is false
PASS CSS.supports("content", "!important") is false
PASS CSS.supports("(display: none)", undefined) is false
PASS CSS.supports("margin", "1__qem") is false
PASS CSS.supports("border", "1px solid #000") is true
PASS CSS.supports("border", "inherit") is true
PASS CSS.supports("border", "1px solid inherit") is false
PASS CSS.supports("border", "inherit solid #000") is false
PASS CSS.supports("border-bottom", "thick green") is true
PASS CSS.supports("border-bottom", "inherit") is true
PASS CSS.supports("border-bottom", "thick green inherit") is false
PASS CSS.supports("border-bottom", "inherit thick green") is false
PASS CSS.supports("border-left", "thick green") is true
PASS CSS.supports("border-left", "inherit") is true
PASS CSS.supports("border-left", "thick green inherit") is false
PASS CSS.supports("border-left", "inherit thick green") is false
PASS CSS.supports("border-right", "thick green") is true
PASS CSS.supports("border-right", "inherit") is true
PASS CSS.supports("border-right", "thick green inherit") is false
PASS CSS.supports("border-right", "inherit thick green") is false
PASS CSS.supports("border-top", "thick green") is true
PASS CSS.supports("border-top", "inherit") is true
PASS CSS.supports("border-top", "thick green inherit") is false
PASS CSS.supports("border-top", "inherit thick green") is false
PASS CSS.supports("border-radius", "1px 0 3px 4px") is true
PASS CSS.supports("border-radius", "inherit") is true
PASS CSS.supports("border-radius", "1px 0 3px inherit") is false
PASS CSS.supports("border-radius", "inherit 0 3px 4px") is false
PASS CSS.supports("border-spacing", "5px 5px") is true
PASS CSS.supports("border-spacing", "inherit") is true
PASS CSS.supports("border-spacing", "5px inherit") is false
PASS CSS.supports("border-spacing", "inherit 5px") is false
PASS CSS.supports("font", "italic small-caps bolder 16px/3 cursive") is true
PASS CSS.supports("font", "inherit") is true
PASS CSS.supports("font", "italic small-caps bolder 16px/3 inherit") is false
PASS CSS.supports("font", "inherit small-caps bolder 16px/3 cursive") is false
PASS CSS.supports("list-style", "georgian inside") is true
PASS CSS.supports("list-style", "inherit") is true
PASS CSS.supports("list-style", "georgian inherit") is false
PASS CSS.supports("list-style", "inherit inside") is false
PASS CSS.supports("margin", "5px") is true
PASS CSS.supports("margin", "inherit") is true
PASS CSS.supports("margin", "inherit 5px") is false
PASS CSS.supports("margin", "5px inherit") is false
PASS CSS.supports("margin", "inherit 5px 5px") is false
PASS CSS.supports("margin", "inherit 5px 5px 5px") is false
PASS CSS.supports("outline", "1px solid #000") is true
PASS CSS.supports("outline", "inherit") is true
PASS CSS.supports("outline", "1px solid inherit") is false
PASS CSS.supports("outline", "inherit solid #000") is false
PASS CSS.supports("overflow", "scroll") is true
PASS CSS.supports("overflow", "inherit") is true
PASS CSS.supports("overflow", "inherit scroll") is false
PASS CSS.supports("overflow", "scroll inherit") is false
PASS CSS.supports("transform", "scaleX(2)") is true
PASS CSS.supports("transform", "inherit") is true
PASS CSS.supports("transform", "scaleX(2) inherit") is false
PASS CSS.supports("transform", "inherit scaleX(2)") is false
PASS CSS.supports("transition", "margin-left 4s") is true
PASS CSS.supports("transition", "inherit") is true
PASS CSS.supports("transition", "margin-left inherit") is false
PASS CSS.supports("transition", "inherit 4s") is false
PASS successfullyParsed is true

TEST COMPLETE
@@ -1,21 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<script src="../resources/js-test-pre.js"></script>
<script src="../resources/js-test.js"></script>
</head>
<body>
<script>
description("Test window.CSS.supports()");

shouldBeTrue('CSS.supports("display: none")');
shouldBeTrue('CSS.supports("(display: none)")');
shouldBeTrue('CSS.supports(" display: none ")');
shouldBeTrue('CSS.supports("(display: none)")');
shouldBeFalse('CSS.supports("(display: deadbeef)")');

shouldBeFalse('CSS.supports("display: deadbeef")');
shouldBeTrue('CSS.supports("(display: none) and ((display: block) or (display: inline))")');
shouldBeTrue('CSS.supports("(not (display: deadbeef)) and (display: block)")');
shouldBeTrue('CSS.supports("top: -webkit-calc(80% - 20px)")');
shouldBeTrue('CSS.supports("top: calc(80% - 20px)")');
shouldBeTrue('CSS.supports("background-color: rgb(0, 128, 0)")');
shouldBeTrue('CSS.supports("background: url(\'/blah\')")');
shouldBeFalse('CSS.supports("background: invalid(\'/blah\')")');
@@ -56,7 +56,6 @@
shouldBeFalse('CSS.supports("(display: none) or(-webkit-transition: all 1s )")');
shouldBeTrue('CSS.supports("(((((((display: none)))))))")');
shouldBeFalse('CSS.supports("(!important)")');
shouldBeFalse('CSS.supports("!important")');
shouldBeFalse('CSS.supports("not not not not (display: none)")');

// Functions.
@@ -73,19 +72,20 @@
shouldBeFalse('CSS.supports("dis\\nplay", "none")');
shouldBeFalse('CSS.supports("display", "rainbow")');
shouldBeFalse('CSS.supports("display", "url(rainbow.png)")');
shouldBeTrue('CSS.supports(" background ", "url(rainbow.png)")');
shouldBeFalse('CSS.supports(" background ", "url(rainbow.png)")');
shouldBeFalse('CSS.supports("background", "invalid(rainbow.png)")');
shouldBeTrue('CSS.supports("background-color", "#000")');
shouldBeTrue('CSS.supports("background-color", "rgba(0,0,0,0)")');
shouldBeTrue('CSS.supports("background-color", "inherit")');
shouldBeFalse('CSS.supports("font-family", "\'\\n\'")');

shouldBeTrue('CSS.supports("display", "none !important")');
shouldBeTrue('CSS.supports("display", "none!important")');
shouldBeFalse('CSS.supports("display", "none !important")');
shouldBeFalse('CSS.supports("display", "none!important")');
shouldBeFalse('CSS.supports("display", "!important none")');
shouldBeFalse('CSS.supports("display", "none !important ! important ")');
shouldBeTrue('CSS.supports("display", "none ! important")');
shouldBeTrue('CSS.supports("display", "none ! \timportant")');
shouldBeTrue('CSS.supports("display", "none ! \\nimportant")');
shouldBeFalse('CSS.supports("display", "none ! important")');
shouldBeFalse('CSS.supports("display", "none ! \timportant")');
shouldBeFalse('CSS.supports("display", "none ! \\nimportant")');

shouldBeFalse('CSS.supports("", "")');
shouldBeFalse('CSS.supports(true, "")');
@@ -96,7 +96,71 @@
shouldBeFalse('CSS.supports("content", [])');
shouldBeFalse('CSS.supports("content", "!important")');
shouldBeFalse('CSS.supports("(display: none)", undefined)');

// Test that __qem is internal
shouldBeFalse('CSS.supports("margin", "1__qem")');

// shorthands and CSS wide keywords
shouldBeTrue('CSS.supports("border", "1px solid #000")');
shouldBeTrue('CSS.supports("border", "inherit")');
shouldBeFalse('CSS.supports("border", "1px solid inherit")');
shouldBeFalse('CSS.supports("border", "inherit solid #000")');
shouldBeTrue('CSS.supports("border-bottom", "thick green")');
shouldBeTrue('CSS.supports("border-bottom", "inherit")');
shouldBeFalse('CSS.supports("border-bottom", "thick green inherit")');
shouldBeFalse('CSS.supports("border-bottom", "inherit thick green")');
shouldBeTrue('CSS.supports("border-left", "thick green")');
shouldBeTrue('CSS.supports("border-left", "inherit")');
shouldBeFalse('CSS.supports("border-left", "thick green inherit")');
shouldBeFalse('CSS.supports("border-left", "inherit thick green")');
shouldBeTrue('CSS.supports("border-right", "thick green")');
shouldBeTrue('CSS.supports("border-right", "inherit")');
shouldBeFalse('CSS.supports("border-right", "thick green inherit")');
shouldBeFalse('CSS.supports("border-right", "inherit thick green")');
shouldBeTrue('CSS.supports("border-top", "thick green")');
shouldBeTrue('CSS.supports("border-top", "inherit")');
shouldBeFalse('CSS.supports("border-top", "thick green inherit")');
shouldBeFalse('CSS.supports("border-top", "inherit thick green")');
shouldBeTrue('CSS.supports("border-radius", "1px 0 3px 4px")');
shouldBeTrue('CSS.supports("border-radius", "inherit")');
shouldBeFalse('CSS.supports("border-radius", "1px 0 3px inherit")');
shouldBeFalse('CSS.supports("border-radius", "inherit 0 3px 4px")');
shouldBeTrue('CSS.supports("border-spacing", "5px 5px")');
shouldBeTrue('CSS.supports("border-spacing", "inherit")');
shouldBeFalse('CSS.supports("border-spacing", "5px inherit")');
shouldBeFalse('CSS.supports("border-spacing", "inherit 5px")');
shouldBeTrue('CSS.supports("font", "italic small-caps bolder 16px/3 cursive")');
shouldBeTrue('CSS.supports("font", "inherit")');
shouldBeFalse('CSS.supports("font", "italic small-caps bolder 16px/3 inherit")');
shouldBeFalse('CSS.supports("font", "inherit small-caps bolder 16px/3 cursive")');
shouldBeTrue('CSS.supports("list-style", "georgian inside")');
shouldBeTrue('CSS.supports("list-style", "inherit")');
shouldBeFalse('CSS.supports("list-style", "georgian inherit")');
shouldBeFalse('CSS.supports("list-style", "inherit inside")');

// border-width, border-color, border-style and padding use same code path as margin
shouldBeTrue('CSS.supports("margin", "5px")');
shouldBeTrue('CSS.supports("margin", "inherit")');
shouldBeFalse('CSS.supports("margin", "inherit 5px")');
shouldBeFalse('CSS.supports("margin", "5px inherit")');
shouldBeFalse('CSS.supports("margin", "inherit 5px 5px")');
shouldBeFalse('CSS.supports("margin", "inherit 5px 5px 5px")');
shouldBeTrue('CSS.supports("outline", "1px solid #000")');
shouldBeTrue('CSS.supports("outline", "inherit")');
shouldBeFalse('CSS.supports("outline", "1px solid inherit")');
shouldBeFalse('CSS.supports("outline", "inherit solid #000")');
shouldBeTrue('CSS.supports("overflow", "scroll")');
shouldBeTrue('CSS.supports("overflow", "inherit")');
shouldBeFalse('CSS.supports("overflow", "inherit scroll")');
shouldBeFalse('CSS.supports("overflow", "scroll inherit")');
shouldBeTrue('CSS.supports("transform", "scaleX(2)")');
shouldBeTrue('CSS.supports("transform", "inherit")');
shouldBeFalse('CSS.supports("transform", "scaleX(2) inherit")');
shouldBeFalse('CSS.supports("transform", "inherit scaleX(2)")');
shouldBeTrue('CSS.supports("transition", "margin-left 4s")');
shouldBeTrue('CSS.supports("transition", "inherit")');
shouldBeFalse('CSS.supports("transition", "margin-left inherit")');
shouldBeFalse('CSS.supports("transition", "inherit 4s")');
</script>
<script src="../resources/js-test-post.js"></script>
</body>
</html>
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2022 Apple Inc. All rights reserved.
* Copyright (C) 2012 Motorola Mobility Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,25 +43,11 @@

namespace WebCore {

static String valueWithoutImportant(const String& value)
{
if (!value.endsWithIgnoringASCIICase("important"_s))
return value;

String newValue = value;
int bangIndex = newValue.length() - 9 - 1;
if (newValue[bangIndex] == ' ')
bangIndex--;
newValue = newValue.left(bangIndex);

return newValue;
}

bool DOMCSSNamespace::supports(Document& document, const String& property, const String& value)
{
CSSParserContext parserContext(document);

auto propertyNameWithoutWhitespace = property.stripWhiteSpace();
auto propertyNameWithoutWhitespace = property;
CSSPropertyID propertyID = cssPropertyID(propertyNameWithoutWhitespace);
if (propertyID == CSSPropertyInvalid && isCustomPropertyName(propertyNameWithoutWhitespace)) {
auto dummyStyle = MutableStyleProperties::create();
@@ -77,17 +64,11 @@ bool DOMCSSNamespace::supports(Document& document, const String& property, const
if (propertyID == CSSPropertyInvalid)
return false;

// CSSParser::parseValue() won't work correctly if !important is present,
// so just get rid of it. It doesn't matter to supports() if it's actually
// there or not, provided how it's specified in the value is correct.
String normalizedValue = value.stripWhiteSpace().simplifyWhiteSpace();
normalizedValue = valueWithoutImportant(normalizedValue);

if (normalizedValue.isEmpty())
if (value.isEmpty())
return false;

auto dummyStyle = MutableStyleProperties::create();
return CSSParser::parseValue(dummyStyle, propertyID, normalizedValue, false, parserContext) != CSSParser::ParseResult::Error;
return CSSParser::parseValue(dummyStyle, propertyID, value, false, parserContext) != CSSParser::ParseResult::Error;
}

bool DOMCSSNamespace::supports(Document& document, const String& conditionText)

0 comments on commit 30880fe

Please sign in to comment.