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