/
githubmirror
/
amphtml
Обзор
Документация
Войти
/
githubmirror
/
amphtml
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
validator/cpp/htmlparser/css/parse-css.h
1 013 строк
34 KB
Allan Banaag
Sync for validator cpp engine and cpp htmlparser (#39797)
06 фев 2024, 01:17
Не верифицирован
06 фев 2024, 01:17
6e751a4
Код
Авторство
О чём код?
// A C++ port of third_party/parse_css/parse-css.js, the original // Javascript implementation came from https://github.com/tabatkins/parse-css. // This library aims to implement the CSS3 syntax module. Some of the original // Javascript implementation has been removed. See b/143785355 for more // information. // // Typical usage (for Utf8ToCodepoints see utf8-util.h): // vector<char32_t> css = Utf8ToCodepoints("foo { bar: baz; }"); // vector<unique_ptr<ErrorToken>> errors; // vector<unique_ptr<Token>> tokens = Tokenize(css, 1, 0, &errors); // unique_ptr<Stylesheet> stylesheet = ParseAStylesheet( // &tokens, AmpCssParsingConfig(), &errors); // // Regarding the implementation of this library, a strange choice was // carried over from Javascript: Classes describing objects generated // by the parsing routines (Stylesheet, AtRule, etc.) extend from // Token, which in a sane world would be in a distinct // hierarchy. However the parsing routines sometimes instantiate a // temporary TokenStream and put some AST nodes into there. #ifndef CPP_HTMLPARSER_CSS_PARSE_CSS_H_ #define CPP_HTMLPARSER_CSS_PARSE_CSS_H_ #include <memory> #include <string> #include <unordered_map> #include <utility> #include <vector> #include "absl/memory/memory.h" #include "absl/strings/strip.h" #include "cpp/htmlparser/css/parse-css.pb.h" #include "cpp/htmlparser/json/types.h" #include "validator.pb.h" namespace htmlparser::css { // Implements 3.3. Preprocessing the input stream. // http://www.w3.org/TR/css-syntax-3/#input-preprocessing void Preprocess(std::vector<char32_t>* codepoints); // Superclass for all objects generated by this lexer/parser // combination. For each subclass, a distinct TokenType::Code (see // proto buffer) is passed in. The C++ code uses this where the Javascript // uses instanceof checks. class Token { public: explicit Token(TokenType::Code type) : type_(type), line_(1), col_(0), pos_(0) {} virtual ~Token() = default; virtual std::unique_ptr<Token> Clone() const; TokenType::Code Type() const { return type_; } // StringValue() returns the string representation of string portion of the // token. This isn't always the input that was given to create the token. virtual const std::string& StringValue() const; // ToString() returns the string representation of the input that was given // to create the token. virtual std::string ToString() const; // GroupingToken and its subclasses return true. virtual bool IsGroupingToken() const { return false; } // Renders the object as JSON. virtual htmlparser::json::JsonDict ToJson() const; void set_line(int line) { line_ = line; } void set_col(int col) { col_ = col; } int32_t line() const { return line_; } int32_t col() const { return col_; } // |pos| is redundant with |line|/|col|. In practice, |line|/|col| are used // for error messages but |pos| is useful for transformations. |pos| indicates // the position/offset in the original stylesheet at which this token starts; // that is, the character position (Unicode characters), not necessarily // the byte position. // // Note that |pos()| is the position after performing preprocessing, that is, // if you have '\r' in your input then this routine will yield the "wrong" // positions. Call quality_dni::parse_css::Preprocess or // strings::CleanStringLineEndings on your input to fix the problem. void set_pos(int pos) { pos_ = pos; } int32_t pos() const { return pos_; } // Propagates the start position of |this| to |other|. void CopyStartPositionTo(Token* other) const { other->set_line(line_); other->set_col(col_); other->set_pos(pos_); } private: TokenType::Code type_; int32_t line_; int32_t col_; int pos_; }; // The specific clone methods produce unique_ptr<Token> type returns. // In some cases, we need to clone to a specific token subclass, which // we can do with CloneToken and CloneTokens template <typename T> std::unique_ptr<T> CloneToken(const std::unique_ptr<T>& token) { return absl::WrapUnique<T>(static_cast<T*>(token->Clone().release())); } template <typename T> std::vector<std::unique_ptr<T>> CloneTokens( const std::vector<std::unique_ptr<T>>& tokens) { std::vector<std::unique_ptr<T>> clone; for (int ii = 0; ii < tokens.size(); ++ii) { clone.emplace_back(CloneToken(tokens[ii])); } return clone; } // A stream view of a vector of Token instances. class TokenStream { public: explicit TokenStream(std::vector<std::unique_ptr<Token>> tokens); const Token& TokenAt(int n) const; void Consume(int n = 1); const Token& Next(); void Reconsume(); const Token& Current() const; std::unique_ptr<Token> ReleaseCurrentOrCreateEof(); private: std::vector<std::unique_ptr<Token>> tokens_; int pos_; std::unique_ptr<Token> eof_; }; class WhitespaceToken : public Token { public: WhitespaceToken() : Token(TokenType::WHITESPACE) {} std::unique_ptr<Token> Clone() const override; }; class CDCToken : public Token { public: CDCToken() : Token(TokenType::CDC) {} std::unique_ptr<Token> Clone() const override; }; class CDOToken : public Token { public: CDOToken() : Token(TokenType::CDO) {} std::unique_ptr<Token> Clone() const override; }; class ColonToken : public Token { public: ColonToken() : Token(TokenType::COLON) {} std::unique_ptr<Token> Clone() const override; }; class SemicolonToken : public Token { public: SemicolonToken() : Token(TokenType::SEMICOLON) {} std::unique_ptr<Token> Clone() const override; }; class CommaToken : public Token { public: CommaToken() : Token(TokenType::COMMA) {} std::unique_ptr<Token> Clone() const override; }; // Grouping tokens are {}, [], (). class GroupingToken : public Token { public: GroupingToken(TokenType::Code type, const std::string& value, const std::string& mirror) : Token(type), value_(value), mirror_(mirror) {} std::unique_ptr<Token> Clone() const override; // The token we encountered. const std::string& StringValue() const override { return value_; } std::string ToString() const override; // The mirror token which would complete this grouping token. const std::string& Mirror() const { return mirror_; } // Predicate to distinguish grouping tokens. bool IsGroupingToken() const override { return true; } private: std::string value_; std::string mirror_; }; class OpenCurlyToken : public GroupingToken { public: OpenCurlyToken() : GroupingToken(TokenType::OPEN_CURLY, "{", "}") {} std::unique_ptr<Token> Clone() const override; }; class CloseCurlyToken : public GroupingToken { public: CloseCurlyToken() : GroupingToken(TokenType::CLOSE_CURLY, "}", "{") {} std::unique_ptr<Token> Clone() const override; }; class OpenSquareToken : public GroupingToken { public: OpenSquareToken() : GroupingToken(TokenType::OPEN_SQUARE, "[", "]") {} std::unique_ptr<Token> Clone() const override; }; class CloseSquareToken : public GroupingToken { public: CloseSquareToken() : GroupingToken(TokenType::CLOSE_SQUARE, "]", "[") {} std::unique_ptr<Token> Clone() const override; }; class OpenParenToken : public GroupingToken { public: OpenParenToken() : GroupingToken(TokenType::OPEN_PAREN, "(", ")") {} std::unique_ptr<Token> Clone() const override; }; class CloseParenToken : public GroupingToken { public: CloseParenToken() : GroupingToken(TokenType::CLOSE_PAREN, ")", "(") {} std::unique_ptr<Token> Clone() const override; }; class IncludeMatchToken : public Token { public: IncludeMatchToken() : Token(TokenType::INCLUDE_MATCH) {} std::unique_ptr<Token> Clone() const override; }; class DashMatchToken : public Token { public: DashMatchToken() : Token(TokenType::DASH_MATCH) {} std::unique_ptr<Token> Clone() const override; }; class PrefixMatchToken : public Token { public: PrefixMatchToken() : Token(TokenType::PREFIX_MATCH) {} std::unique_ptr<Token> Clone() const override; }; class SuffixMatchToken : public Token { public: SuffixMatchToken() : Token(TokenType::SUFFIX_MATCH) {} std::unique_ptr<Token> Clone() const override; }; class SubstringMatchToken : public Token { public: SubstringMatchToken() : Token(TokenType::SUBSTRING_MATCH) {} std::unique_ptr<Token> Clone() const override; }; class ColumnToken : public Token { public: ColumnToken() : Token(TokenType::COLUMN) {} std::unique_ptr<Token> Clone() const override; }; class EOFToken : public Token { public: EOFToken() : Token(TokenType::EOF_TOKEN) {} std::unique_ptr<Token> Clone() const override; }; class DelimToken : public Token { public: explicit DelimToken(char32_t code); std::unique_ptr<Token> Clone() const override; const std::string& StringValue() const override; std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; private: explicit DelimToken(const std::string& value); std::string value_; }; class StringValuedToken : public Token { public: StringValuedToken(const std::string& value, TokenType::Code type) : Token(type), value_(value) {} std::unique_ptr<Token> Clone() const override; const std::string& StringValue() const override; std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; private: std::string value_; }; class IdentToken : public StringValuedToken { public: explicit IdentToken(const std::string& val) : StringValuedToken(val, TokenType::IDENT) {} std::unique_ptr<Token> Clone() const override; }; class FunctionToken : public StringValuedToken { public: explicit FunctionToken(const std::string& val) : StringValuedToken(val, TokenType::FUNCTION_TOKEN) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; }; class AtKeywordToken : public StringValuedToken { public: explicit AtKeywordToken(const std::string& val) : StringValuedToken(val, TokenType::AT_KEYWORD) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; }; class HashToken : public StringValuedToken { public: HashToken(const std::string& val, const std::string& type) : StringValuedToken(val, TokenType::HASH), type_(type) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; private: std::string type_; }; class StringToken : public StringValuedToken { public: explicit StringToken(const std::string& val) : StringValuedToken(val, TokenType::STRING) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; }; class URLToken : public StringValuedToken { public: explicit URLToken(const std::string& val) : StringValuedToken(val, TokenType::URL) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; }; class NumberToken : public Token { public: NumberToken(const std::string& repr, const std::string& type, double value) : Token(TokenType::NUMBER), repr_(repr), type_(type), value_(value) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; private: std::string repr_; std::string type_; double value_; }; class PercentageToken : public Token { public: explicit PercentageToken(const std::string& repr, double value) : Token(TokenType::PERCENTAGE), repr_(repr), value_(value) {} std::unique_ptr<Token> Clone() const override; htmlparser::json::JsonDict ToJson() const override; const std::string& StringValue() const override; private: std::string repr_; double value_; }; class DimensionToken : public Token { public: DimensionToken(const std::string& type, const std::string& repr, const std::string& unit, double value) : Token(TokenType::DIMENSION), repr_(repr), type_(type), unit_(unit), value_(value) {} std::unique_ptr<Token> Clone() const override; std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; private: std::string repr_; std::string type_; std::string unit_; double value_; }; class ErrorToken : public Token { public: ErrorToken(amp::validator::ValidationError::Code code, const std::vector<std::string>& params) : Token(TokenType::ERROR), code_(code), params_(params) {} std::unique_ptr<Token> Clone() const override; htmlparser::json::JsonDict ToJson() const override; amp::validator::ValidationError::Code code() const { return code_; } const std::vector<std::string>& params() const { return params_; } private: const amp::validator::ValidationError::Code code_; const std::vector<std::string> params_; }; namespace internal { std::string_view StripVendorPrefix(absl::string_view prefixed_string); std::string_view StripMinMaxPrefix(absl::string_view prefixed_string); // Macro to generate method that may accept any string parameter of type // absl::string_view, std::string_view, std::string, char* and const char* #define TEMPLATED_METHOD_FOR_STRING_TYPES(PUBLIC_METHOD, INTERNAL_IMPL)\ /* absl::string_view specialization */\ template<class T, \ typename std::enable_if<std::is_same<T, absl::string_view>::value, \ bool>::type = true>\ std::string_view PUBLIC_METHOD(T prefixed_string) {\ return INTERNAL_IMPL(prefixed_string);\ }\ \ /* std::string, char* specialization */\ template<class T, typename std::enable_if<\ std::is_same<T, std::string>::value || \ std::is_same<char const*, typename std::decay<T>::type>::value || \ std::is_same<char*, typename std::decay<T>::type>::value, \ bool>::type = true>\ std::string_view PUBLIC_METHOD(const T& str) {\ absl::string_view prefixed_string(str);\ return INTERNAL_IMPL(prefixed_string);\ } } // namespace internal // Strips vendor prefixes from identifiers, e.g. property names or names // of at rules. E.g., "-moz-keyframes" -> "keyframes". TEMPLATED_METHOD_FOR_STRING_TYPES(StripVendorPrefix, internal::StripVendorPrefix); TEMPLATED_METHOD_FOR_STRING_TYPES(StripMinMaxPrefix, internal::StripMinMaxPrefix); class RuleVisitor; // The classes below this line are used for parsing. Note that they // also extend the Token class. class Rule : public Token { public: explicit Rule(TokenType::Code type) : Token(type) {} // Accept will first dispatch to the appropriate // visitor->VisitRuleType method, then recurse into the children of // this node which are Rule instances. virtual void Accept(RuleVisitor* visitor) const = 0; }; class Stylesheet : public Rule { public: explicit Stylesheet(std::vector<std::unique_ptr<Rule>> rules, std::unique_ptr<Token> eof) : Rule(TokenType::STYLESHEET), rules_(std::move(rules)), eof_(std::move(eof)) {} const std::vector<std::unique_ptr<Rule>>& rules() const { return rules_; } std::vector<std::unique_ptr<Rule>>* mutable_rules() { return &rules_; } const Token& eof() const { return *eof_; } htmlparser::json::JsonDict ToJson() const override; void Accept(RuleVisitor* visitor) const override; private: std::vector<std::unique_ptr<Rule>> rules_; std::unique_ptr<Token> eof_; }; class Declaration; class AtRule : public Rule { public: explicit AtRule(const std::string& name) : Rule(TokenType::AT_RULE), name_(name) {} htmlparser::json::JsonDict ToJson() const override; const std::string& name() const { return name_; } const std::vector<std::unique_ptr<Token>>& prelude() const { return prelude_; } const std::vector<std::unique_ptr<Rule>>& rules() const { return rules_; } std::vector<std::unique_ptr<Token>>* mutable_prelude() { return &prelude_; } std::vector<std::unique_ptr<Rule>>* mutable_rules() { return &rules_; } std::vector<std::unique_ptr<Declaration>>* mutable_declarations() { return &declarations_; } void Accept(RuleVisitor* visitor) const override; private: std::string name_; std::vector<std::unique_ptr<Token>> prelude_; std::vector<std::unique_ptr<Rule>> rules_; std::vector<std::unique_ptr<Declaration>> declarations_; }; class QualifiedRule : public Rule { public: QualifiedRule() : Rule(TokenType::QUALIFIED_RULE) {} htmlparser::json::JsonDict ToJson() const override; std::string RuleName() const; std::vector<std::unique_ptr<Token>>* mutable_prelude() { return &prelude_; } const std::vector<std::unique_ptr<Token>>& prelude() const; std::vector<std::unique_ptr<Declaration>>* mutable_declarations() { return &declarations_; } const std::vector<std::unique_ptr<Declaration>>& declarations() const { return declarations_; } void Accept(RuleVisitor* visitor) const override; private: std::vector<std::unique_ptr<Token>> prelude_; std::vector<std::unique_ptr<Declaration>> declarations_; }; class Declaration : public Rule { public: explicit Declaration(const std::string& name) : Rule(TokenType::DECLARATION), name_(name) {} std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; const std::string& name() const { return name_; } std::vector<std::unique_ptr<Token>>* mutable_value() { return &value_; } const std::vector<std::unique_ptr<Token>>& value() const { return value_; } // Records that this declaration was marked as important with `!important` // and stores the line/col of the `!important` marker. void set_important(int line, int col) { important_ = true; important_line_ = line; important_col_ = col; } bool important() const { return important_; } int32_t important_line() const { return important_line_; } int32_t important_col() const { return important_col_; } void Accept(RuleVisitor* visitor) const override; std::string FirstIdent() const; private: std::string name_; std::vector<std::unique_ptr<Token>> value_; bool important_ = false; int32_t important_line_ = -1; int32_t important_col_ = -1; }; // A visitor for Rule subclasses (StyleSheet, AtRule, QualifiedRule, // Declaration). Pass this to the Rule::Accept method. // Visitation order is to call the Visit* method on the current node, // then visit the children, then call the Leave* method on the current node. class RuleVisitor { public: virtual ~RuleVisitor() = default; virtual void VisitStylesheet(const Stylesheet& stylesheet) {} virtual void LeaveStylesheet(const Stylesheet& stylesheet) {} virtual void VisitAtRule(const AtRule& at_rule) {} virtual void LeaveAtRule(const AtRule& at_rule) {} virtual void VisitQualifiedRule(const QualifiedRule& qualified_rule) {} virtual void LeaveQualifiedRule(const QualifiedRule& qualified_rule) {} virtual void VisitDeclaration(const Declaration& declaration) {} virtual void LeaveDeclaration(const Declaration& declaration) {} }; // Tokenizes the provided input string. Note: may mutate the input string. std::vector<std::unique_ptr<Token>> Tokenize( std::vector<char32_t>* str_in, int line, int col, std::vector<std::unique_ptr<ErrorToken>>* errors); // Specifies how at rules are to be handled by the parser. struct CssParsingConfig { std::unordered_map<std::string, BlockType::Code> at_rule_spec; BlockType::Code default_spec = BlockType::PARSE_AS_IGNORE; }; // Returns a Stylesheet object with nested CSSParserRules. // The top level CSSParserRules in a Stylesheet are always a series of // QualifiedRule's or AtRule's. // // |tokens| is the tokenized input (call Tokenize if needed). // |config.at_rule_spec| are the block type rules for all CSS AT rules this // canonicalizer should handle. // |config.default_spec| default block type for types not found in atRuleSpec. // |errors| output array for the errors to which the function will append. std::unique_ptr<Stylesheet> ParseAStylesheet( std::vector<std::unique_ptr<Token>>* tokens, const CssParsingConfig& config, std::vector<std::unique_ptr<ErrorToken>>* errors); // Returns a list of Declaration objects. // // |tokens| is the tokenized input (call Tokenize if needed). // |errors| output array for the errors to which the function will append. std::vector<std::unique_ptr<Declaration>> ParseInlineStyle( std::vector<std::unique_ptr<Token>>* tokens, std::vector<std::unique_ptr<ErrorToken>>* errors); // Used by |ExtractUrls| to return urls it has seen. This represents // URLs in CSS such as url(http://foo.com/) and url("http://bar.com/"). // For this token, pos(), line(), col() indicate the position information // of the left-most CSS token that's part of the URL. E.g., this would be // the URLToken instance or the FunctionToken instance. // end_pos() is provided in addition. class ParsedCssUrl : public Token { public: ParsedCssUrl() : Token(TokenType::PARSED_CSS_URL), end_pos_(0) {} std::unique_ptr<Token> Clone() const override; // The position past the right-most CSS token that's part of the URL, // that is, after the closing paren. int end_pos() const { return end_pos_; } void set_end_pos(int end_pos) { end_pos_ = end_pos; } // The decoded URL. This string will not contain CSS string escapes, // quotes, or similar. Encoding is utf8. const std::string& utf8_url() const { return utf8_url_; } void set_utf8_url(const std::string& utf8_url) { utf8_url_ = utf8_url; } // A rule scope, in case the url was encountered within an at-rule. // If not within an at-rule, this string is empty. const std::string& at_rule_scope() const { return at_rule_scope_; } void set_at_rule_scope(const std::string& at_rule_scope) { at_rule_scope_ = at_rule_scope; } htmlparser::json::JsonDict ToJson() const override; private: explicit ParsedCssUrl(int end_pos) : Token(TokenType::PARSED_CSS_URL), end_pos_(end_pos) {} int end_pos_; std::string utf8_url_; std::string at_rule_scope_; }; // Extracts the URLs within |stylesheet|, emitting them into |parsed_urls| // and errors into |errors|. void ExtractUrls(const Stylesheet& stylesheet, std::vector<std::unique_ptr<ParsedCssUrl>>* parsed_urls, std::vector<std::unique_ptr<ErrorToken>>* errors); // Same as the stylesheet variant above, but operates on a single declaration at // time. Useful when operating on parsed style attributes. void ExtractUrls(const Declaration& declaration, std::vector<std::unique_ptr<ParsedCssUrl>>* parsed_urls, std::vector<std::unique_ptr<ErrorToken>>* errors); // Extracts the declarations marked !important within |stylesheet|, // emitting them into |important|. void ExtractImportantDeclarations(const Stylesheet& stylesheet, std::vector<const Declaration*>* important); // Parses media queries within |stylesheet|, emitting the set of discovered // media types and features into |media_types| and |media_features|, and // errors into |errors|. void ParseMediaQueries(const Stylesheet& stylesheet, std::vector<std::unique_ptr<Token>>* media_types, std::vector<std::unique_ptr<Token>>* media_features, std::vector<std::unique_ptr<ErrorToken>>* errors); class AttrSelector; class ClassSelector; class IdSelector; class TypeSelector; class SelectorVisitor; // Abstract super class for CSS Selectors. The Token class, which this // class inherits from, has line, col, and tokenType fields. class Selector : public Token { public: explicit Selector(TokenType::Code type); virtual void ForEachChild(std::function<void(const Selector&)> lambda) const; virtual void Accept(SelectorVisitor* visitor) const = 0; }; // This node models type selectors and universial selectors. // http://www.w3.org/TR/css3-selectors/#type-selectors // http://www.w3.org/TR/css3-selectors/#universal-selector class TypeSelector : public Selector { public: // Choices for |namespace_prefix|: // - 'a specific namespace prefix' means 'just that specific namespace'. // - '' means 'without a namespace' // - '*' means 'any namespace including without a namespace' // - null means the default namespace if one is declared, and '*' otherwise. // // The universal selector is covered by setting the |element_name| to '*'. TypeSelector(std::unique_ptr<std::string> namespace_prefix, const std::string& element_name); std::unique_ptr<Token> Clone() const override; // The position past the right-most CSS token that's part of the // type selector, that is, after the element name. int end_pos() const { return end_pos_; } void set_end_pos(int end_pos) { end_pos_ = end_pos; } const std::string& element_name() const { return element_name_; } const std::string* namespace_prefix() const { return namespace_prefix_.get(); } std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; private: int end_pos_; const std::unique_ptr<std::string> namespace_prefix_; const std::string element_name_; }; // token_stream->Current() is the first token of the type selector. std::unique_ptr<TypeSelector> ParseATypeSelector(TokenStream* token_stream); // An ID selector references some document id. // http://www.w3.org/TR/css3-selectors/#id-selectors // Typically written as '#foo'. class IdSelector : public Selector { public: explicit IdSelector(const std::string& value); std::unique_ptr<Token> Clone() const override; int end_pos() const { return end_pos_; } void set_end_pos(int end_pos) { end_pos_ = end_pos; } const std::string& value() const { return value_; } std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; private: int end_pos_; const std::string value_; }; // token_stream->Current() must be the hash token. std::unique_ptr<IdSelector> ParseAnIdSelector(TokenStream* token_stream); // An attribute selector matches document nodes based on their attributes. // http://www.w3.org/TR/css3-selectors/#attribute-selectors // // Typically written as '[foo=bar]'. class AttrSelector : public Selector { public: // |match_operator| is either the string representation of the match // operator (e.g., '=' or '~=') or '', in which case the attribute // selector is a check for the presence of the attribute. // |value| is the value to apply the match operator against, or if // matchOperator is '', then this must be empty as well. AttrSelector(std::unique_ptr<std::string> namespace_prefix, const std::string& attr_name, const std::string& match_operator, const std::string& value); std::unique_ptr<Token> Clone() const override; int value_start_pos() const { return value_start_pos_; } void set_value_start_pos(int value_start_pos) { value_start_pos_ = value_start_pos; } int value_end_pos() const { return value_end_pos_; } void set_value_end_pos(int value_end_pos) { value_end_pos_ = value_end_pos; } const std::string& attr_name() const { return attr_name_; } const std::string& match_operator() const { return match_operator_; } const std::string& value() const { return value_; } htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; private: int value_start_pos_; int value_end_pos_; const std::unique_ptr<std::string> namespace_prefix_; const std::string attr_name_; const std::string match_operator_; const std::string value_; }; // A pseudo selector can match either pseudo classes or pseudo elements. // http://www.w3.org/TR/css3-selectors/#pseudo-classes // http://www.w3.org/TR/css3-selectors/#pseudo-elements. // // Typically written as ':visited', ':lang(fr)', and '::first-line'. class PseudoSelector : public Selector { public: // |is_class|: Pseudo selectors with a single colon (e.g., ':visited') // are pseudo class selectors. Selectors with two colons (e.g., // '::first-line') are pseudo elements. // |func|: If it's a function style pseudo selector, like lang(fr), then func // is the function tokens. PseudoSelector(bool is_class, const std::string& name, const htmlparser::json::JsonArray& func); std::unique_ptr<Token> Clone() const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; const std::string& name() const { return name_; } bool is_pseudo_class() const { return is_class_; } bool is_pseudo_element() const { return !is_class_; } private: const bool is_class_; const std::string name_; const htmlparser::json::JsonArray func_; }; // A class selector of the form '.value' is a shorthand notation for // an attribute match of the form '[class~=value]'. // http://www.w3.org/TR/css3-selectors/#class-html class ClassSelector : public Selector { public: // |value| denotes the class to match. explicit ClassSelector(const std::string& value); std::unique_ptr<Token> Clone() const override; int end_pos() const { return end_pos_; } void set_end_pos(int end_pos) { end_pos_ = end_pos; } const std::string& value() const { return value_; } std::string ToString() const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; private: int end_pos_; const std::string value_; }; // token_stream->Current() must be the '.' delimiter token. std::unique_ptr<ClassSelector> ParseAClassSelector(TokenStream* token_stream); // Models a simple selector sequence, e.g. '*|foo#id'. class SimpleSelectorSequence : public Selector { public: SimpleSelectorSequence( std::unique_ptr<TypeSelector> type_selector, std::vector<std::unique_ptr<Selector>> other_selectors); std::unique_ptr<Token> Clone() const override; void ForEachChild(std::function<void(const Selector&)> lambda) const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; private: std::unique_ptr<TypeSelector> type_selector_; std::vector<std::unique_ptr<Selector>> other_selectors_; }; // Shortens the return type declarations for the parsing routines, // E.g. ErrorTokenOr<Result> indicates the return type is either // unique_ptr<ErrorToken> or unique_ptr<Result>. template <class Result> using ErrorTokenOr = std::variant<std::unique_ptr<ErrorToken>, std::unique_ptr<Result>>; // token_stream->Current() must be the first token of the sequence. // This function will return an error if no selector is found. // Returns either a SimpleSelectorSequence or an ErrorToken. ErrorTokenOr<SimpleSelectorSequence> ParseASimpleSelectorSequence( TokenStream* token_stream); // Models a combinator, as described in // http://www.w3.org/TR/css3-selectors/#combinators. class Combinator : public Selector { public: Combinator(CombinatorType::Code combinator_type, std::unique_ptr<Selector> left, std::unique_ptr<SimpleSelectorSequence> right); std::unique_ptr<Token> Clone() const override; void ForEachChild(std::function<void(const Selector&)> lambda) const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; CombinatorType::Code combinator_type() const; private: CombinatorType::Code combinator_type_; // Either SimpleSelectorSequence or Combinator. std::unique_ptr<Selector> left_; std::unique_ptr<SimpleSelectorSequence> right_; }; // The selector production from // http://www.w3.org/TR/css3-selectors/#grammar // Returns an ErrorToken if no selector is found; otherwise returns a // SimpleSelectorSequence or Combinator. ErrorTokenOr<Selector> ParseASelector(TokenStream* token_stream); // Models a selectors group, as described in // http://www.w3.org/TR/css3-selectors/#grouping. class SelectorsGroup : public Selector { public: explicit SelectorsGroup(std::vector<std::unique_ptr<Selector>> elements); std::unique_ptr<Token> Clone() const override; void ForEachChild(std::function<void(const Selector&)> lambda) const override; htmlparser::json::JsonDict ToJson() const override; void Accept(SelectorVisitor* visitor) const override; private: std::vector<std::unique_ptr<Selector>> elements_; }; // The selectors_group production from // http://www.w3.org/TR/css3-selectors/#grammar. // In addition, this parsing routine checks that no input remains, // that is, after parsing the production we reached the end of |token_stream|. // Returns an ErrorToken if no selector is found; otherwise returns a // SelectorsGroup, SimpleSelectorSequence, or Combinator. ErrorTokenOr<Selector> ParseASelectorsGroup(TokenStream* token_stream); // A super class for making visitors (by overriding the types of interest) of // Selector fields. The standard RuleVisitor does not recursively parse the // prelude of qualified rules for the components of a selectors. This Visitor // re-parses these preludes and then visits the fields within. The parse step // has the possibility of emitting new CSS ErrorTokens. class SelectorVisitor : public RuleVisitor { public: explicit SelectorVisitor(std::vector<std::unique_ptr<ErrorToken>>* errors) : errors_(errors) {} virtual ~SelectorVisitor() = default; virtual void VisitTypeSelector(const TypeSelector& selector) {} virtual void VisitIdSelector(const IdSelector& selector) {} virtual void VisitAttrSelector(const AttrSelector& selector) {} virtual void VisitPseudoSelector(const PseudoSelector& selector) {} virtual void VisitClassSelector(const ClassSelector& selector) {} virtual void VisitSimpleSelectorSequence( const SimpleSelectorSequence& sequence) {} virtual void VisitCombinator(const Combinator& combinator) {} virtual void VisitSelectorsGroup(const SelectorsGroup& group) {} private: void VisitStylesheet(const Stylesheet& stylesheet) final {} void LeaveStylesheet(const Stylesheet& stylesheet) final {} void VisitAtRule(const AtRule& at_rule) final {} void LeaveAtRule(const AtRule& at_rule) final {} void VisitQualifiedRule(const QualifiedRule& qualified_rule) final; void LeaveQualifiedRule(const QualifiedRule& qualified_rule) final {} void VisitDeclaration(const Declaration& declaration) final {} void LeaveDeclaration(const Declaration& declaration) final {} std::vector<std::unique_ptr<ErrorToken>>* errors_; }; } // namespace htmlparser::css #endif // CPP_HTMLPARSER_CSS_PARSE_CSS_H_