[CSS Container Queries] Basic parsing support for query syntax
https://bugs.webkit.org/show_bug.cgi?id=235712
Reviewed by Darin Adler.
LayoutTests/imported/w3c:
* web-platform-tests/css/css-contain/container-queries/at-container-parsing-expected.txt:
Source/WebCore:
https://drafts.csswg.org/css-contain-3/#at-ruledef-container
Use MediaQueryParser to parse the query syntax. Note that because the limitations
of MediaQueryParser only simple non-nested queries can be parsed for now.
* css/StyleRule.h:
* css/parser/CSSParserImpl.cpp:
(WebCore::filterProperties):
(WebCore::CSSParserImpl::consumeAtRule):
(WebCore::CSSParserImpl::consumeContainerRule):
* css/parser/CSSPropertyParser.cpp:
(WebCore::consumeContainerName):
* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::consumeSingleContainerName):
* css/parser/CSSPropertyParserHelpers.h:
* css/parser/MediaQueryParser.cpp:
(WebCore::MediaQueryParser::parseContainerQuery):
(WebCore::MediaQueryParser::MediaQueryParser):
(WebCore::MediaQueryParser::readContainerQuery):
(WebCore::MediaQueryParser::handleBlocks):
(WebCore::MediaQueryParser::processToken):
(WebCore::MediaQueryParser::parseInternal):
* css/parser/MediaQueryParser.h:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@288675 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/imported/w3c/ChangeLog b/LayoutTests/imported/w3c/ChangeLog
index b598431..f7fa83d 100644
--- a/LayoutTests/imported/w3c/ChangeLog
+++ b/LayoutTests/imported/w3c/ChangeLog
@@ -1,3 +1,12 @@
+2022-01-27 Antti Koivisto <antti@apple.com>
+
+ [CSS Container Queries] Basic parsing support for query syntax
+ https://bugs.webkit.org/show_bug.cgi?id=235712
+
+ Reviewed by Darin Adler.
+
+ * web-platform-tests/css/css-contain/container-queries/at-container-parsing-expected.txt:
+
2022-01-26 Alexey Shvayka <ashvayka@apple.com>
globalThis.queueMicrotask() should report thrown exceptions
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/at-container-parsing-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/at-container-parsing-expected.txt
index e0178c2..502b9f7 100644
--- a/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/at-container-parsing-expected.txt
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/at-container-parsing-expected.txt
@@ -33,14 +33,14 @@
PASS (color-index >= 1)
PASS size(grid)
PASS (grid)
-FAIL screen assert_equals: expected 0 but got 1
-FAIL print assert_equals: expected 0 but got 1
-FAIL not print assert_equals: expected 0 but got 1
-FAIL only print assert_equals: expected 0 but got 1
-FAIL screen and (width: 100px) assert_equals: expected 0 but got 1
-FAIL screen or (width: 100px) assert_equals: expected 0 but got 1
-FAIL not screen and (width: 100px) assert_equals: expected 0 but got 1
-FAIL not screen or (width: 100px) assert_equals: expected 0 but got 1
+PASS screen
+PASS print
+PASS not print
+PASS only print
+PASS screen and (width: 100px)
+PASS screen or (width: 100px)
+PASS not screen and (width: 100px)
+PASS not screen or (width: 100px)
FAIL (width: 100px), (height: 100px) assert_equals: expected 0 but got 1
PASS Container selector: foo
PASS Container selector: foo
@@ -52,9 +52,9 @@
PASS Container selector: type(block-size)
PASS Container selector: name(bar) type(block-size)
PASS Container selector: type(block-size) name(bar)
-FAIL Container selector: foo foo assert_equals: expected 0 but got 1
-FAIL Container selector: 1px assert_equals: expected 0 but got 1
-FAIL Container selector: 50gil assert_equals: expected 0 but got 1
+PASS Container selector: foo foo
+PASS Container selector: 1px
+PASS Container selector: 50gil
FAIL Container selector: name(1px) assert_equals: expected 0 but got 1
FAIL Container selector: type(1px) assert_equals: expected 0 but got 1
FAIL Container selector: type(red) assert_equals: expected 0 but got 1
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 7d96218..df4dceb 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,34 @@
+2022-01-27 Antti Koivisto <antti@apple.com>
+
+ [CSS Container Queries] Basic parsing support for query syntax
+ https://bugs.webkit.org/show_bug.cgi?id=235712
+
+ Reviewed by Darin Adler.
+
+ https://drafts.csswg.org/css-contain-3/#at-ruledef-container
+
+ Use MediaQueryParser to parse the query syntax. Note that because the limitations
+ of MediaQueryParser only simple non-nested queries can be parsed for now.
+
+ * css/StyleRule.h:
+ * css/parser/CSSParserImpl.cpp:
+ (WebCore::filterProperties):
+ (WebCore::CSSParserImpl::consumeAtRule):
+ (WebCore::CSSParserImpl::consumeContainerRule):
+ * css/parser/CSSPropertyParser.cpp:
+ (WebCore::consumeContainerName):
+ * css/parser/CSSPropertyParserHelpers.cpp:
+ (WebCore::CSSPropertyParserHelpers::consumeSingleContainerName):
+ * css/parser/CSSPropertyParserHelpers.h:
+ * css/parser/MediaQueryParser.cpp:
+ (WebCore::MediaQueryParser::parseContainerQuery):
+ (WebCore::MediaQueryParser::MediaQueryParser):
+ (WebCore::MediaQueryParser::readContainerQuery):
+ (WebCore::MediaQueryParser::handleBlocks):
+ (WebCore::MediaQueryParser::processToken):
+ (WebCore::MediaQueryParser::parseInternal):
+ * css/parser/MediaQueryParser.h:
+
2022-01-27 Tyler Wilcock <tyler_w@apple.com>
AX ITM: Defer to the tree when determining AX object loading progress
diff --git a/Source/WebCore/css/StyleRule.h b/Source/WebCore/css/StyleRule.h
index cd15f87..5e1c07b 100644
--- a/Source/WebCore/css/StyleRule.h
+++ b/Source/WebCore/css/StyleRule.h
@@ -314,7 +314,10 @@
std::variant<CascadeLayerName, Vector<CascadeLayerName>> m_nameVariant;
};
-struct ContainerQuery { };
+struct ContainerQuery {
+ AtomString containerName;
+ Ref<MediaQuerySet> query;
+};
class StyleRuleContainer final : public StyleRuleGroup {
public:
diff --git a/Source/WebCore/css/parser/CSSParserImpl.cpp b/Source/WebCore/css/parser/CSSParserImpl.cpp
index 9d1a6e9..f7d5592 100644
--- a/Source/WebCore/css/parser/CSSParserImpl.cpp
+++ b/Source/WebCore/css/parser/CSSParserImpl.cpp
@@ -123,12 +123,6 @@
output[--unusedEntries] = property;
continue;
}
-
- // FIXME-NEWPARSER: We won't support @apply yet.
- /*else if (property.id() == CSSPropertyApplyAtRule) {
- // FIXME: Do we need to do anything here?
- } */
-
if (seenProperties.test(propertyIDIndex))
continue;
@@ -430,11 +424,6 @@
return consumeNamespaceRule(prelude);
if (allowedRules <= RegularRules && id == CSSAtRuleLayer)
return consumeLayerRule(prelude, { });
- // FIXME-NEWPARSER: Support "apply"
- /*if (allowedRules == ApplyRules && id == CSSAtRuleApply) {
- consumeApplyRule(prelude);
- return nullptr; // consumeApplyRule just updates m_parsedProperties
- }*/
return nullptr; // Parse error, unrecognised at-rule without block
}
@@ -623,7 +612,7 @@
if (m_observerWrapper)
m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
- return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude, MediaQueryParserContext(m_context)).releaseNonNull(), WTFMove(rules));
+ return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude, { m_context }).releaseNonNull(), WTFMove(rules));
}
RefPtr<StyleRuleSupports> CSSParserImpl::consumeSupportsRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
@@ -862,8 +851,26 @@
if (!m_context.containerQueriesEnabled)
return nullptr;
+ if (prelude.atEnd())
+ return nullptr;
+
+ auto consumeName = [&]() -> AtomString {
+ if (prelude.peek().type() == LeftParenthesisToken || prelude.peek().type() == FunctionToken)
+ return nullAtom();
+ auto nameValue = CSSPropertyParserHelpers::consumeSingleContainerName(prelude);
+ if (!nameValue)
+ return nullAtom();
+ return nameValue->stringValue();
+ };
+
+ auto name = consumeName();
+
+ auto query = MediaQueryParser::parseContainerQuery(prelude, MediaQueryParserContext(m_context));
+ if (!query)
+ return nullptr;
+
if (m_deferredParser)
- return StyleRuleContainer::create({ }, makeUnique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
+ return StyleRuleContainer::create({ name, query.releaseNonNull() }, makeUnique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
Vector<RefPtr<StyleRuleBase>> rules;
@@ -881,7 +888,7 @@
if (m_observerWrapper)
m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
- return StyleRuleContainer::create({ }, WTFMove(rules));
+ return StyleRuleContainer::create({ name, query.releaseNonNull() }, WTFMove(rules));
}
RefPtr<StyleRuleKeyframe> CSSParserImpl::consumeKeyframeStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
diff --git a/Source/WebCore/css/parser/CSSPropertyParser.cpp b/Source/WebCore/css/parser/CSSPropertyParser.cpp
index 6757abb..bfb9b61 100644
--- a/Source/WebCore/css/parser/CSSPropertyParser.cpp
+++ b/Source/WebCore/css/parser/CSSPropertyParser.cpp
@@ -3806,19 +3806,9 @@
if (range.peek().id() == CSSValueNone)
return consumeIdent(range);
- auto consumeName = [&]() -> RefPtr<CSSValue> {
- if (range.peek().id() == CSSValueNone)
- return nullptr;
- if (auto ident = consumeCustomIdent(range))
- return ident;
- if (auto string = consumeString(range))
- return string;
- return nullptr;
- };
-
auto list = CSSValueList::createSpaceSeparated();
do {
- auto name = consumeName();
+ auto name = consumeSingleContainerName(range);
if (!name)
return nullptr;
list->append(name.releaseNonNull());
diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
index e3f1a92..89809f1 100644
--- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
+++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
@@ -4279,6 +4279,17 @@
return isPredefinedCounterStyle(nameToken.id()) ? name.convertToASCIILowercaseAtom() : name.toAtomString();
}
+RefPtr<CSSPrimitiveValue> consumeSingleContainerName(CSSParserTokenRange& range)
+{
+ if (range.peek().id() == CSSValueNone)
+ return nullptr;
+ if (auto ident = consumeCustomIdent(range))
+ return ident;
+ if (auto string = consumeString(range))
+ return string;
+ return nullptr;
+}
+
std::optional<CSSValueID> consumeFontVariantCSS21Raw(CSSParserTokenRange& range)
{
return consumeIdentRaw<CSSValueNormal, CSSValueSmallCaps>(range);
diff --git a/Source/WebCore/css/parser/CSSPropertyParserHelpers.h b/Source/WebCore/css/parser/CSSPropertyParserHelpers.h
index d118292..2a7eac3 100644
--- a/Source/WebCore/css/parser/CSSPropertyParserHelpers.h
+++ b/Source/WebCore/css/parser/CSSPropertyParserHelpers.h
@@ -193,6 +193,7 @@
bool isPredefinedCounterStyle(CSSValueID);
RefPtr<CSSPrimitiveValue> consumeCounterStyleName(CSSParserTokenRange&);
AtomString consumeCounterStyleNameInPrelude(CSSParserTokenRange&);
+RefPtr<CSSPrimitiveValue> consumeSingleContainerName(CSSParserTokenRange&);
std::optional<CSSValueID> consumeFontVariantCSS21Raw(CSSParserTokenRange&);
std::optional<CSSValueID> consumeFontWeightKeywordValueRaw(CSSParserTokenRange&);
diff --git a/Source/WebCore/css/parser/MediaQueryParser.cpp b/Source/WebCore/css/parser/MediaQueryParser.cpp
index 876c99b..cb5a08a 100644
--- a/Source/WebCore/css/parser/MediaQueryParser.cpp
+++ b/Source/WebCore/css/parser/MediaQueryParser.cpp
@@ -55,8 +55,18 @@
return MediaQueryParser(MediaConditionParser, context).parseInternal(range);
}
+RefPtr<MediaQuerySet> MediaQueryParser::parseContainerQuery(CSSParserTokenRange range, MediaQueryParserContext context)
+{
+ if (range.atEnd())
+ return nullptr;
+ if (range.peek().type() != LeftParenthesisToken && range.peek().type() != FunctionToken)
+ return nullptr;
+ return MediaQueryParser(ContainerQueryParser, context).parseInternal(range);
+}
+
const MediaQueryParser::State MediaQueryParser::ReadRestrictor = &MediaQueryParser::readRestrictor;
const MediaQueryParser::State MediaQueryParser::ReadMediaNot = &MediaQueryParser::readMediaNot;
+const MediaQueryParser::State MediaQueryParser::ReadContainerQuery = &MediaQueryParser::readContainerQuery;
const MediaQueryParser::State MediaQueryParser::ReadMediaType = &MediaQueryParser::readMediaType;
const MediaQueryParser::State MediaQueryParser::ReadAnd = &MediaQueryParser::readAnd;
const MediaQueryParser::State MediaQueryParser::ReadFeatureStart = &MediaQueryParser::readFeatureStart;
@@ -74,10 +84,17 @@
, m_querySet(MediaQuerySet::create())
{
- if (parserType == MediaQuerySetParser)
+ switch (m_parserType) {
+ case MediaQuerySetParser:
m_state = &MediaQueryParser::readRestrictor;
- else // MediaConditionParser
+ break;
+ case MediaConditionParser:
m_state = &MediaQueryParser::readMediaNot;
+ break;
+ case ContainerQueryParser:
+ m_state = &MediaQueryParser::readContainerQuery;
+ break;
+ }
}
MediaQueryParser::~MediaQueryParser() = default;
@@ -102,6 +119,12 @@
readFeatureStart(type, token, range);
}
+void MediaQueryParser::readContainerQuery(CSSParserTokenType type, const CSSParserToken&, CSSParserTokenRange&)
+{
+ if (type == FunctionToken || type == LeftParenthesisToken)
+ m_state = ReadFeature;
+}
+
static bool isRestrictorOrLogicalOperator(const CSSParserToken& token)
{
// FIXME: it would be more efficient to use lower-case always for tokenValue.
@@ -239,29 +262,45 @@
void MediaQueryParser::handleBlocks(const CSSParserToken& token)
{
- if (token.getBlockType() == CSSParserToken::BlockStart
- && (token.type() != LeftParenthesisToken || m_blockWatcher.blockLevel()))
- m_state = SkipUntilBlockEnd;
+ if (token.getBlockType() != CSSParserToken::BlockStart)
+ return;
+ auto shouldSkipBlock = [&] {
+ // FIXME: Nested blocks should be supported.
+ if (m_blockWatcher.blockLevel())
+ return true;
+ if (token.type() == LeftParenthesisToken)
+ return false;
+ if (m_parserType == ContainerQueryParser && token.type() == FunctionToken)
+ return !equalLettersIgnoringASCIICase(token.value(), "size");
+ return true;
+ }();
+ if (shouldSkipBlock)
+ m_state = SkipUntilBlockEnd;
}
void MediaQueryParser::processToken(const CSSParserToken& token, CSSParserTokenRange& range)
{
CSSParserTokenType type = token.type();
- if (m_state != ReadFeatureValue || type == WhitespaceToken) {
+ if (type == WhitespaceToken) {
+ range.consume();
+ return;
+ }
+
+ if (m_state != ReadFeatureValue) {
handleBlocks(token);
m_blockWatcher.handleToken(token);
range.consume();
}
// Call the function that handles current state
- if (type != WhitespaceToken)
- ((this)->*(m_state))(type, token, range);
+ ((this)->*(m_state))(type, token, range);
}
// The state machine loop
-RefPtr<MediaQuerySet> MediaQueryParser::parseInternal(CSSParserTokenRange range)
+RefPtr<MediaQuerySet> MediaQueryParser::parseInternal(CSSParserTokenRange& range)
{
+
while (!range.atEnd())
processToken(range.peek(), range);
diff --git a/Source/WebCore/css/parser/MediaQueryParser.h b/Source/WebCore/css/parser/MediaQueryParser.h
index 6de823c..e6c15ec 100644
--- a/Source/WebCore/css/parser/MediaQueryParser.h
+++ b/Source/WebCore/css/parser/MediaQueryParser.h
@@ -48,22 +48,25 @@
static RefPtr<MediaQuerySet> parseMediaQuerySet(const String&, MediaQueryParserContext);
static RefPtr<MediaQuerySet> parseMediaQuerySet(CSSParserTokenRange, MediaQueryParserContext);
static RefPtr<MediaQuerySet> parseMediaCondition(CSSParserTokenRange, MediaQueryParserContext);
+ static RefPtr<MediaQuerySet> parseContainerQuery(CSSParserTokenRange, MediaQueryParserContext);
private:
enum ParserType {
MediaQuerySetParser,
MediaConditionParser,
+ ContainerQueryParser,
};
MediaQueryParser(ParserType, MediaQueryParserContext);
virtual ~MediaQueryParser();
- RefPtr<MediaQuerySet> parseInternal(CSSParserTokenRange);
+ RefPtr<MediaQuerySet> parseInternal(CSSParserTokenRange&);
void processToken(const CSSParserToken&, CSSParserTokenRange&);
void readRestrictor(CSSParserTokenType, const CSSParserToken&, CSSParserTokenRange&);
void readMediaNot(CSSParserTokenType, const CSSParserToken&, CSSParserTokenRange&);
+ void readContainerQuery(CSSParserTokenType, const CSSParserToken&, CSSParserTokenRange&);
void readMediaType(CSSParserTokenType, const CSSParserToken&, CSSParserTokenRange&);
void readAnd(CSSParserTokenType, const CSSParserToken&, CSSParserTokenRange&);
void readFeatureStart(CSSParserTokenType, const CSSParserToken&, CSSParserTokenRange&);
@@ -124,6 +127,7 @@
const static State ReadRestrictor;
const static State ReadMediaNot;
+ const static State ReadContainerQuery;
const static State ReadMediaType;
const static State ReadAnd;
const static State ReadFeatureStart;