Commit bce18d53 authored by Kevin Babbitt's avatar Kevin Babbitt Committed by Commit Bot

[CSSParser] Use streaming tokenizer for @supports rules and CSS.supports

This gets us one step closer to using the streaming tokenizer for
declarations, which will allow us to preserve raw text for CSS variable
values.

The existing logic made heavy use of sub-ranges to track block
boundaries and restart parsing to find the correct branch of a
production. To support the new logic, the streaming tokenizer's
BlockGuard gained the ability to tell its caller whether the end of
the block it was created for has been reached. Since the streaming
parser cannot be rewound to a previous position, the supports parser
had to be restructured to peek ahead more than one token for the
supports-in-parens grammar, and to decide based on lead token whether
to parse supports-feature or general-enclosed.

Finally, to maintain support for the CSSConditionRule.conditionText
property, the streaming tokenizer gained the ability to return raw
text for an underlying range.

Bug: 661854
Change-Id: I7fcf244e5f360e3715c8543ac7283e351d6b2baf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2238512Reviewed-by: default avatarAnders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Kevin Babbitt <kbabbitt@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#789195}
parent b5bdc990
......@@ -10,6 +10,7 @@
#include "third_party/blink/renderer/core/css/css_keyframe_rule.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_fast_paths.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
#include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_supports_parser.h"
......@@ -212,13 +213,17 @@ StyleRuleKeyframe* CSSParser::ParseKeyframeRule(const CSSParserContext* context,
bool CSSParser::ParseSupportsCondition(const String& condition,
SecureContextMode secure_context_mode) {
CSSTokenizer tokenizer(condition);
const auto tokens = tokenizer.TokenizeToEOF();
// window.CSS.supports requires to parse as-if it was wrapped in parenthesis.
String wrapped_condition = "(" + condition + ")";
CSSTokenizer tokenizer(wrapped_condition);
CSSParserTokenStream stream(tokenizer);
CSSParserImpl parser(StrictCSSParserContext(secure_context_mode));
return CSSSupportsParser::SupportsCondition(
CSSParserTokenRange(tokens), parser,
CSSSupportsParser::Mode::kForWindowCSS) ==
CSSSupportsParser::Result::kSupported;
CSSSupportsParser::Result result =
CSSSupportsParser::ConsumeSupportsCondition(stream, parser);
if (!stream.AtEnd())
result = CSSSupportsParser::Result::kParseFailure;
return result == CSSSupportsParser::Result::kSupported;
}
bool CSSParser::ParseColor(Color& color, const String& string, bool strict) {
......
......@@ -730,14 +730,19 @@ StyleRuleMedia* CSSParserImpl::ConsumeMediaRule(CSSParserTokenStream& stream) {
StyleRuleSupports* CSSParserImpl::ConsumeSupportsRule(
CSSParserTokenStream& stream) {
wtf_size_t prelude_offset_start = stream.LookAheadOffset();
CSSParserTokenRange prelude = ConsumeAtRulePrelude(stream);
CSSSupportsParser::Result supported =
CSSSupportsParser::ConsumeSupportsCondition(stream, *this);
// Check whether the entire prelude was consumed. If it wasn't, ensure we
// consume any leftovers plus the block before returning a parse error.
stream.ConsumeWhitespace();
CSSParserTokenRange prelude_remainder = ConsumeAtRulePrelude(stream);
if (!prelude_remainder.AtEnd())
supported = CSSSupportsParser::Result::kParseFailure;
wtf_size_t prelude_offset_end = stream.LookAheadOffset();
if (!ConsumeEndOfPreludeForAtRuleWithBlock(stream))
return nullptr;
CSSParserTokenStream::BlockGuard guard(stream);
CSSSupportsParser::Result supported = CSSSupportsParser::SupportsCondition(
prelude, *this, CSSSupportsParser::Mode::kForAtRule);
if (supported == CSSSupportsParser::Result::kParseFailure)
return nullptr; // Parse error, invalid @supports condition
......@@ -747,7 +752,12 @@ StyleRuleSupports* CSSParserImpl::ConsumeSupportsRule(
observer_->StartRuleBody(stream.Offset());
}
const auto prelude_serialized = prelude.Serialize().StripWhiteSpace();
const auto prelude_serialized =
stream
.StringRangeAt(prelude_offset_start,
prelude_offset_end - prelude_offset_start)
.ToString()
.SimplifyWhiteSpace();
HeapVector<Member<StyleRuleBase>> rules;
ConsumeRuleList(stream, kRegularRuleList,
......
......@@ -6,6 +6,15 @@
namespace blink {
StringView CSSParserTokenStream::StringRangeAt(wtf_size_t start,
wtf_size_t length) const {
return tokenizer_.StringRangeAt(start, length);
}
wtf_size_t CSSParserTokenStream::BlockStackDepth() const {
return tokenizer_.BlockStackDepth();
}
void CSSParserTokenStream::ConsumeWhitespace() {
while (Peek().GetType() == kWhitespaceToken)
UncheckedConsume();
......
......@@ -47,15 +47,24 @@ class CORE_EXPORT CSSParserTokenStream {
explicit BlockGuard(CSSParserTokenStream& stream) : stream_(stream) {
const CSSParserToken next = stream.ConsumeInternal();
DCHECK_EQ(next.GetBlockType(), CSSParserToken::kBlockStart);
initial_stack_depth_ = stream_.BlockStackDepth();
DCHECK_GT(initial_stack_depth_, 0u);
}
~BlockGuard() {
stream_.EnsureLookAhead();
stream_.UncheckedSkipToEndOfBlock();
DCHECK(AtEndOfBlock());
}
bool AtEndOfBlock() const {
return (stream_.BlockStackDepth() == initial_stack_depth_ - 1) ||
stream_.AtEnd();
}
private:
CSSParserTokenStream& stream_;
wtf_size_t initial_stack_depth_;
};
// We found that this value works well empirically by printing out the
......@@ -132,6 +141,12 @@ class CORE_EXPORT CSSParserTokenStream {
return tokenizer_.PreviousOffset();
}
// Returns a view on a range of characters in the original string.
StringView StringRangeAt(wtf_size_t start, wtf_size_t length) const;
// Returns the block stack depth for the underlying tokenizer.
wtf_size_t BlockStackDepth() const;
void ConsumeWhitespace();
CSSParserToken ConsumeIncludingWhitespace();
void UncheckedConsumeComponentValue();
......
......@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/core/css/parser/css_supports_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
#include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
......@@ -49,37 +50,25 @@ bool ConsumeAnyValue(CSSParserTokenRange& range) {
} // namespace
CSSSupportsParser::Result CSSSupportsParser::SupportsCondition(
CSSParserTokenRange range,
CSSParserImpl& parser,
Mode mode) {
range.ConsumeWhitespace();
CSSParserTokenRange stored_range = range;
CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsCondition(
CSSParserTokenStream& stream,
CSSParserImpl& parser) {
stream.ConsumeWhitespace();
CSSSupportsParser supports_parser(parser);
Result result = supports_parser.ConsumeSupportsCondition(range);
if (mode != Mode::kForWindowCSS || result != Result::kParseFailure)
return result;
// window.CSS.supports requires to parse as-if it was wrapped in parenthesis.
// The only wrapped production that wouldn't have parsed above is
// <declaration>.
if (stored_range.Peek().GetType() != kIdentToken)
return Result::kParseFailure;
if (parser.SupportsDeclaration(stored_range))
return Result::kSupported;
return Result::kUnsupported;
return supports_parser.ConsumeSupportsCondition(stream);
}
bool CSSSupportsParser::AtIdent(CSSParserTokenRange& range, const char* ident) {
return range.Peek().GetType() == kIdentToken &&
EqualIgnoringASCIICase(range.Peek().Value(), ident);
bool CSSSupportsParser::AtIdent(const CSSParserToken& token,
const char* ident) {
return token.GetType() == kIdentToken &&
EqualIgnoringASCIICase(token.Value(), ident);
}
bool CSSSupportsParser::ConsumeIfIdent(CSSParserTokenRange& range,
bool CSSSupportsParser::ConsumeIfIdent(CSSParserTokenStream& stream,
const char* ident) {
if (!AtIdent(range, ident))
if (!AtIdent(stream.Peek(), ident))
return false;
range.ConsumeIncludingWhitespace();
stream.ConsumeIncludingWhitespace();
return true;
}
......@@ -87,53 +76,97 @@ bool CSSSupportsParser::ConsumeIfIdent(CSSParserTokenRange& range,
// | <supports-in-parens> [ and <supports-in-parens> ]*
// | <supports-in-parens> [ or <supports-in-parens> ]*
CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsCondition(
CSSParserTokenRange& range) {
CSSParserTokenStream& stream) {
// not <supports-in-parens>
if (ConsumeIfIdent(range, "not")) {
Result result = !ConsumeSupportsInParens(range);
return range.AtEnd() ? result : Result::kParseFailure;
}
stream.ConsumeWhitespace();
if (ConsumeIfIdent(stream, "not"))
return !ConsumeSupportsInParens(stream);
// <supports-in-parens> [ and <supports-in-parens> ]*
// | <supports-in-parens> [ or <supports-in-parens> ]*
Result result = ConsumeSupportsInParens(range);
if (AtIdent(range, "and")) {
while (ConsumeIfIdent(range, "and"))
result = result & ConsumeSupportsInParens(range);
} else if (AtIdent(range, "or")) {
while (ConsumeIfIdent(range, "or"))
result = result | ConsumeSupportsInParens(range);
Result result = ConsumeSupportsInParens(stream);
stream.ConsumeWhitespace();
if (AtIdent(stream.Peek(), "and")) {
stream.ConsumeWhitespace();
while (ConsumeIfIdent(stream, "and")) {
result = result & ConsumeSupportsInParens(stream);
stream.ConsumeWhitespace();
}
} else if (AtIdent(stream.Peek(), "or")) {
stream.ConsumeWhitespace();
while (ConsumeIfIdent(stream, "or")) {
result = result | ConsumeSupportsInParens(stream);
stream.ConsumeWhitespace();
}
}
return range.AtEnd() ? result : Result::kParseFailure;
return result;
}
bool CSSSupportsParser::IsSupportsInParens(const CSSParserToken& token) {
// All three productions for <supports-in-parens> must start with either a
// left parenthesis or a function.
return token.GetType() == kLeftParenthesisToken ||
token.GetType() == kFunctionToken;
}
bool CSSSupportsParser::IsEnclosedSupportsCondition(
const CSSParserToken& first_token,
const CSSParserToken& second_token) {
return (first_token.GetType() == kLeftParenthesisToken) &&
(AtIdent(second_token, "not") ||
second_token.GetType() == kLeftParenthesisToken ||
second_token.GetType() == kFunctionToken);
}
bool CSSSupportsParser::IsSupportsSelectorFn(
const CSSParserToken& first_token,
const CSSParserToken& second_token) {
return (first_token.GetType() == kFunctionToken &&
first_token.FunctionId() == CSSValueID::kSelector);
}
bool CSSSupportsParser::IsSupportsDecl(const CSSParserToken& first_token,
const CSSParserToken& second_token) {
return first_token.GetType() == kLeftParenthesisToken &&
second_token.GetType() == kIdentToken;
}
bool CSSSupportsParser::IsSupportsFeature(const CSSParserToken& first_token,
const CSSParserToken& second_token) {
return IsSupportsSelectorFn(first_token, second_token) ||
IsSupportsDecl(first_token, second_token);
}
bool CSSSupportsParser::IsGeneralEnclosed(const CSSParserToken& first_token) {
return first_token.GetType() == kLeftParenthesisToken ||
first_token.GetType() == kFunctionToken;
}
// <supports-in-parens> = ( <supports-condition> )
// | <supports-feature>
// | <general-enclosed>
CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsInParens(
CSSParserTokenRange& range) {
const CSSParserTokenRange stored_range = range;
CSSParserTokenStream& stream) {
CSSParserToken first_token = stream.Peek();
if (!IsSupportsInParens(first_token))
return Result::kParseFailure;
CSSParserTokenStream::BlockGuard guard(stream);
stream.ConsumeWhitespace();
// ( <supports-condition> )
if (range.Peek().GetType() == kLeftParenthesisToken) {
auto block = range.ConsumeBlock();
block.ConsumeWhitespace();
range.ConsumeWhitespace();
Result result = ConsumeSupportsCondition(block);
if (result != Result::kParseFailure)
return result;
// Parsing failed, so try parsing again as <supports-feature>.
range = stored_range;
if (IsEnclosedSupportsCondition(first_token, stream.Peek())) {
Result result = ConsumeSupportsCondition(stream);
return guard.AtEndOfBlock() ? result : Result::kParseFailure;
}
// <supports-feature>
Result result = ConsumeSupportsFeature(range);
if (result != Result::kParseFailure)
return result;
// Parsing failed, try again as <general-enclosed>
range = stored_range;
if (IsSupportsFeature(first_token, stream.Peek())) {
Result result = ConsumeSupportsFeature(first_token, stream);
return guard.AtEndOfBlock() ? result : Result::kParseFailure;
}
// <general-enclosed>
//
......@@ -142,36 +175,29 @@ CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsInParens(
// The result kUnknown is supposed to be evaluated at the top level, but
// we have already shipped the behavior of evaluating it here, and Firefox
// does the same thing.
return EvalUnknown(ConsumeGeneralEnclosed(range));
return EvalUnknown(ConsumeGeneralEnclosed(first_token, stream));
}
// <supports-feature> = <supports-selector-fn> | <supports-decl>
CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsFeature(
CSSParserTokenRange& range) {
const CSSParserTokenRange stored_range = range;
const CSSParserToken& first_token,
CSSParserTokenStream& stream) {
// <supports-selector-fn>
Result result = ConsumeSupportsSelectorFn(range);
if (result != Result::kParseFailure)
return result;
range = stored_range;
if (IsSupportsSelectorFn(first_token, stream.Peek()))
return ConsumeSupportsSelectorFn(first_token, stream);
// <supports-decl>
return ConsumeSupportsDecl(range);
return ConsumeSupportsDecl(first_token, stream);
}
// <supports-selector-fn> = selector( <complex-selector> )
CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsSelectorFn(
CSSParserTokenRange& range) {
const CSSParserToken& first_token,
CSSParserTokenStream& stream) {
if (!RuntimeEnabledFeatures::CSSSupportsSelectorEnabled())
return Result::kParseFailure;
if (range.Peek().GetType() != kFunctionToken)
return Result::kParseFailure;
if (range.Peek().FunctionId() != CSSValueID::kSelector)
return Result::kParseFailure;
auto block = range.ConsumeBlock();
block.ConsumeWhitespace();
range.ConsumeWhitespace();
DCHECK(IsSupportsSelectorFn(first_token, stream.Peek()));
auto block = stream.ConsumeUntilPeekedTypeIs<kRightParenthesisToken>();
if (CSSSelectorParser::SupportsComplexSelector(block, parser_.GetContext()))
return Result::kSupported;
return Result::kUnsupported;
......@@ -179,14 +205,11 @@ CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsSelectorFn(
// <supports-decl> = ( <declaration> )
CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsDecl(
CSSParserTokenRange& range) {
if (range.Peek().GetType() != kLeftParenthesisToken)
return Result::kParseFailure;
auto block = range.ConsumeBlock();
block.ConsumeWhitespace();
range.ConsumeWhitespace();
if (block.Peek().GetType() != kIdentToken)
const CSSParserToken& first_token,
CSSParserTokenStream& stream) {
if (!IsSupportsDecl(first_token, stream.Peek()))
return Result::kParseFailure;
auto block = stream.ConsumeUntilPeekedTypeIs<kRightParenthesisToken>();
if (parser_.SupportsDeclaration(block))
return Result::kSupported;
return Result::kUnsupported;
......@@ -195,16 +218,17 @@ CSSSupportsParser::Result CSSSupportsParser::ConsumeSupportsDecl(
// <general-enclosed> = [ <function-token> <any-value> ) ]
// | ( <ident> <any-value> )
CSSSupportsParser::Result CSSSupportsParser::ConsumeGeneralEnclosed(
CSSParserTokenRange& range) {
if (range.Peek().GetType() == kFunctionToken ||
range.Peek().GetType() == kLeftParenthesisToken) {
auto block = range.ConsumeBlock();
const CSSParserToken& first_token,
CSSParserTokenStream& stream) {
if (IsGeneralEnclosed(first_token)) {
auto block = stream.ConsumeUntilPeekedTypeIs<kRightParenthesisToken>();
// Note that <any-value> matches a sequence of one or more tokens, hence the
// block-range can't be empty.
// https://drafts.csswg.org/css-syntax-3/#typedef-any-value
if (block.AtEnd() || !ConsumeAnyValue(block))
return Result::kParseFailure;
range.ConsumeWhitespace();
stream.ConsumeWhitespace();
return Result::kUnknown;
}
return Result::kParseFailure;
......
......@@ -11,14 +11,13 @@
namespace blink {
class CSSParserImpl;
class CSSParserTokenRange;
class CSSParserToken;
class CSSParserTokenStream;
class CORE_EXPORT CSSSupportsParser {
STACK_ALLOCATED();
public:
enum class Mode { kForAtRule, kForWindowCSS };
enum class Result {
// kUnsupported/kSupported means that we parsed the @supports
// successfully, and conclusively determined that we either support or
......@@ -47,20 +46,20 @@ class CORE_EXPORT CSSSupportsParser {
kParseFailure
};
static Result SupportsCondition(CSSParserTokenRange, CSSParserImpl&, Mode);
static Result ConsumeSupportsCondition(CSSParserTokenStream&, CSSParserImpl&);
private:
friend class CSSSupportsParserTest;
CSSSupportsParser(CSSParserImpl& parser) : parser_(parser) {}
// True if the current token is a kIdentToken with the specified value
// True if the given token is a kIdentToken with the specified value
// (case-insensitive).
bool AtIdent(CSSParserTokenRange&, const char*);
static bool AtIdent(const CSSParserToken&, const char*);
// If the current token is a kIdentToken with the specified value (case
// insensitive), consumes the token and returns true.
bool ConsumeIfIdent(CSSParserTokenRange&, const char*);
static bool ConsumeIfIdent(CSSParserTokenStream&, const char*);
// Parsing functions follow, as defined by:
// https://drafts.csswg.org/css-conditional-3/#typedef-supports-condition
......@@ -68,24 +67,35 @@ class CORE_EXPORT CSSSupportsParser {
// <supports-condition> = not <supports-in-parens>
// | <supports-in-parens> [ and <supports-in-parens> ]*
// | <supports-in-parens> [ or <supports-in-parens> ]*
Result ConsumeSupportsCondition(CSSParserTokenRange&);
Result ConsumeSupportsCondition(CSSParserTokenStream&);
// <supports-in-parens> = ( <supports-condition> )
// | <supports-feature>
// | <general-enclosed>
Result ConsumeSupportsInParens(CSSParserTokenRange&);
Result ConsumeSupportsInParens(CSSParserTokenStream&);
// <supports-feature> = <supports-selector-fn> | <supports-decl>
Result ConsumeSupportsFeature(CSSParserTokenRange&);
Result ConsumeSupportsFeature(const CSSParserToken&, CSSParserTokenStream&);
// <supports-selector-fn> = selector( <complex-selector> )
Result ConsumeSupportsSelectorFn(CSSParserTokenRange&);
Result ConsumeSupportsSelectorFn(const CSSParserToken&,
CSSParserTokenStream&);
// <supports-decl> = ( <declaration> )
Result ConsumeSupportsDecl(CSSParserTokenRange&);
Result ConsumeSupportsDecl(const CSSParserToken&, CSSParserTokenStream&);
// <general-enclosed>
Result ConsumeGeneralEnclosed(CSSParserTokenRange&);
Result ConsumeGeneralEnclosed(const CSSParserToken&, CSSParserTokenStream&);
// Parsing helpers.
static bool IsSupportsInParens(const CSSParserToken&);
static bool IsEnclosedSupportsCondition(const CSSParserToken&,
const CSSParserToken&);
static bool IsSupportsSelectorFn(const CSSParserToken&,
const CSSParserToken&);
static bool IsSupportsDecl(const CSSParserToken&, const CSSParserToken&);
static bool IsSupportsFeature(const CSSParserToken&, const CSSParserToken&);
static bool IsGeneralEnclosed(const CSSParserToken&);
CSSParserImpl& parser_;
};
......
......@@ -6,6 +6,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
......@@ -24,66 +25,81 @@ class CSSSupportsParserTest : public testing::Test {
return CSSTokenizer(string).TokenizeToEOF();
}
Result SupportsCondition(String string, CSSSupportsParser::Mode mode) {
Result StaticConsumeSupportsCondition(String string) {
CSSParserImpl impl(MakeContext());
auto tokens = Tokenize(string);
return CSSSupportsParser::SupportsCondition(tokens, impl, mode);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
Result result = CSSSupportsParser::ConsumeSupportsCondition(stream, impl);
return stream.AtEnd() ? result : Result::kParseFailure;
}
Result AtSupports(String string) {
return SupportsCondition(string, CSSSupportsParser::Mode::kForAtRule);
return StaticConsumeSupportsCondition(string);
}
Result WindowCSSSupports(String string) {
return SupportsCondition(string, CSSSupportsParser::Mode::kForWindowCSS);
String wrapped_condition = "(" + string + ")";
return StaticConsumeSupportsCondition(wrapped_condition);
}
Result ConsumeSupportsCondition(String string) {
CSSParserImpl impl(MakeContext());
CSSSupportsParser parser(impl);
auto tokens = Tokenize(string);
CSSParserTokenRange range = tokens;
return parser.ConsumeSupportsCondition(range);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
return parser.ConsumeSupportsCondition(stream);
}
Result ConsumeSupportsInParens(String string) {
CSSParserImpl impl(MakeContext());
CSSSupportsParser parser(impl);
auto tokens = Tokenize(string);
CSSParserTokenRange range = tokens;
return parser.ConsumeSupportsInParens(range);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
return parser.ConsumeSupportsInParens(stream);
}
Result ConsumeSupportsFeature(String string) {
CSSParserImpl impl(MakeContext());
CSSSupportsParser parser(impl);
auto tokens = Tokenize(string);
CSSParserTokenRange range = tokens;
return parser.ConsumeSupportsFeature(range);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
CSSParserToken first_token = stream.Peek();
CSSParserTokenStream::BlockGuard guard(stream);
stream.ConsumeWhitespace();
return parser.ConsumeSupportsFeature(first_token, stream);
}
Result ConsumeSupportsSelectorFn(String string) {
CSSParserImpl impl(MakeContext());
CSSSupportsParser parser(impl);
auto tokens = Tokenize(string);
CSSParserTokenRange range = tokens;
return parser.ConsumeSupportsSelectorFn(range);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
CSSParserToken first_token = stream.Peek();
CSSParserTokenStream::BlockGuard guard(stream);
stream.ConsumeWhitespace();
return parser.ConsumeSupportsSelectorFn(first_token, stream);
}
Result ConsumeSupportsDecl(String string) {
CSSParserImpl impl(MakeContext());
CSSSupportsParser parser(impl);
auto tokens = Tokenize(string);
CSSParserTokenRange range = tokens;
return parser.ConsumeSupportsDecl(range);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
CSSParserToken first_token = stream.Peek();
CSSParserTokenStream::BlockGuard guard(stream);
stream.ConsumeWhitespace();
return parser.ConsumeSupportsDecl(first_token, stream);
}
Result ConsumeGeneralEnclosed(String string) {
CSSParserImpl impl(MakeContext());
CSSSupportsParser parser(impl);
auto tokens = Tokenize(string);
CSSParserTokenRange range = tokens;
return parser.ConsumeGeneralEnclosed(range);
CSSTokenizer tokenizer(string);
CSSParserTokenStream stream(tokenizer);
CSSParserToken first_token = stream.Peek();
CSSParserTokenStream::BlockGuard guard(stream);
stream.ConsumeWhitespace();
return parser.ConsumeGeneralEnclosed(first_token, stream);
}
};
......@@ -190,7 +206,27 @@ TEST_F(CSSSupportsParserTest, ConsumeSupportsInParens) {
ConsumeSupportsInParens("selector(div)or (color:green)"));
EXPECT_EQ(Result::kSupported,
ConsumeSupportsInParens("selector(div)and (color:green)"));
// Invalid <supports-selector-fn> formerly handled by
// ConsumeSupportsSelectorFn()
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens("#test"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens("test"));
// Invalid <supports-selector-fn> but valid <general-enclosed>
EXPECT_EQ(Result::kUnsupported, ConsumeSupportsInParens("test(1)"));
}
// Invalid <supports-decl> formerly handled by ConsumeSupportsDecl()
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens(""));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens(")"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens("color:red)"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens("color:red"));
// Invalid <general-enclosed> formerly handled by ConsumeGeneralEnclosed()
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens(""));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens(")"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens("color:red"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsInParens("asdf"));
}
TEST_F(CSSSupportsParserTest, ConsumeSupportsSelectorFn) {
......@@ -256,10 +292,6 @@ TEST_F(CSSSupportsParserTest, ConsumeSupportsSelectorFn) {
EXPECT_EQ(Result::kUnsupported, ConsumeSupportsSelectorFn("selector(:asdf)"));
EXPECT_EQ(Result::kUnsupported,
ConsumeSupportsSelectorFn("selector(::asdf)"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsSelectorFn("#test"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsSelectorFn("test"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsSelectorFn("test(1)"));
}
TEST_F(CSSSupportsParserTest, ConsumeSupportsSelectorFnWithFeatureDisabled) {
......@@ -290,12 +322,8 @@ TEST_F(CSSSupportsParserTest, ConsumeSupportsDecl) {
EXPECT_EQ(Result::kUnsupported, ConsumeSupportsDecl("(color)"));
EXPECT_EQ(Result::kUnsupported, ConsumeSupportsDecl("(color:)"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsDecl(""));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsDecl("("));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsDecl(")"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsDecl("()"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsDecl("color:red)"));
EXPECT_EQ(Result::kParseFailure, ConsumeSupportsDecl("color:red"));
}
TEST_F(CSSSupportsParserTest, ConsumeSupportsFeature) {
......@@ -315,26 +343,8 @@ TEST_F(CSSSupportsParserTest, ConsumeGeneralEnclosed) {
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("asdf(1, 2)"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("asdf(1, 2)\t"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed(""));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("("));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed(")"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("()"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("color:red"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("asdf"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("(asdf)"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("( asdf )"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("(3)"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("max(1, 2)"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("asdf(1, 2)"));
EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("asdf(1, 2)\t"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed(""));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("("));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed(")"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("()"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("color:red"));
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("asdf"));
// Invalid <any-value>:
EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("(asdf})"));
......
......@@ -48,6 +48,15 @@ Vector<CSSParserToken, 32> CSSTokenizer::TokenizeToEOF() {
}
}
StringView CSSTokenizer::StringRangeAt(wtf_size_t start,
wtf_size_t length) const {
return input_.RangeAt(start, length);
}
wtf_size_t CSSTokenizer::BlockStackDepth() const {
return block_stack_.size();
}
CSSParserToken CSSTokenizer::TokenizeSingle() {
while (true) {
prev_offset_ = input_.Offset();
......
......@@ -31,6 +31,8 @@ class CORE_EXPORT CSSTokenizer {
wtf_size_t Offset() const { return input_.Offset(); }
wtf_size_t PreviousOffset() const { return prev_offset_; }
StringView StringRangeAt(wtf_size_t start, wtf_size_t length) const;
wtf_size_t BlockStackDepth() const;
private:
CSSParserToken TokenizeSingle();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment