float_pigment_css/parser/
mod.rs

1//! The CSS parser module.
2
3use alloc::{
4    borrow::ToOwned,
5    boxed::Box,
6    rc::Rc,
7    string::{String, ToString},
8    vec::Vec,
9};
10
11use cssparser::{
12    match_ignore_ascii_case, parse_important, parse_nth, BasicParseError, Delimiter, ParseError,
13    ParseErrorKind, Parser, ParserInput, SourceLocation, SourcePosition, Token,
14};
15use cssparser::{BasicParseErrorKind, CowRcStr};
16
17use self::property_value::font::{font_display, font_face_src, font_family_name};
18use crate::property::*;
19use crate::sheet::*;
20use crate::typing::*;
21
22pub mod hooks;
23pub(crate) mod property_value;
24
25pub(crate) const DEFAULT_INPUT_CSS_EXTENSION: &str = ".wxss";
26pub(crate) const DEFAULT_OUTPUT_CSS_EXTENSION: &str = "";
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29#[allow(dead_code)]
30pub(crate) enum CustomError {
31    Unmatched,
32    UnsupportedProperty,
33    SkipErrorBlock,
34    Unsupported,
35    Eop,
36    Reason(String),
37    VariableCycle(String, bool),
38    UnexpectedTokenInAttributeSelector,
39    BadValueInAttr,
40}
41
42/// Warning kind.
43#[allow(missing_docs)]
44#[repr(u32)]
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum WarningKind {
47    Unknown = 0x10000,
48    HooksGenerated,
49    SerializationFailed,
50    DeserializationFailed,
51    UnsupportedSegment,
52    UnknownAtBlock,
53    InvalidMediaExpression,
54    UnsupportedMediaSyntax,
55    InvalidImportURL,
56    MissingImportTarget,
57    RecursiveImports,
58    ImportNotOnTop,
59    IllegalKeyframesBlock,
60    IllegalKeyframesIdentifier,
61    UnsupportedKeyframesSyntax,
62    InvalidFontFaceProperty,
63    InvalidSelector,
64    UnsupportedSelector,
65    InvalidPseudoElement,
66    UnsupportedPseudoElement,
67    InvalidPseudoClass,
68    UnsupportedPseudoClass,
69    InvalidProperty,
70    UnsupportedProperty,
71    MissingColonAfterProperty,
72    InvalidEnvDefaultValue,
73}
74
75impl WarningKind {
76    /// Get the error code.
77    pub fn code(&self) -> u32 {
78        *self as u32
79    }
80
81    /// Get a brief message of the error.
82    pub fn static_message(&self) -> &'static str {
83        match self {
84            Self::Unknown => "unknown error",
85            Self::HooksGenerated => "warning from hooks",
86            Self::SerializationFailed => "failed during serialization",
87            Self::DeserializationFailed => "failed during deserialization",
88            Self::UnsupportedSegment => "unsupported segment",
89            Self::UnknownAtBlock => "unknown at-block",
90            Self::InvalidMediaExpression => "invalid media expression",
91            Self::UnsupportedMediaSyntax => "unsupported media syntax",
92            Self::InvalidImportURL => "invalid @import URL",
93            Self::ImportNotOnTop => "@import should appear before any other code blocks",
94            Self::MissingImportTarget => "@import source not found",
95            Self::RecursiveImports => "recursive @import",
96            Self::IllegalKeyframesBlock => "illegal keyframes block",
97            Self::IllegalKeyframesIdentifier => "illegal keyframes identifier",
98            Self::UnsupportedKeyframesSyntax => "unsupported keyframes syntax",
99            Self::InvalidFontFaceProperty => "invalid property inside @font-face",
100            Self::InvalidSelector => "invalid selector",
101            Self::UnsupportedSelector => "unsupported selector",
102            Self::InvalidPseudoElement => "invalid pseudo element",
103            Self::UnsupportedPseudoElement => "unsupported pseudo element",
104            Self::InvalidPseudoClass => "invalid pseudo class",
105            Self::UnsupportedPseudoClass => "unsupported pseudo class",
106            Self::InvalidProperty => "invalid property",
107            Self::UnsupportedProperty => "unsupported property",
108            Self::MissingColonAfterProperty => "missing colon after property",
109            Self::InvalidEnvDefaultValue => "the default value of `env()` is invalid",
110        }
111    }
112}
113
114impl core::fmt::Display for WarningKind {
115    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116        write!(f, "{}", self.static_message())
117    }
118}
119
120/// Warnings generated while parsing.
121#[repr(C)]
122#[derive(Clone, PartialEq)]
123pub struct Warning {
124    /// The category of the warning, which has a corresponding error code.
125    pub kind: WarningKind,
126    /// The detailed message.
127    pub message: str_store::StrRef,
128    /// The start line.
129    pub start_line: u32,
130    /// The start column in UTF-16 word.
131    pub start_col: u32,
132    /// The end line.
133    pub end_line: u32,
134    /// The end column in UTF-16 word.
135    pub end_col: u32,
136}
137
138impl core::fmt::Debug for Warning {
139    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
140        write!(
141            f,
142            r#"Warning({} from line {} column {} to line {} column {}, #{})"#,
143            self.message.as_str(),
144            self.start_line,
145            self.start_col,
146            self.end_line,
147            self.end_col,
148            self.kind as u32
149        )
150    }
151}
152
153fn is_url(path: &str) -> bool {
154    if path.starts_with("//") {
155        return true;
156    }
157    // the URL protocol format is /[a-z][-+.a-z0-9]+/i
158    let mut byte_iter = path.as_bytes().iter();
159    let Some(c) = byte_iter.next() else {
160        return false;
161    };
162    if c.to_ascii_lowercase().is_ascii_lowercase() {
163        while let Some(c) = byte_iter.next() {
164            if *c == b'-' {
165                continue;
166            }
167            if *c == b'+' {
168                continue;
169            }
170            if *c == b'.' {
171                continue;
172            }
173            if c.is_ascii_lowercase() {
174                continue;
175            }
176            if c.is_ascii_uppercase() {
177                continue;
178            }
179            if c.is_ascii_digit() {
180                continue;
181            }
182            if *c == b':' {
183                return true;
184            }
185            break;
186        }
187    }
188    false
189}
190
191fn resolve_relative_path(
192    base: &str,
193    rel: &str,
194    input_extension: &str,
195    output_extension: &str,
196) -> String {
197    let absolute_path = crate::path::resolve(base, rel);
198    if input_extension.is_empty() && output_extension.is_empty() {
199        return absolute_path;
200    }
201    if let Some(s) = absolute_path.strip_suffix(input_extension) {
202        return format!("{s}{output_extension}");
203    }
204    if absolute_path.ends_with(output_extension) {
205        return absolute_path;
206    }
207    absolute_path + output_extension
208}
209
210pub(crate) struct ParseState {
211    import_base_path: Option<String>,
212    media: Option<Rc<Media>>,
213    warnings: Vec<Warning>,
214    debug_mode: StyleParsingDebugMode,
215    hooks: Option<Box<dyn hooks::Hooks>>,
216}
217
218impl ParseState {
219    pub(crate) fn new(
220        import_base_path: Option<String>,
221        debug_mode: StyleParsingDebugMode,
222        hooks: Option<Box<dyn hooks::Hooks>>,
223    ) -> Self {
224        Self {
225            import_base_path,
226            media: None,
227            warnings: vec![],
228            debug_mode,
229            hooks,
230        }
231    }
232
233    pub(crate) fn add_warning(
234        &mut self,
235        kind: WarningKind,
236        start: SourceLocation,
237        end: SourceLocation,
238    ) {
239        self.warnings.push(Warning {
240            kind,
241            message: kind.static_message().into(),
242            start_line: start.line,
243            start_col: start.column,
244            end_line: end.line,
245            end_col: end.column,
246        })
247    }
248
249    pub(crate) fn add_warning_with_message(
250        &mut self,
251        kind: WarningKind,
252        message: impl Into<String>,
253        start: SourceLocation,
254        end: SourceLocation,
255    ) {
256        self.warnings.push(Warning {
257            kind,
258            message: message.into().into(),
259            start_line: start.line,
260            start_col: start.column,
261            end_line: end.line,
262            end_col: end.column,
263        })
264    }
265}
266
267/// Parse string into a style sheet, returning it with warnings.
268pub(crate) fn parse_style_sheet(path: &str, source: &str) -> (CompiledStyleSheet, Vec<Warning>) {
269    parse_style_sheet_with_hooks(path, source, None)
270}
271
272/// Parse string into a style sheet, returning it with warnings.
273///
274/// Parser hooks can be attached in this function.
275pub(crate) fn parse_style_sheet_with_hooks(
276    path: &str,
277    source: &str,
278    hooks: Option<Box<dyn hooks::Hooks>>,
279) -> (CompiledStyleSheet, Vec<Warning>) {
280    let mut parser_input = ParserInput::new(source);
281    let mut parser = Parser::new(&mut parser_input);
282    let mut sheet = CompiledStyleSheet::new();
283    let mut state = ParseState::new(Some(path.into()), StyleParsingDebugMode::None, hooks);
284    parse_segment(&mut parser, &mut sheet, &mut state);
285    (sheet, state.warnings)
286}
287
288/// The debug mode used in style parsing.
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub enum StyleParsingDebugMode {
291    /// Disable debug mode (best performance).
292    None,
293    /// Enable debug mode.
294    Debug,
295    /// Enable debug mode and mark all parsed properties disabled.
296    DebugAndDisabled,
297}
298
299/// Parse inline style for an element, a.k.a. the style in `<div style="...">`.
300pub fn parse_inline_style(
301    source: &str,
302    debug_mode: StyleParsingDebugMode,
303) -> (Vec<PropertyMeta>, Vec<Warning>) {
304    let mut trim_source = source.trim().to_string();
305    if !trim_source.ends_with(';') {
306        trim_source.push(';');
307    }
308    let mut parser_input = ParserInput::new(trim_source.as_str());
309    let mut parser = Parser::new(&mut parser_input);
310    let mut properties = vec![];
311    let mut state: ParseState = ParseState::new(None, debug_mode, None);
312    parse_property_list(&mut parser, &mut properties, &mut state, None);
313    (properties, state.warnings)
314}
315
316/// Parse a string of the property value of the specified `property_name`.
317pub fn parse_property_value_string(
318    property_name: &str,
319    source: &str,
320) -> (Vec<PropertyMeta>, Vec<Warning>) {
321    let mut parser_input = ParserInput::new(source);
322    let mut parser = Parser::new(&mut parser_input);
323    let mut properties = vec![];
324    let mut state: ParseState = ParseState::new(None, StyleParsingDebugMode::None, None);
325    let _ = parse_property_value(
326        &mut parser,
327        property_name,
328        &mut properties,
329        &mut state,
330        None,
331    );
332    (properties, state.warnings)
333}
334
335pub(crate) fn parse_selector_only(source: &str) -> Result<Selector, Warning> {
336    let mut parser_input = ParserInput::new(source);
337    let mut parser = Parser::new(&mut parser_input);
338    let mut state = ParseState::new(None, StyleParsingDebugMode::None, None);
339    parse_selector(&mut parser, &mut state).map_err(|_| {
340        let cur = parser.current_source_location();
341        Warning {
342            kind: WarningKind::InvalidSelector,
343            message: WarningKind::InvalidSelector.to_string().into(),
344            start_line: cur.line,
345            start_col: cur.column,
346            end_line: cur.line,
347            end_col: cur.column,
348        }
349    })
350}
351
352pub(crate) fn parse_media_expression_only(source: &str) -> Result<Media, Warning> {
353    let mut parser_input = ParserInput::new(source);
354    let mut parser = Parser::new(&mut parser_input);
355    let mut state = ParseState::new(None, StyleParsingDebugMode::None, None);
356    parse_media_expression_series(&mut parser, &mut state).map_err(|_| {
357        let cur = parser.current_source_location();
358        Warning {
359            kind: WarningKind::InvalidMediaExpression,
360            message: WarningKind::InvalidMediaExpression.to_string().into(),
361            start_line: cur.line,
362            start_col: cur.column,
363            end_line: cur.line,
364            end_col: cur.column,
365        }
366    })
367}
368
369#[allow(dead_code)]
370pub(crate) fn parse_color_to_rgba(source: &str) -> (u8, u8, u8, u8) {
371    let mut parser_input = ParserInput::new(source);
372    let mut parser = Parser::new(&mut parser_input);
373    let ret = cssparser_color::Color::parse(&mut parser);
374    ret.map(|color| match color {
375        cssparser_color::Color::Rgba(rgba) => {
376            (rgba.red, rgba.green, rgba.blue, (rgba.alpha * 256.) as u8)
377        }
378        _ => (0, 0, 0, 0),
379    })
380    .unwrap_or((0, 0, 0, 0))
381}
382
383fn parse_segment<'a, 't: 'a, 'i: 't>(
384    parser: &'a mut Parser<'i, 't>,
385    sheet: &mut CompiledStyleSheet,
386    st: &mut ParseState,
387) {
388    while !parser.is_exhausted() {
389        parse_block(parser, sheet, st);
390    }
391}
392
393fn parse_to_paren_end<'a, 't: 'a, 'i: 't>(
394    parser: &'a mut Parser<'i, 't>,
395    need_warning: bool,
396    st: &mut ParseState,
397) {
398    parser.skip_whitespace();
399    let start = parser.current_source_location();
400    let mut has_extra_chars = false;
401    loop {
402        let next = match parser.next() {
403            Ok(x) => x,
404            Err(_) => break,
405        };
406        match next {
407            Token::CloseParenthesis => {
408                break;
409            }
410            _ => {
411                has_extra_chars = true;
412            }
413        }
414    }
415    if need_warning && has_extra_chars {
416        let end = parser.current_source_location();
417        st.add_warning(WarningKind::UnsupportedSegment, start, end);
418    }
419}
420
421// may replace
422fn parse_to_block_end<'a, 't: 'a, 'i: 't>(
423    parser: &'a mut Parser<'i, 't>,
424    need_warning: bool,
425    st: &mut ParseState,
426) {
427    parser.skip_whitespace();
428    let start = parser.current_source_location();
429    let mut has_extra_chars = false;
430    loop {
431        let next = match parser.next() {
432            Ok(x) => x,
433            Err(_) => break,
434        };
435        match next {
436            Token::Semicolon => {
437                break;
438            }
439            Token::CurlyBracketBlock => {
440                break;
441            }
442            _ => {
443                has_extra_chars = true;
444            }
445        }
446    }
447    if need_warning && has_extra_chars {
448        let end = parser.current_source_location();
449        st.add_warning(WarningKind::UnsupportedSegment, start, end);
450    }
451}
452
453fn parse_block<'a, 't: 'a, 'i: 't>(
454    parser: &'a mut Parser<'i, 't>,
455    sheet: &mut CompiledStyleSheet,
456    st: &mut ParseState,
457) {
458    parser
459        .try_parse(|parser| {
460            // try parsing at keyword
461            if let Token::AtKeyword(k) = parser.next()?.clone() {
462                parse_at_keyword_block(parser, &k, sheet, st);
463                Ok(())
464            } else {
465                Err(parser.new_custom_error(CustomError::Unmatched))
466            }
467        })
468        .or_else(|err: ParseError<'_, CustomError>| {
469            st.import_base_path = None;
470            if let ParseErrorKind::Custom(err) = err.kind {
471                if CustomError::Unmatched == err {
472                    let rule = parse_rule(parser, st)?;
473                    sheet.add_rule(rule);
474                    return Ok(());
475                }
476                return Err(parser.new_custom_error(CustomError::Unmatched));
477            }
478            Err(parser.new_custom_error(CustomError::Unmatched))
479        })
480        .unwrap_or(())
481}
482
483fn parse_at_keyword_block<'a, 't: 'a, 'i: 't>(
484    parser: &'a mut Parser<'i, 't>,
485    key: &str,
486    sheet: &mut CompiledStyleSheet,
487    st: &mut ParseState,
488) {
489    if !(key == "import" || key == "font-face") {
490        st.import_base_path = None;
491    }
492    match key {
493        "import" => {
494            parser.skip_whitespace();
495            let start = parser.current_source_location();
496            match parser.expect_url_or_string() {
497                Err(_) => {
498                    parse_to_block_end(parser, false, st);
499                    st.add_warning(
500                        WarningKind::InvalidImportURL,
501                        start,
502                        parser.current_source_location(),
503                    );
504                }
505                Ok(url) => {
506                    let media = parser
507                        .try_parse::<_, _, ParseError<CustomError>>(|parser| {
508                            parser.expect_semicolon()?;
509                            Ok(None)
510                        })
511                        .unwrap_or_else(|_| {
512                            let media = parse_media_expression_series(parser, st);
513                            match media {
514                                Err(err) => {
515                                    parse_to_block_end(parser, false, st);
516                                    st.add_warning(
517                                        WarningKind::UnsupportedMediaSyntax,
518                                        err.location,
519                                        err.location,
520                                    );
521                                    None
522                                }
523                                Ok(media) => {
524                                    parse_to_block_end(parser, true, st);
525                                    Some(Rc::new(media))
526                                }
527                            }
528                        });
529                    if let Some(base_path) = st.import_base_path.clone() {
530                        let url: &str = &url;
531                        if is_url(url) {
532                            sheet.add_import(url.to_string(), media);
533                        } else {
534                            let path = resolve_relative_path(
535                                base_path.as_str(),
536                                url,
537                                DEFAULT_INPUT_CSS_EXTENSION,
538                                DEFAULT_OUTPUT_CSS_EXTENSION,
539                            );
540                            sheet.add_import(path, media);
541                        }
542                    } else {
543                        st.add_warning(
544                            WarningKind::ImportNotOnTop,
545                            start,
546                            parser.current_source_location(),
547                        );
548                    }
549                }
550            }
551        }
552        "media" => {
553            parse_media_block(parser, sheet, st);
554        }
555        // IDEA support @keyframes
556        "keyframes" => {
557            parse_keyframes_block(parser, sheet, st);
558        }
559        "font-face" => {
560            parse_font_face_block(parser, sheet, st);
561        }
562        _ => {
563            parser.skip_whitespace();
564            let start = parser.current_source_location();
565            parse_to_block_end(parser, false, st);
566            st.add_warning_with_message(
567                WarningKind::UnknownAtBlock,
568                format!(r#"unsupported @{key} block"#),
569                start,
570                parser.current_source_location(),
571            );
572        }
573    }
574}
575
576fn str_to_media_type(s: &str) -> Option<MediaType> {
577    let s = s.to_lowercase();
578    match s.as_str() {
579        "all" => Some(MediaType::All),
580        "screen" => Some(MediaType::Screen),
581        _ => None,
582    }
583}
584
585fn parse_media_block<'a, 't: 'a, 'i: 't>(
586    parser: &'a mut Parser<'i, 't>,
587    sheet: &mut CompiledStyleSheet,
588    st: &mut ParseState,
589) {
590    match parse_media_expression_series(parser, st) {
591        Err(err) => {
592            parse_to_block_end(parser, false, st);
593            st.add_warning(
594                WarningKind::UnsupportedMediaSyntax,
595                err.location,
596                err.location,
597            );
598        }
599        Ok(media) => {
600            if parser.expect_curly_bracket_block().is_ok() {
601                let old_media = st.media.take();
602                st.media = Some(Rc::new(media));
603                parser
604                    .parse_nested_block::<_, _, ParseError<'i, CustomError>>(|parser| {
605                        parse_segment(parser, sheet, st);
606                        Ok(())
607                    })
608                    .unwrap();
609                st.media = old_media;
610            }
611        }
612    }
613}
614
615fn parse_media_expression_series<'a, 't: 'a, 'i: 't>(
616    parser: &'a mut Parser<'i, 't>,
617    st: &mut ParseState,
618) -> Result<Media, ParseError<'i, CustomError>> {
619    let mut media = Media::new(st.media.clone());
620    parser.parse_until_before(
621        Delimiter::CurlyBracketBlock | Delimiter::Semicolon,
622        |parser| {
623            parser.parse_comma_separated(|parser| {
624                let mut mq = MediaQuery::new();
625                let next = parser.next()?.clone();
626                match &next {
627                    Token::Ident(s) => {
628                        let s = s.to_owned().to_lowercase();
629                        match s.as_str() {
630                            "only" => {
631                                mq.set_decorator(MediaTypeDecorator::Only);
632                                let expr = parse_media_expression(parser, st)?;
633                                mq.add_media_expression(expr);
634                            }
635                            "not" => {
636                                mq.set_decorator(MediaTypeDecorator::Not);
637                                let expr = parse_media_expression(parser, st)?;
638                                mq.add_media_expression(expr);
639                            }
640                            _ => match str_to_media_type(&s) {
641                                Some(mt) => mq.add_media_expression(MediaExpression::MediaType(mt)),
642                                None => mq.add_media_expression(MediaExpression::Unknown),
643                            },
644                        }
645                    }
646                    Token::ParenthesisBlock => {
647                        let expr = parse_media_expression_inner(parser, st)?;
648                        mq.add_media_expression(expr);
649                    }
650                    _ => {
651                        return Err(parser.new_unexpected_token_error(next));
652                    }
653                }
654                loop {
655                    match parser.try_parse(|parser| {
656                        if parser.is_exhausted() {
657                            return Err(parser.new_custom_error(CustomError::Unmatched));
658                        }
659                        let next = parser.next()?;
660                        if let Token::Ident(s) = next {
661                            let s = s.to_lowercase();
662                            if s.as_str() == "and" {
663                                let expr = parse_media_expression(parser, st)?;
664                                mq.add_media_expression(expr);
665                                return Ok(());
666                            }
667                        }
668                        Err(parser.new_custom_error(CustomError::Unmatched))
669                    }) {
670                        Ok(_) => {}
671                        Err(err) => {
672                            if let ParseErrorKind::Custom(err) = &err.kind {
673                                if CustomError::Unmatched == *err {
674                                    break;
675                                }
676                            }
677                            return Err(err);
678                        }
679                    };
680                }
681                media.add_media_query(mq);
682                Ok(())
683            })
684        },
685    )?;
686    Ok(media)
687}
688
689fn parse_media_expression<'a, 't: 'a, 'i: 't>(
690    parser: &'a mut Parser<'i, 't>,
691    st: &mut ParseState,
692) -> Result<MediaExpression, ParseError<'i, CustomError>> {
693    let token = parser.next()?.clone();
694    match token {
695        Token::Ident(s) => Ok(match str_to_media_type(&s) {
696            Some(mt) => MediaExpression::MediaType(mt),
697            None => MediaExpression::Unknown,
698        }),
699        Token::ParenthesisBlock => parse_media_expression_inner(parser, st),
700        _ => Err(parser.new_unexpected_token_error(token)),
701    }
702}
703
704fn parse_media_expression_inner<'a, 't: 'a, 'i: 't>(
705    parser: &'a mut Parser<'i, 't>,
706    st: &mut ParseState,
707) -> Result<MediaExpression, ParseError<'i, CustomError>> {
708    parser.parse_nested_block(|parser| {
709        let token = parser.next()?.clone();
710        if let Token::Ident(name) = &token {
711            let expr = if parser.is_exhausted() {
712                match str_to_media_type(name) {
713                    Some(mt) => MediaExpression::MediaType(mt),
714                    None => MediaExpression::Unknown,
715                }
716            } else {
717                parser.expect_colon()?;
718                let name: &str = name;
719                match name {
720                    "orientation" => {
721                        let t = parser.expect_ident()?;
722                        let t: &str = t;
723                        match t {
724                            "portrait" => MediaExpression::Orientation(Orientation::Portrait),
725                            "landscape" => MediaExpression::Orientation(Orientation::Landscape),
726                            _ => MediaExpression::Orientation(Orientation::None),
727                        }
728                    }
729                    "width" => MediaExpression::Width(parse_px_length(parser, st)?),
730                    "min-width" => MediaExpression::MinWidth(parse_px_length(parser, st)?),
731                    "max-width" => MediaExpression::MaxWidth(parse_px_length(parser, st)?),
732                    "height" => MediaExpression::Height(parse_px_length(parser, st)?),
733                    "min-height" => MediaExpression::MinHeight(parse_px_length(parser, st)?),
734                    "max-height" => MediaExpression::MaxHeight(parse_px_length(parser, st)?),
735                    "prefers-color-scheme" => {
736                        let t = parser.expect_ident()?;
737                        let t: &str = t;
738                        match t {
739                            "light" => MediaExpression::Theme(Theme::Light),
740                            "dark" => MediaExpression::Theme(Theme::Dark),
741                            _ => MediaExpression::Unknown,
742                        }
743                    }
744                    _ => MediaExpression::Unknown,
745                }
746            };
747            parse_to_paren_end(parser, true, st);
748            Ok(expr)
749        } else {
750            Err(parser.new_unexpected_token_error(token))
751        }
752    })
753}
754
755fn parse_keyframes_block<'a, 't: 'a, 'i: 't>(
756    parser: &'a mut Parser<'i, 't>,
757    sheet: &mut CompiledStyleSheet,
758    st: &mut ParseState,
759) {
760    parser.skip_whitespace();
761    let start_location = parser.current_source_location();
762    if let Ok(ident) = parse_keyframes_ident(parser) {
763        if parser.expect_curly_bracket_block().is_err() {
764            st.add_warning(
765                WarningKind::IllegalKeyframesBlock,
766                start_location,
767                parser.current_source_location(),
768            );
769            return;
770        }
771        let keyframes = parser.parse_nested_block(|parser| {
772            let mut keyframes = vec![];
773            while !parser.is_exhausted() {
774                keyframes.push(parse_keyframe_rule(parser, st)?);
775            }
776            Ok(keyframes)
777        });
778        match keyframes {
779            Ok(keyframes) => sheet.add_keyframes(keyframes::KeyFrames::new(ident, keyframes)),
780            Err(err) => {
781                st.add_warning(
782                    WarningKind::UnsupportedKeyframesSyntax,
783                    err.location,
784                    err.location,
785                );
786            }
787        }
788    } else {
789        st.add_warning(
790            WarningKind::IllegalKeyframesIdentifier,
791            start_location,
792            parser.current_source_location(),
793        );
794    }
795}
796
797fn parse_keyframe_rule<'a, 't: 'a, 'i: 't>(
798    parser: &'a mut Parser<'i, 't>,
799    st: &mut ParseState,
800) -> Result<keyframes::KeyFrameRule, ParseError<'i, CustomError>> {
801    let keyframe = parse_keyframe(parser)?;
802    // get CloseCurlyBracket position
803    let current_state = parser.state();
804    let _ =
805        parser.parse_until_after::<_, (), CustomError>(Delimiter::CurlyBracketBlock, |parser| {
806            while !parser.is_exhausted() {
807                parser.next()?;
808            }
809            Ok(())
810        });
811    let close_curly_block_position = parser.position();
812    parser.reset(&current_state);
813    parser.expect_curly_bracket_block()?;
814    let mut properties: Vec<PropertyMeta> = vec![];
815    parser.parse_nested_block::<_, _, CustomError>(|parser| {
816        parse_property_list(
817            parser,
818            &mut properties,
819            st,
820            Some(close_curly_block_position),
821        );
822        Ok(())
823    })?;
824    Ok(keyframes::KeyFrameRule::new(keyframe, properties))
825}
826
827fn parse_keyframe<'a, 't: 'a, 'i: 't>(
828    parser: &'a mut Parser<'i, 't>,
829) -> Result<Vec<keyframes::KeyFrame>, ParseError<'i, CustomError>> {
830    parser.parse_until_before(Delimiter::CurlyBracketBlock, |parser| {
831        parser.parse_comma_separated(|parser| {
832            let next = parser.next()?.clone();
833            match next {
834                Token::Percentage { unit_value, .. } => Ok(KeyFrame::Ratio(unit_value)),
835                Token::Ident(ident) => {
836                    let ident: &str = &ident.to_ascii_lowercase();
837                    match ident {
838                        "from" => Ok(KeyFrame::From),
839                        "to" => Ok(KeyFrame::To),
840                        _ => Err(parser.new_custom_error(CustomError::Unsupported)),
841                    }
842                }
843                _ => Err(parser.new_custom_error(CustomError::Unsupported)),
844            }
845        })
846    })
847}
848
849fn parse_keyframes_ident<'a, 't: 'a, 'i: 't>(
850    parser: &'a mut Parser<'i, 't>,
851) -> Result<String, ParseError<'i, CustomError>> {
852    let ident = parser.parse_until_before(Delimiter::CurlyBracketBlock, |parser| {
853        let ret = parser.expect_ident();
854        Ok(ret?.to_string())
855    })?;
856    Ok(ident)
857}
858
859fn parse_font_face_block<'a, 't: 'a, 'i: 't>(
860    parser: &'a mut Parser<'i, 't>,
861    sheet: &mut CompiledStyleSheet,
862    st: &mut ParseState,
863) {
864    if parser.expect_curly_bracket_block().is_ok() {
865        let mut font_face = FontFace::new();
866        let mut properties = vec![];
867        let _ = parser.parse_nested_block(|parser| -> Result<(), ParseError<'_, CustomError>> {
868            loop {
869                parser.skip_whitespace();
870                if parser.is_exhausted() {
871                    break;
872                }
873                let mut start_loc = parser.current_source_location();
874                let start_pos = parser.position();
875                parser
876                    .parse_until_after(Delimiter::Semicolon, |parser| {
877                        let (name, _) = &parse_property_name(parser, start_loc, start_pos, st)?;
878                        let name: &str = name;
879                        start_loc = parser.current_source_location();
880                        match name {
881                            "font-family" => {
882                                let font_family: FontFamilyName = font_family_name(parser)?;
883                                font_face.font_family = font_family;
884                            }
885                            "src" => {
886                                let mut src: Vec<FontSrc> =
887                                    font_face_src(parser, &mut properties, st)?;
888                                src.iter_mut().for_each(|item| {
889                                    if let FontSrc::Url(font_url) = item {
890                                        let url = font_url.url.clone();
891                                        if let Some(base_path) = &st.import_base_path {
892                                            if !is_url(url.as_str()) {
893                                                font_url.url = resolve_relative_path(
894                                                    base_path,
895                                                    url.as_str(),
896                                                    "",
897                                                    "",
898                                                );
899                                            }
900                                        }
901                                    }
902                                });
903                                font_face.src = src;
904                            }
905                            "font-style" => {
906                                let font_style: FontStyleType =
907                                    font_style_repr(parser, &mut properties, st)?;
908                                font_face.font_style = Some(font_style);
909                            }
910                            "font-weight" => {
911                                let font_weight: FontWeightType =
912                                    font_weight_repr(parser, &mut properties, st)?;
913                                font_face.font_weight = Some(font_weight);
914                            }
915                            "font-display" => {
916                                let font_display: FontDisplay = font_display(parser)?;
917                                font_face.font_display = Some(font_display);
918                            }
919                            _ => {
920                                return Err(
921                                    parser.new_custom_error(CustomError::UnsupportedProperty)
922                                );
923                            }
924                        }
925                        Ok(())
926                    })
927                    .unwrap_or_else(|_| {
928                        st.add_warning(
929                            WarningKind::InvalidFontFaceProperty,
930                            start_loc,
931                            parser.current_source_location(),
932                        );
933                    });
934            }
935            Ok(())
936        });
937        // if let Some(ff) = st.font_face.as_mut() {
938        //     ff.push(font_face);
939        // } else {
940        //     st.font_face = Some(vec![font_face]);
941        // }
942        sheet.add_font_face(font_face);
943    }
944}
945fn parse_px_length<'a, 't: 'a, 'i: 't>(
946    parser: &'a mut Parser<'i, 't>,
947    _st: &mut ParseState,
948) -> Result<f32, ParseError<'i, CustomError>> {
949    let next = parser.next()?;
950    match next {
951        Token::Number { value, .. } => {
952            if *value == 0. {
953                return Ok(0.);
954            }
955        }
956        Token::Dimension { value, unit, .. } => {
957            let unit: &str = unit;
958            if unit == "px" {
959                return Ok(*value);
960            }
961        }
962        _ => {}
963    }
964    let next = next.clone();
965    Err(parser.new_unexpected_token_error(next))
966}
967
968fn parse_rule<'a, 't: 'a, 'i: 't>(
969    parser: &'a mut Parser<'i, 't>,
970    st: &mut ParseState,
971) -> Result<Box<Rule>, ParseError<'i, CustomError>> {
972    match parse_selector(parser, st) {
973        Ok(selector) => {
974            // get CloseCurlyBracket position
975            let current_state = parser.state();
976            let _ = parser.parse_until_after::<_, (), CustomError>(
977                Delimiter::CurlyBracketBlock,
978                |parser| {
979                    while !parser.is_exhausted() {
980                        parser.next()?;
981                    }
982                    Ok(())
983                },
984            );
985            let close_curly_block_position = parser.position();
986            parser.reset(&current_state);
987            parser.expect_curly_bracket_block()?;
988            let mut properties: Vec<PropertyMeta> = vec![];
989            parser.parse_nested_block::<_, _, CustomError>(|parser| {
990                parse_property_list(
991                    parser,
992                    &mut properties,
993                    st,
994                    Some(close_curly_block_position),
995                );
996                Ok(())
997            })?;
998            if properties.is_empty() {
999                return Err(parser.new_custom_error(CustomError::SkipErrorBlock));
1000            }
1001            Ok(Rule::new(selector, properties, st.media.clone()))
1002        }
1003        Err(_) => parser.parse_until_after(Delimiter::CurlyBracketBlock, |parser| {
1004            Err(parser.new_custom_error(CustomError::SkipErrorBlock))
1005        }),
1006    }
1007}
1008
1009pub(crate) fn parse_not_function<'a, 't: 'a, 'i: 't>(
1010    parser: &'a mut Parser<'i, 't>,
1011    st: &mut ParseState,
1012    cur_frag: &mut SelectorFragment,
1013    prev_sep: &mut PrevSep,
1014    start_pos: SourcePosition,
1015    start_loc: SourceLocation,
1016) -> Result<(), ParseError<'i, CustomError>> {
1017    let selector = parser.parse_nested_block(|parser| parse_selector(parser, st))?;
1018    let mut frags = selector.fragments;
1019    if let Some(ref mut pseudo_classes) = cur_frag.pseudo_classes {
1020        match pseudo_classes.as_mut() {
1021            PseudoClasses::Not(v) => {
1022                v.append(&mut frags);
1023            }
1024            _ => {
1025                st.add_warning_with_message(
1026                    WarningKind::UnsupportedSelector,
1027                    format!(
1028                        r#"unsupported selector: {:?}"#,
1029                        parser.slice_from(start_pos).trim()
1030                    ),
1031                    start_loc,
1032                    parser.current_source_location(),
1033                );
1034                return Err(parser.new_custom_error(CustomError::Unsupported));
1035            }
1036        }
1037    } else {
1038        cur_frag.set_pseudo_classes(PseudoClasses::Not(frags));
1039    }
1040    *prev_sep = PrevSep::PseudoClassesNot;
1041    Ok(())
1042}
1043
1044#[derive(Copy, Clone, Eq, PartialEq)]
1045pub(crate) enum NthType {
1046    Child,
1047    OfType,
1048}
1049
1050pub(crate) fn parse_nth_function<'a, 't: 'a, 'i: 't>(
1051    parser: &'a mut Parser<'i, 't>,
1052    st: &mut ParseState,
1053    cur_frag: &mut SelectorFragment,
1054    prev_sep: &mut PrevSep,
1055    nth_type: NthType,
1056) -> Result<(), ParseError<'i, CustomError>> {
1057    parser.parse_nested_block(|parser| {
1058        let (a, b) = parse_nth(parser)?;
1059        if nth_type == NthType::OfType {
1060            cur_frag.set_pseudo_classes(PseudoClasses::NthOfType(a, b));
1061            *prev_sep = PrevSep::None;
1062            if parser.is_exhausted() {
1063                return Ok(());
1064            }
1065            return Err(parser.new_custom_error(CustomError::Unsupported));
1066        }
1067        if parser
1068            .try_parse(|parser| parser.expect_ident_matching("of"))
1069            .is_err()
1070        {
1071            cur_frag.set_pseudo_classes(PseudoClasses::NthChild(a, b, None));
1072            *prev_sep = PrevSep::None;
1073            if parser.is_exhausted() {
1074                return Ok(());
1075            }
1076            return Err(parser.new_custom_error(CustomError::Unsupported));
1077        }
1078        let selectors = parse_selector(parser, st)?;
1079        cur_frag.set_pseudo_classes(PseudoClasses::NthChild(
1080            a,
1081            b,
1082            Some(Box::new(selectors.fragments)),
1083        ));
1084        *prev_sep = PrevSep::None;
1085        Ok(())
1086    })
1087}
1088
1089#[derive(Debug, Copy, Clone)]
1090pub(crate) enum PrevSep {
1091    Init,
1092    None,
1093    Space,
1094    Child,
1095    Universal,
1096    NextSibling,
1097    SubsequentSibling,
1098    End,
1099    PseudoClassesNot,
1100}
1101
1102pub(crate) fn parse_selector<'a, 't: 'a, 'i: 't>(
1103    parser: &'a mut Parser<'i, 't>,
1104    st: &mut ParseState,
1105) -> Result<Selector, ParseError<'i, CustomError>> {
1106    let fragments = parser.parse_until_before(Delimiter::CurlyBracketBlock, |parser| {
1107        // let most_start_loc = parser.current_source_location();
1108        parser.parse_comma_separated(|parser| {
1109            parser.skip_whitespace();
1110            let item_start_loc = parser.current_source_location();
1111            let item_start_pos = parser.position();
1112            if parser.is_exhausted() {
1113                st.add_warning_with_message(
1114                    WarningKind::InvalidSelector,
1115                    format!(r#"selector not terminated: {}"#, parser.slice_from(item_start_pos).trim()),
1116                    item_start_loc,
1117                    parser.current_source_location(),
1118                );
1119                return Err(parser.new_custom_error(CustomError::Unsupported));
1120            }
1121            let mut cur_frag = SelectorFragment::new();
1122            let mut prev_sep = PrevSep::Init;
1123            macro_rules! clear_prev_sep {
1124                () => {
1125                    match prev_sep {
1126                        PrevSep::Space => {
1127                            cur_frag = SelectorFragment::with_relation(SelectorRelationType::Ancestor(
1128                                cur_frag,
1129                            ));
1130                        }
1131                        PrevSep::Child => {
1132                            cur_frag = SelectorFragment::with_relation(
1133                                SelectorRelationType::DirectParent(cur_frag),
1134                            );
1135                        }
1136                        PrevSep::NextSibling => {
1137                            cur_frag = SelectorFragment::with_relation(
1138                                SelectorRelationType::NextSibling(cur_frag)
1139                            )
1140                        }
1141                        PrevSep::SubsequentSibling => {
1142                            cur_frag = SelectorFragment::with_relation(
1143                                SelectorRelationType::SubsequentSibling(cur_frag)
1144                            )
1145                        }
1146                        _ => {}
1147                    }
1148                    prev_sep = PrevSep::None;
1149                };
1150            }
1151            while !parser.is_exhausted() {
1152                let start_loc = parser.current_source_location();
1153                let start_pos = parser.position();
1154                let next = match prev_sep {
1155                    PrevSep::None | PrevSep::PseudoClassesNot => parser.next_including_whitespace(),
1156                    PrevSep::End => {
1157                        st.add_warning_with_message(
1158                            WarningKind::UnsupportedSelector,
1159                            format!(r#"unsupported selector: {:?}"#, parser.slice_from(item_start_pos).trim()),
1160                            item_start_loc,
1161                            parser.current_source_location(),
1162                        );
1163                        Err(parser.new_basic_error(BasicParseErrorKind::EndOfInput))
1164                    },
1165                    _ => parser.next(),
1166                }?
1167                .clone();
1168                match next {
1169                    Token::Ident(ref s) => {
1170                        clear_prev_sep!();
1171                        cur_frag.set_tag_name(s);
1172                    }
1173                    Token::IDHash(ref s) => {
1174                        clear_prev_sep!();
1175                        cur_frag.set_id(s);
1176                    }
1177                    Token::Hash(_c) => {
1178                        st.add_warning_with_message(
1179                            WarningKind::InvalidSelector,
1180                            format!(r#"illegal ID selector: {}"#, parser.slice_from(start_pos).trim()),
1181                            start_loc,
1182                            parser.current_source_location(),
1183                        );
1184                        return Err(parser.new_custom_error(CustomError::Unsupported));
1185                    }
1186                    Token::Delim(c) => match c {
1187                        '.' => {
1188                            let class = parser.expect_ident().cloned().map_err(|_| {
1189                                st.add_warning_with_message(
1190                                    WarningKind::InvalidSelector,
1191                                    format!(r#"illegal classes name: {}"#, parser.slice_from(start_pos).trim()),
1192                                    start_loc,
1193                                    parser.current_source_location(),
1194                                );
1195                                parser.new_custom_error(CustomError::Unsupported)
1196                            })?;
1197                            clear_prev_sep!();
1198                            cur_frag.add_class(&class);
1199                        }
1200                        '>' => match prev_sep {
1201                            PrevSep::Init => {
1202                                st.add_warning_with_message(
1203                                    WarningKind::InvalidSelector,
1204                                    format!(r#"combinator (>) needs to appear after other selectors: {}"#, parser.slice_from(start_pos).trim()),
1205                                    start_loc,
1206                                    parser.current_source_location(),
1207                                );
1208                                return Err(parser.new_custom_error(CustomError::Unsupported));
1209                            }
1210                            _ => prev_sep = PrevSep::Child,
1211                        },
1212                        '+' => match prev_sep {
1213                            PrevSep::Init => {
1214                                st.add_warning_with_message(
1215                                    WarningKind::InvalidSelector,
1216                                    format!(r#"combinator (+) needs to appear after selector: {}"#, parser.slice_from(start_pos).trim()),
1217                                    start_loc,
1218                                    parser.current_source_location(),
1219                                );
1220                                return Err(parser.new_custom_error(CustomError::Unsupported));
1221                            }
1222                            _ => prev_sep = PrevSep::NextSibling,
1223                        }
1224                        '~' => match prev_sep {
1225                            PrevSep::Init => {
1226                                st.add_warning_with_message(
1227                                    WarningKind::InvalidSelector,
1228                                    format!(r#"combinator (~) needs to appear after selector: {}"#, parser.slice_from(start_pos).trim()),
1229                                    start_loc,
1230                                    parser.current_source_location(),
1231                                );
1232                                return Err(parser.new_custom_error(CustomError::Unsupported));
1233                            }
1234                            _ => prev_sep = PrevSep::SubsequentSibling
1235                        }
1236                        '*' => match prev_sep {
1237                            PrevSep::Space => {
1238                                cur_frag = SelectorFragment::with_relation(
1239                                    SelectorRelationType::Ancestor(cur_frag),
1240                                );
1241                                prev_sep = PrevSep::None;
1242                            }
1243                            PrevSep::Child => {
1244                                cur_frag = SelectorFragment::with_relation(
1245                                    SelectorRelationType::DirectParent(cur_frag),
1246                                );
1247                                prev_sep = PrevSep::None;
1248                            }
1249                            PrevSep::None => {
1250                                st.add_warning_with_message(
1251                                    WarningKind::InvalidSelector,
1252                                    format!(r#"universal selector (*) must be the first selector in the compound selector: {}"#, parser.slice_from(start_pos).trim()),
1253                                    start_loc,
1254                                    parser.current_source_location(),
1255                                );
1256                                return Err(parser.new_custom_error(CustomError::Unsupported));
1257                            }
1258                            _ => {
1259                                prev_sep = PrevSep::Universal;
1260                            }
1261                        },
1262                        _ => {
1263                            st.add_warning_with_message(
1264                                WarningKind::UnsupportedSelector,
1265                                format!(r#"unsupported selector: {}"#, parser.slice_from(start_pos).trim()),
1266                                start_loc,
1267                                parser.current_source_location(),
1268                            );
1269                            return Err(parser.new_custom_error(CustomError::Unsupported));
1270                        }
1271                    },
1272                    Token::Colon => match prev_sep {
1273                        PrevSep::Init => {
1274                            let next = parser.next_including_whitespace()?.clone();
1275                            match next {
1276                                Token::Colon => {
1277                                    let next = parser.next_including_whitespace()?.clone();
1278                                    match next {
1279                                        Token::Ident(pseudo_elements) => {
1280                                            let s = pseudo_elements.to_lowercase();
1281                                            match s.as_str() {
1282                                                "before" => {
1283                                                    cur_frag.set_pseudo_elements(PseudoElements::Before);
1284                                                    prev_sep = PrevSep::End
1285                                                }
1286                                                "after" => {
1287                                                    cur_frag.set_pseudo_elements(PseudoElements::After);
1288                                                    prev_sep = PrevSep::End
1289                                                }
1290                                                "selection" => {
1291                                                    cur_frag.set_pseudo_elements(PseudoElements::Selection);
1292                                                    prev_sep = PrevSep::End
1293                                                }
1294                                                _ => {
1295                                                    st.add_warning_with_message(
1296                                                        WarningKind::UnsupportedPseudoElement,
1297                                                        format!("unsupported pseudo elements: {}", parser.slice_from(item_start_pos).trim()),
1298                                                        item_start_loc,
1299                                                        parser.current_source_location(),
1300                                                    );
1301                                                    return Err(
1302                                                        parser.new_custom_error(CustomError::Unsupported)
1303                                                    );
1304                                                }
1305                                            }
1306                                        }
1307                                        _ => {
1308                                            st.add_warning_with_message(
1309                                                WarningKind::UnsupportedSelector,
1310                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1311                                                item_start_loc,
1312                                                parser.current_source_location(),
1313                                            );
1314                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1315                                        }
1316                                    }
1317                                }
1318                                Token::Ident(pseudo_classes) => {
1319                                    let s = pseudo_classes.to_lowercase();
1320                                    match s.as_str() {
1321                                        "first-child" => {
1322                                            cur_frag.set_pseudo_classes(PseudoClasses::FirstChild);
1323                                            prev_sep = PrevSep::None
1324                                        }
1325                                        "last-child" => {
1326                                            cur_frag.set_pseudo_classes(PseudoClasses::LastChild);
1327                                            prev_sep = PrevSep::None
1328                                        }
1329                                        "only-child" => {
1330                                            cur_frag.set_pseudo_classes(PseudoClasses::OnlyChild);
1331                                            prev_sep = PrevSep::None
1332                                        }
1333                                        "empty" => {
1334                                            cur_frag.set_pseudo_classes(PseudoClasses::Empty);
1335                                            prev_sep = PrevSep::None
1336                                        }
1337                                        "host" => {
1338                                            cur_frag.set_pseudo_classes(PseudoClasses::Host);
1339                                            prev_sep = PrevSep::End
1340                                        }
1341                                        // 
1342                                        "before" => {
1343                                            cur_frag.set_pseudo_elements(PseudoElements::Before);
1344                                            prev_sep = PrevSep::End;
1345                                            st.add_warning_with_message(
1346                                                WarningKind::InvalidPseudoElement,
1347                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1348                                                item_start_loc,
1349                                                parser.current_source_location(),
1350                                            );
1351                                        }
1352                                        "after" => {
1353                                            cur_frag.set_pseudo_elements(PseudoElements::After);
1354                                            prev_sep = PrevSep::End;
1355                                            st.add_warning_with_message(
1356                                                WarningKind::InvalidPseudoElement,
1357                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1358                                                item_start_loc,
1359                                                parser.current_source_location(),
1360                                            );
1361                                        }
1362                                        "selection" => {
1363                                            cur_frag.set_pseudo_elements(PseudoElements::Selection);
1364                                            prev_sep = PrevSep::End;
1365                                            st.add_warning_with_message(
1366                                                WarningKind::InvalidPseudoElement,
1367                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1368                                                item_start_loc,
1369                                                parser.current_source_location(),
1370                                            );
1371                                        }
1372                                        _ => {
1373                                            st.add_warning_with_message(
1374                                                WarningKind::UnsupportedPseudoClass,
1375                                                format!("unsupported pseudo class: {:?}", parser.slice_from(item_start_pos).trim()),
1376                                                item_start_loc,
1377                                                parser.current_source_location(),
1378                                            );
1379                                            return Err(
1380                                                parser.new_custom_error(CustomError::Unsupported)
1381                                            );
1382                                        }
1383                                    }
1384                                }
1385                                Token::Function(ref name) => {
1386                                    let name: &str = name;
1387                                    match name {
1388                                        "not" => {
1389                                            parse_not_function(parser, st, &mut cur_frag, &mut prev_sep, item_start_pos, item_start_loc)?;
1390                                        },
1391                                        "nth-child" => {
1392                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::Child)?;
1393                                        },
1394                                        "nth-of-type" => {
1395                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::OfType)?;
1396                                        }
1397                                        _ => {
1398                                            st.add_warning_with_message(
1399                                                WarningKind::UnsupportedSelector,
1400                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1401                                                item_start_loc,
1402                                                parser.current_source_location(),
1403                                            );
1404                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1405                                        }
1406                                    }
1407                                }
1408                                _ => {
1409                                    st.add_warning_with_message(
1410                                        WarningKind::UnsupportedSelector,
1411                                        format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1412                                        item_start_loc,
1413                                        parser.current_source_location(),
1414                                    );
1415                                    return Err(parser.new_custom_error(CustomError::Unsupported));
1416                                }
1417                            }
1418                        }
1419                        PrevSep::None => {
1420                            let next = parser.next_including_whitespace()?.clone();
1421                            match next {
1422                                Token::Colon => {
1423                                    let next = parser.next_including_whitespace()?.clone();
1424                                    match next {
1425                                        Token::Ident(pseudo_elements) => {
1426                                            let s = pseudo_elements.to_lowercase();
1427                                            match s.as_str() {
1428                                                "before" => {
1429                                                    cur_frag.set_pseudo_elements(PseudoElements::Before);
1430                                                    prev_sep = PrevSep::End
1431                                                }
1432                                                "after" => {
1433                                                    cur_frag.set_pseudo_elements(PseudoElements::After);
1434                                                    prev_sep = PrevSep::End
1435                                                }
1436                                                "selection" => {
1437                                                    cur_frag.set_pseudo_elements(PseudoElements::Selection);
1438                                                    prev_sep = PrevSep::End
1439                                                }
1440                                                _ => {
1441                                                    st.add_warning_with_message(
1442                                                        WarningKind::UnsupportedPseudoElement,
1443                                                        format!("unsupported pseudo element: {}", parser.slice_from(item_start_pos).trim()),
1444                                                        item_start_loc,
1445                                                        parser.current_source_location(),
1446                                                    );
1447                                                    return Err(
1448                                                        parser.new_custom_error(CustomError::Unsupported)
1449                                                    );
1450                                                }
1451                                            }
1452                                        }
1453                                        _ => {
1454                                            st.add_warning_with_message(
1455                                                WarningKind::UnsupportedSelector,
1456                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1457                                                item_start_loc,
1458                                                parser.current_source_location(),
1459                                            );
1460                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1461                                        }
1462                                    }
1463                                }
1464                                Token::Ident(pseudo_classes) => {
1465                                    let s = pseudo_classes.to_lowercase();
1466                                    match s.as_str() {
1467                                        "first-child" => {
1468                                            cur_frag.set_pseudo_classes(PseudoClasses::FirstChild);
1469                                            prev_sep = PrevSep::None
1470                                        }
1471                                        "last-child" => {
1472                                            cur_frag.set_pseudo_classes(PseudoClasses::LastChild);
1473                                            prev_sep = PrevSep::None
1474                                        }
1475                                        "only-child" => {
1476                                            cur_frag.set_pseudo_classes(PseudoClasses::OnlyChild);
1477                                            prev_sep = PrevSep::None
1478                                        }
1479                                        "empty" => {
1480                                            cur_frag.set_pseudo_classes(PseudoClasses::Empty);
1481                                            prev_sep = PrevSep::None
1482                                        }
1483                                        // 
1484                                        "before" => {
1485                                            cur_frag.set_pseudo_elements(PseudoElements::Before);
1486                                            prev_sep = PrevSep::End;
1487                                            st.add_warning_with_message(
1488                                                WarningKind::InvalidPseudoElement,
1489                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1490                                                item_start_loc,
1491                                                parser.current_source_location(),
1492                                            );
1493                                        }
1494                                        "after" => {
1495                                            cur_frag.set_pseudo_elements(PseudoElements::After);
1496                                            prev_sep = PrevSep::End;
1497                                            st.add_warning_with_message(
1498                                                WarningKind::InvalidPseudoElement,
1499                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1500                                                item_start_loc,
1501                                                parser.current_source_location(),
1502                                            );
1503                                        }
1504                                        _ => {
1505                                            st.add_warning_with_message(
1506                                                WarningKind::UnsupportedPseudoClass,
1507                                                format!("unsupported pseudo class: {}", parser.slice_from(item_start_pos).trim()),
1508                                                item_start_loc,
1509                                                parser.current_source_location(),
1510                                            );
1511                                            return Err(
1512                                                parser.new_custom_error(CustomError::Unsupported)
1513                                            );
1514                                        }
1515                                    }
1516                                }
1517                                Token::Function(ref name) => {
1518                                    let name: &str = name;
1519                                    match name {
1520                                        "not" => {
1521                                            parse_not_function(parser, st, &mut cur_frag, &mut prev_sep, item_start_pos, item_start_loc)?;
1522                                        },
1523                                        "nth-child" => {
1524                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::Child)?;
1525                                        },
1526                                        "nth-of-type" => {
1527                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::OfType)?;
1528                                        }
1529                                        _ => {
1530                                            st.add_warning_with_message(
1531                                                WarningKind::UnsupportedSelector,
1532                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1533                                                item_start_loc,
1534                                                parser.current_source_location(),
1535                                            );
1536                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1537                                        }
1538                                    }
1539                                }
1540                                _ => {
1541                                    st.add_warning_with_message(
1542                                        WarningKind::UnsupportedSelector,
1543                                        format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1544                                        item_start_loc,
1545                                        parser.current_source_location(),
1546                                    );
1547                                    return Err(parser.new_custom_error(CustomError::Unsupported));
1548                                }
1549                            }
1550                        }
1551                        PrevSep::PseudoClassesNot => {
1552                            let next = parser.next_including_whitespace()?.clone();
1553                            match next {
1554                                Token::Function(ref name) => {
1555                                    let name: &str = name;
1556                                    match name {
1557                                        "not" => {
1558                                           parse_not_function(parser, st, &mut cur_frag, &mut prev_sep, item_start_pos, item_start_loc)?;
1559                                        },
1560                                        _ => {
1561                                            st.add_warning_with_message(
1562                                                WarningKind::UnsupportedSelector,
1563                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1564                                                item_start_loc,
1565                                                parser.current_source_location(),
1566                                            );
1567                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1568                                        }
1569                                    }
1570                                }
1571                                _ => {
1572                                    st.add_warning_with_message(
1573                                        WarningKind::UnsupportedSelector,
1574                                        format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1575                                        item_start_loc,
1576                                        parser.current_source_location(),
1577                                    );
1578                                    return Err(parser.new_custom_error(CustomError::Unsupported));
1579                                }
1580                            }
1581                        }
1582                        _ => {
1583                            st.add_warning_with_message(
1584                                WarningKind::UnsupportedSelector,
1585                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1586                                item_start_loc,
1587                                parser.current_source_location(),
1588                            );
1589                            return Err(parser.new_custom_error(CustomError::Unsupported));
1590                        }
1591                    },
1592                    Token::WhiteSpace(_) => {
1593                        prev_sep = PrevSep::Space;
1594                    }
1595                    Token::CDC => {}
1596                    Token::CDO => {}
1597                    Token::Comment(_) => {
1598                        prev_sep = PrevSep::Space;
1599                    }
1600                    Token::SquareBracketBlock => {
1601                        clear_prev_sep!();
1602                        let attr = parser.parse_nested_block(|parser| {
1603                            parse_attribute_selector(parser)
1604                        })?;
1605                        cur_frag.add_attribute(attr);
1606                    }
1607                    _ => {
1608                        st.add_warning_with_message(
1609                            WarningKind::UnsupportedSelector,
1610                            format!(r#"unsupported selector: {}"#, parser.slice_from(start_pos).trim()),
1611                            start_loc,
1612                            parser.current_source_location(),
1613                        );
1614                        return Err(parser.new_custom_error(CustomError::Unsupported));
1615                    }
1616                };
1617            }
1618            // if let PrevSep::Init = prev_sep {
1619            //     st.add_warning(
1620            //         format!(r#"Selector should be set"#),
1621            //         item_start_loc,
1622            //         parser.current_source_location(),
1623            //     );
1624            //     return Err(parser.new_custom_error(CustomError::Unsupported));
1625            // };
1626            if let PrevSep::Child = prev_sep {
1627                st.add_warning_with_message(
1628                    WarningKind::InvalidSelector,
1629                    format!(r#"selector not terminated: {}"#, parser.slice_from(item_start_pos).trim()),
1630                    item_start_loc,
1631                    parser.current_source_location(),
1632                );
1633                return Err(parser.new_custom_error(CustomError::Unsupported));
1634            };
1635            Ok(cur_frag)
1636        })
1637    })?;
1638    Ok(Selector::from_fragments(fragments))
1639}
1640
1641#[inline(always)]
1642fn parse_attribute_selector<'a, 't: 'a, 'i: 't>(
1643    parser: &'a mut Parser<'i, 't>,
1644) -> Result<Attribute, ParseError<'i, CustomError>> {
1645    parser.skip_whitespace();
1646
1647    // parse attribute name
1648    let name = parser.expect_ident()?.to_string();
1649
1650    // try parse operator
1651    let location: SourceLocation = parser.current_source_location();
1652    let operator = match parser.next() {
1653        // [name]
1654        Err(_) => return Ok(Attribute::new_set(name.to_string())),
1655        // [name=value]
1656        Ok(&Token::Delim('=')) => AttributeOperator::Exact,
1657        // [name~=value]
1658        Ok(&Token::IncludeMatch) => AttributeOperator::List,
1659        // [name|=value]
1660        Ok(&Token::DashMatch) => AttributeOperator::Hyphen,
1661        // [name^=value]
1662        Ok(&Token::PrefixMatch) => AttributeOperator::Begin,
1663        // [name$=value]
1664        Ok(&Token::SuffixMatch) => AttributeOperator::End,
1665        // [name*=value]
1666        Ok(&Token::SubstringMatch) => AttributeOperator::Contain,
1667        Ok(_) => {
1668            return Err(location.new_custom_error(CustomError::UnexpectedTokenInAttributeSelector))
1669        }
1670    };
1671
1672    let value = match parser.expect_ident_or_string() {
1673        Ok(t) => t.clone(),
1674        Err(BasicParseError {
1675            kind: BasicParseErrorKind::UnexpectedToken(_),
1676            location,
1677        }) => return Err(location.new_custom_error(CustomError::BadValueInAttr)),
1678        Err(e) => return Err(e.into()),
1679    }
1680    .to_string();
1681    let never_matches = match operator {
1682        AttributeOperator::Exact | AttributeOperator::Hyphen => false,
1683        AttributeOperator::Begin | AttributeOperator::End | AttributeOperator::List => {
1684            value.is_empty()
1685        }
1686        AttributeOperator::Contain => value.is_empty() || value.contains(SELECTOR_WHITESPACE),
1687        AttributeOperator::Set => unreachable!(),
1688    };
1689    let attribute_flags = parse_attribute_flags(parser)?;
1690    Ok(Attribute {
1691        operator,
1692        case_insensitive: attribute_flags,
1693        never_matches,
1694        name,
1695        value: Some(value),
1696    })
1697}
1698
1699#[inline(always)]
1700fn parse_attribute_flags<'a, 't: 'a, 'i: 't>(
1701    parser: &'a mut Parser<'i, 't>,
1702) -> Result<AttributeFlags, BasicParseError<'i>> {
1703    let location = parser.current_source_location();
1704    match parser.next() {
1705        Ok(t) => {
1706            if let Token::Ident(ref i) = t {
1707                Ok(match_ignore_ascii_case! {
1708                    i,
1709                    "i" => AttributeFlags::CaseInsensitive,
1710                    "s" => AttributeFlags::CaseSensitive,
1711                    _ => return Err(location.new_basic_unexpected_token_error(t.clone())),
1712                })
1713            } else {
1714                Err(location.new_basic_unexpected_token_error(t.clone()))
1715            }
1716        }
1717        Err(_) => Ok(AttributeFlags::CaseSensitivityDependsOnName),
1718    }
1719}
1720
1721#[inline(always)]
1722fn parse_property_list<'a, 't: 'a, 'i: 't>(
1723    parser: &'a mut Parser<'i, 't>,
1724    properties: &'a mut Vec<PropertyMeta>,
1725    st: &mut ParseState,
1726    close_curly_block_position: Option<SourcePosition>,
1727) {
1728    loop {
1729        if st.debug_mode != StyleParsingDebugMode::None
1730            && parser
1731                .try_parse(|parser| loop {
1732                    let token = parser.next_including_whitespace_and_comments()?;
1733                    match token {
1734                        Token::Comment(s) => {
1735                            let mut commented_props =
1736                                parse_inline_style(s, StyleParsingDebugMode::DebugAndDisabled).0;
1737                            properties.append(&mut commented_props);
1738                            break Ok(());
1739                        }
1740                        Token::WhiteSpace(_) => {
1741                            continue;
1742                        }
1743                        _ => {
1744                            let token = token.clone();
1745                            break Err(parser.new_basic_unexpected_token_error(token));
1746                        }
1747                    }
1748                })
1749                .is_ok()
1750        {
1751            continue;
1752        }
1753        while parser.try_parse(|parser| parser.expect_semicolon()).is_ok() {}
1754        parser.skip_whitespace();
1755        if parser.is_exhausted() {
1756            break;
1757        }
1758        let prev_properties_len = properties.len();
1759        let start_loc = parser.current_source_location();
1760        let start_pos = parser.position();
1761
1762        let mut rule_end_position = None;
1763        let current_state = parser.state();
1764        while !parser.is_exhausted() {
1765            if let Ok(&Token::Semicolon) = parser.next() {
1766                rule_end_position = Some(parser.position());
1767                break;
1768            }
1769        }
1770        if rule_end_position.is_none() {
1771            rule_end_position = close_curly_block_position;
1772        }
1773        parser.reset(&current_state);
1774        parser
1775            .parse_until_after(Delimiter::Semicolon, |parser| {
1776                let mut ret = if st.debug_mode != StyleParsingDebugMode::None {
1777                    parse_property_item_debug(
1778                        parser,
1779                        properties,
1780                        st,
1781                        st.debug_mode == StyleParsingDebugMode::DebugAndDisabled,
1782                        rule_end_position,
1783                    )
1784                } else {
1785                    parse_property_item(parser, properties, st, rule_end_position)
1786                };
1787                if ret.is_err() {
1788                    while !parser.is_exhausted() {
1789                        let _ = parser.next();
1790                    }
1791                    return ret;
1792                }
1793                if !parser.is_exhausted() {
1794                    ret = Err(parser.new_custom_error(CustomError::UnsupportedProperty));
1795                }
1796                ret
1797            })
1798            .unwrap_or_else(|err| {
1799                // restore properties state
1800                properties.drain(prev_properties_len..);
1801                let end_pos = parser.position();
1802                let end_loc = parser.current_source_location();
1803                let mut kind = WarningKind::UnsupportedProperty;
1804                let mut warning_tmpl = "unsupported property".to_string();
1805                if let ParseErrorKind::Custom(CustomError::Reason(s)) = err.kind {
1806                    kind = WarningKind::InvalidProperty;
1807                    warning_tmpl = s;
1808                }
1809                st.add_warning_with_message(
1810                    kind,
1811                    format!(
1812                        "{}: {}",
1813                        warning_tmpl,
1814                        parser.slice(start_pos..end_pos).trim()
1815                    ),
1816                    start_loc,
1817                    end_loc,
1818                );
1819            });
1820    }
1821}
1822
1823#[inline(always)]
1824fn parse_property_item<'a, 't: 'a, 'i: 't>(
1825    parser: &'a mut Parser<'i, 't>,
1826    properties: &'a mut Vec<PropertyMeta>,
1827    st: &mut ParseState,
1828    rule_end_position: Option<SourcePosition>,
1829) -> Result<(), ParseError<'i, CustomError>> {
1830    parser.skip_whitespace();
1831    let prop_name_start_loc = parser.current_source_location();
1832    let prop_name_start_pos = parser.position();
1833    let (name, is_custom_property) =
1834        parse_property_name(parser, prop_name_start_loc, prop_name_start_pos, st)?;
1835    if is_custom_property {
1836        parse_custom_property_value_with_important(parser, &name, properties, rule_end_position)?;
1837    } else {
1838        parse_property_value_with_important(
1839            parser,
1840            &name,
1841            properties,
1842            prop_name_start_loc,
1843            st,
1844            rule_end_position,
1845        )?;
1846    }
1847    Ok(())
1848}
1849
1850#[inline(always)]
1851fn parse_property_item_debug<'a, 't: 'a, 'i: 't>(
1852    parser: &'a mut Parser<'i, 't>,
1853    properties: &'a mut Vec<PropertyMeta>,
1854    st: &mut ParseState,
1855    disabled: bool,
1856    rule_end_position: Option<SourcePosition>,
1857) -> Result<(), ParseError<'i, CustomError>> {
1858    parser.skip_whitespace();
1859    let prev_properties_len = properties.len();
1860    let prop_name_start_index = parser.position();
1861    let prop_name_start_loc = parser.current_source_location();
1862    let (name, is_custom_property) =
1863        parse_property_name(parser, prop_name_start_loc, prop_name_start_index, st)?;
1864    let prop_value_start_index = parser.position();
1865    if is_custom_property {
1866        parse_custom_property_value_with_important(parser, &name, properties, rule_end_position)?;
1867    } else {
1868        parse_property_value_with_important(
1869            parser,
1870            &name,
1871            properties,
1872            prop_name_start_loc,
1873            st,
1874            rule_end_position,
1875        )?;
1876    }
1877    let mut is_important = false;
1878    let grouped_properties = properties
1879        .drain(prev_properties_len..)
1880        .map(|p| match p {
1881            PropertyMeta::Normal { property } => property,
1882            PropertyMeta::Important { property } => {
1883                is_important = true;
1884                property
1885            }
1886            PropertyMeta::DebugGroup { .. } => unreachable!(),
1887        })
1888        .collect::<Box<_>>();
1889    let name_with_colon = parser.slice(prop_name_start_index..prop_value_start_index);
1890    let name = &name_with_colon[0..(name_with_colon.len() - 1)];
1891    let value = parser.slice_from(prop_value_start_index);
1892    properties.push(PropertyMeta::DebugGroup {
1893        original_name_value: Box::new((name.into(), value.into())),
1894        properties: grouped_properties,
1895        important: is_important,
1896        disabled,
1897    });
1898    Ok(())
1899}
1900
1901#[inline(always)]
1902fn parse_property_name<'a, 't: 'a, 'i: 't>(
1903    parser: &'a mut Parser<'i, 't>,
1904    prop_name_start_loc: SourceLocation,
1905    prop_name_start_pos: SourcePosition,
1906    st: &mut ParseState,
1907) -> Result<(CowRcStr<'i>, bool), ParseError<'i, CustomError>> {
1908    let t = parser.expect_ident().cloned();
1909    let name = t.inspect_err(|_| {
1910        st.add_warning_with_message(
1911            WarningKind::InvalidProperty,
1912            format!(
1913                r#"invalid property: {}"#,
1914                parser.slice_from(prop_name_start_pos).trim()
1915            ),
1916            prop_name_start_loc,
1917            parser.current_source_location(),
1918        );
1919    })?;
1920    parser.expect_colon().inspect_err(|_| {
1921        st.add_warning_with_message(
1922            WarningKind::MissingColonAfterProperty,
1923            format!(
1924                r#"expect colon after property: {}"#,
1925                parser.slice_from(prop_name_start_pos).trim()
1926            ),
1927            prop_name_start_loc,
1928            parser.current_source_location(),
1929        );
1930    })?;
1931    let is_custom_property = name.starts_with("--");
1932    Ok((name, is_custom_property))
1933}
1934
1935#[inline(always)]
1936fn parse_property_value_with_important<'a, 't: 'a, 'i: 't>(
1937    parser: &'a mut Parser<'i, 't>,
1938    name: &str,
1939    properties: &'a mut Vec<PropertyMeta>,
1940    prop_name_start_loc: SourceLocation,
1941    st: &mut ParseState,
1942    rule_end_position: Option<SourcePosition>,
1943) -> Result<(), ParseError<'i, CustomError>> {
1944    let prev_properties_len = properties.len();
1945    let skip_parse_important =
1946        parse_property_value(parser, name, properties, st, rule_end_position)?;
1947    if !skip_parse_important {
1948        let is_important = parser.try_parse(parse_important).is_ok();
1949        if is_important {
1950            for pm in &mut properties[prev_properties_len..] {
1951                let mut pm2: PropertyMeta = PropertyMeta::Normal {
1952                    property: Property::Unknown,
1953                };
1954                core::mem::swap(&mut pm2, pm);
1955                *pm = match pm2 {
1956                    PropertyMeta::Normal { property } => PropertyMeta::Important { property },
1957                    PropertyMeta::Important { .. } => unreachable!(),
1958                    PropertyMeta::DebugGroup { .. } => unreachable!(),
1959                };
1960            }
1961        };
1962    }
1963    for mut pm in &mut properties[prev_properties_len..] {
1964        let ParseState {
1965            ref mut warnings,
1966            ref mut hooks,
1967            ..
1968        } = st;
1969        if let Some(hooks) = hooks.as_mut() {
1970            if let Some(p) = match &mut pm {
1971                PropertyMeta::Normal { property } => Some(property),
1972                PropertyMeta::Important { property } => Some(property),
1973                PropertyMeta::DebugGroup { .. } => None,
1974            } {
1975                let ctx = &mut hooks::ParserHooksContext {
1976                    warnings,
1977                    start_loc: prop_name_start_loc,
1978                    end_loc: parser.current_source_location(),
1979                };
1980                hooks.parsed_property(ctx, p);
1981            }
1982        }
1983    }
1984    Ok(())
1985}
1986
1987#[inline(always)]
1988fn parse_custom_property_value_with_important<'a, 't: 'a, 'i: 't>(
1989    parser: &'a mut Parser<'i, 't>,
1990    name: &str,
1991    properties: &'a mut Vec<PropertyMeta>,
1992    rule_end_position: Option<SourcePosition>,
1993) -> Result<(), ParseError<'i, CustomError>> {
1994    if name.len() <= 2 {
1995        return Err(parser.new_custom_error(CustomError::Unmatched));
1996    }
1997    let value_start_pos = parser.position();
1998    parser
1999        .parse_until_before::<_, _, CustomError>(Delimiter::Semicolon, |parser| {
2000            while !parser.is_exhausted() {
2001                parser.next()?;
2002            }
2003            let mut value: &str = parser
2004                .slice(value_start_pos..rule_end_position.unwrap_or_else(|| parser.position()));
2005            value = value.trim_end_matches(['\n', '}', ';']);
2006            if value.trim_end().ends_with("!important") {
2007                value = value.trim_end().trim_end_matches("!important");
2008                if value.trim_end().ends_with("!important") {
2009                    return Err(parser.new_custom_error(CustomError::Unmatched));
2010                }
2011                properties.push(PropertyMeta::Important {
2012                    property: Property::CustomProperty(CustomPropertyType::Expr(
2013                        name.trim().into(),
2014                        value.into(),
2015                    )),
2016                })
2017            } else {
2018                properties.push(PropertyMeta::Normal {
2019                    property: Property::CustomProperty(CustomPropertyType::Expr(
2020                        name.trim().into(),
2021                        value.into(),
2022                    )),
2023                });
2024            }
2025            // TODO impl debug
2026            Ok(())
2027        })
2028        .map_err(|_| parser.new_custom_error(CustomError::Unsupported))
2029}
2030
2031#[cfg(test)]
2032mod test {
2033    use crate::{property::Property, typing::DisplayType};
2034
2035    use super::{is_url, parse_color_to_rgba, resolve_relative_path};
2036
2037    #[test]
2038    fn parse_color_test() {
2039        let source = "#FFFFFF";
2040        let ret = parse_color_to_rgba(source);
2041        assert_eq!(ret.0, 255);
2042        assert_eq!(ret.1, 255);
2043        assert_eq!(ret.2, 255);
2044        assert_eq!(ret.3, 255);
2045
2046        let source = "red";
2047        let ret = parse_color_to_rgba(source);
2048        assert_eq!(ret.0, 255);
2049        assert_eq!(ret.1, 0);
2050        assert_eq!(ret.2, 0);
2051        assert_eq!(ret.3, 255);
2052    }
2053
2054    #[test]
2055    fn resolve_relative_path_test() {
2056        assert_eq!(
2057            resolve_relative_path("/src/components/a.wxss", "./hello.wxss", ".wxss", ".css"),
2058            "src/components/hello.css"
2059        );
2060
2061        assert_eq!(
2062            resolve_relative_path("src/components/a.wxss", "./hello.wxss", ".wxss", ".css"),
2063            "src/components/hello.css"
2064        );
2065
2066        assert_eq!(
2067            resolve_relative_path("src/components/a.wxss", "../hello.wxss", ".wxss", ".css"),
2068            "src/hello.css"
2069        );
2070
2071        assert_eq!(
2072            resolve_relative_path("src/components/a.wxss", ".././hello.wxss", ".wxss", ".css"),
2073            "src/hello.css"
2074        );
2075
2076        assert_eq!(
2077            resolve_relative_path(
2078                "src/components/test/a.wxss",
2079                "../../test/../hello.wxss",
2080                ".wxss",
2081                ".css"
2082            ),
2083            "src/hello.css"
2084        );
2085
2086        assert_eq!(
2087            resolve_relative_path(
2088                "src/components/test/a.wxss",
2089                "../../test/../hello.wxss",
2090                "",
2091                ".css"
2092            ),
2093            "src/hello.wxss.css"
2094        );
2095
2096        assert_eq!(
2097            resolve_relative_path(
2098                "src/components/a.wxss",
2099                "../../../../../hello.wxss",
2100                ".wxss",
2101                ".css"
2102            ),
2103            "../../../hello.css"
2104        );
2105
2106        assert_eq!(
2107            resolve_relative_path("src/components/a.wxss", "/hello.wxss", ".wxss", ".css"),
2108            "hello.css"
2109        );
2110
2111        assert_eq!(
2112            resolve_relative_path("src/components/././a.wxss", "/hello.wxss", ".wxss", ".css"),
2113            "hello.css"
2114        );
2115
2116        assert_eq!(
2117            resolve_relative_path(
2118                "src/components/.\\.\\a.wxss",
2119                "/hello.wxss",
2120                ".wxss",
2121                ".css"
2122            ),
2123            "hello.css"
2124        );
2125
2126        assert!(is_url("https://wxweb/float-pigment"));
2127        assert!(is_url("http://wxweb/float-pigment"));
2128        assert!(is_url("data:application/octet-stream;base64,AAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzJAKEx+AAABfAAAAFZjbWFw65cFHQAAAhwAAAJQZ2x5ZvCRR/EAAASUAAAKtGhlYWQLKIN9AAAA4AAAADZoaGVhCCwD+gAAALwAAAAkaG10eEJo//8AA="));
2129        assert!(!is_url("www.wxweb/float-pigment"));
2130        assert!(!is_url("www.wxweb/float-pigment"));
2131    }
2132
2133    #[test]
2134    fn parse_property_value_string() {
2135        let (prop_none, warnings) = super::parse_property_value_string("display", "none");
2136        assert!(warnings.is_empty());
2137        assert_eq!(
2138            prop_none[0].property().unwrap(),
2139            Property::Display(DisplayType::None)
2140        );
2141    }
2142
2143    #[cfg(test)]
2144    mod parse_inline_style {
2145        use crate::{
2146            parser::{parse_inline_style, StyleParsingDebugMode},
2147            typing::LengthType,
2148        };
2149
2150        #[test]
2151        fn single_prop_ends_without_semicolon() {
2152            let (props, warnings) = parse_inline_style("width: 100px", StyleParsingDebugMode::None);
2153            assert!(warnings.is_empty());
2154            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2155            assert_eq!(width, LengthType::Px(100.));
2156        }
2157
2158        #[test]
2159        fn single_prop_ends_with_semicolon() {
2160            let (props, warnings) =
2161                parse_inline_style("width: 100px;", StyleParsingDebugMode::None);
2162            assert!(warnings.is_empty());
2163            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2164            assert_eq!(width, LengthType::Px(100.));
2165        }
2166
2167        #[test]
2168        fn multi_props_ends_with_semicolon() {
2169            let (props, warnings) =
2170                parse_inline_style("width: 100px;height: 200px;", StyleParsingDebugMode::None);
2171            assert!(warnings.is_empty());
2172            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2173            assert_eq!(width, LengthType::Px(100.));
2174            let height = props.get(1).unwrap().property().unwrap().height().unwrap();
2175            assert_eq!(height, LengthType::Px(200.));
2176        }
2177
2178        #[test]
2179        fn multi_props_ends_without_semicolon() {
2180            let (props, warnings) =
2181                parse_inline_style("width: 100px;height: 200px ", StyleParsingDebugMode::None);
2182            assert!(warnings.is_empty());
2183            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2184            assert_eq!(width, LengthType::Px(100.));
2185            let height = props.get(1).unwrap().property().unwrap().height().unwrap();
2186            assert_eq!(height, LengthType::Px(200.));
2187        }
2188    }
2189}