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                                                _ => {
1272                                                    st.add_warning_with_message(
1273                                                        WarningKind::UnsupportedPseudoElement,
1274                                                        format!("unsupported pseudo elements: {}", parser.slice_from(item_start_pos).trim()),
1275                                                        item_start_loc,
1276                                                        parser.current_source_location(),
1277                                                    );
1278                                                    return Err(
1279                                                        parser.new_custom_error(CustomError::Unsupported)
1280                                                    );
1281                                                }
1282                                            }
1283                                        }
1284                                        _ => {
1285                                            st.add_warning_with_message(
1286                                                WarningKind::UnsupportedSelector,
1287                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1288                                                item_start_loc,
1289                                                parser.current_source_location(),
1290                                            );
1291                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1292                                        }
1293                                    }
1294                                }
1295                                Token::Ident(pseudo_classes) => {
1296                                    let s = pseudo_classes.to_lowercase();
1297                                    match s.as_str() {
1298                                        "first-child" => {
1299                                            cur_frag.set_pseudo_classes(PseudoClasses::FirstChild);
1300                                            prev_sep = PrevSep::None
1301                                        }
1302                                        "last-child" => {
1303                                            cur_frag.set_pseudo_classes(PseudoClasses::LastChild);
1304                                            prev_sep = PrevSep::None
1305                                        }
1306                                        "only-child" => {
1307                                            cur_frag.set_pseudo_classes(PseudoClasses::OnlyChild);
1308                                            prev_sep = PrevSep::None
1309                                        }
1310                                        "empty" => {
1311                                            cur_frag.set_pseudo_classes(PseudoClasses::Empty);
1312                                            prev_sep = PrevSep::None
1313                                        }
1314                                        "host" => {
1315                                            cur_frag.set_pseudo_classes(PseudoClasses::Host);
1316                                            prev_sep = PrevSep::End
1317                                        }
1318                                        // 
1319                                        "before" => {
1320                                            cur_frag.set_pseudo_elements(PseudoElements::Before);
1321                                            prev_sep = PrevSep::End;
1322                                            st.add_warning_with_message(
1323                                                WarningKind::InvalidPseudoElement,
1324                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1325                                                item_start_loc,
1326                                                parser.current_source_location(),
1327                                            );
1328                                        }
1329                                        "after" => {
1330                                            cur_frag.set_pseudo_elements(PseudoElements::After);
1331                                            prev_sep = PrevSep::End;
1332                                            st.add_warning_with_message(
1333                                                WarningKind::InvalidPseudoElement,
1334                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1335                                                item_start_loc,
1336                                                parser.current_source_location(),
1337                                            );
1338                                        }
1339                                        _ => {
1340                                            st.add_warning_with_message(
1341                                                WarningKind::UnsupportedPseudoClass,
1342                                                format!("unsupported pseudo class: {:?}", parser.slice_from(item_start_pos).trim()),
1343                                                item_start_loc,
1344                                                parser.current_source_location(),
1345                                            );
1346                                            return Err(
1347                                                parser.new_custom_error(CustomError::Unsupported)
1348                                            );
1349                                        }
1350                                    }
1351                                }
1352                                Token::Function(ref name) => {
1353                                    let name: &str = name;
1354                                    match name {
1355                                        "not" => {
1356                                            parse_not_function(parser, st, &mut cur_frag, &mut prev_sep, item_start_pos, item_start_loc)?;
1357                                        },
1358                                        "nth-child" => {
1359                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::Child)?;
1360                                        },
1361                                        "nth-of-type" => {
1362                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::OfType)?;
1363                                        }
1364                                        _ => {
1365                                            st.add_warning_with_message(
1366                                                WarningKind::UnsupportedSelector,
1367                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1368                                                item_start_loc,
1369                                                parser.current_source_location(),
1370                                            );
1371                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1372                                        }
1373                                    }
1374                                }
1375                                _ => {
1376                                    st.add_warning_with_message(
1377                                        WarningKind::UnsupportedSelector,
1378                                        format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1379                                        item_start_loc,
1380                                        parser.current_source_location(),
1381                                    );
1382                                    return Err(parser.new_custom_error(CustomError::Unsupported));
1383                                }
1384                            }
1385                        }
1386                        PrevSep::None => {
1387                            let next = parser.next_including_whitespace()?.clone();
1388                            match next {
1389                                Token::Colon => {
1390                                    let next = parser.next_including_whitespace()?.clone();
1391                                    match next {
1392                                        Token::Ident(pseudo_elements) => {
1393                                            let s = pseudo_elements.to_lowercase();
1394                                            match s.as_str() {
1395                                                "before" => {
1396                                                    cur_frag.set_pseudo_elements(PseudoElements::Before);
1397                                                    prev_sep = PrevSep::End
1398                                                }
1399                                                "after" => {
1400                                                    cur_frag.set_pseudo_elements(PseudoElements::After);
1401                                                    prev_sep = PrevSep::End
1402                                                }
1403                                                _ => {
1404                                                    st.add_warning_with_message(
1405                                                        WarningKind::UnsupportedPseudoElement,
1406                                                        format!("unsupported pseudo element: {}", parser.slice_from(item_start_pos).trim()),
1407                                                        item_start_loc,
1408                                                        parser.current_source_location(),
1409                                                    );
1410                                                    return Err(
1411                                                        parser.new_custom_error(CustomError::Unsupported)
1412                                                    );
1413                                                }
1414                                            }
1415                                        }
1416                                        _ => {
1417                                            st.add_warning_with_message(
1418                                                WarningKind::UnsupportedSelector,
1419                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1420                                                item_start_loc,
1421                                                parser.current_source_location(),
1422                                            );
1423                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1424                                        }
1425                                    }
1426                                }
1427                                Token::Ident(pseudo_classes) => {
1428                                    let s = pseudo_classes.to_lowercase();
1429                                    match s.as_str() {
1430                                        "first-child" => {
1431                                            cur_frag.set_pseudo_classes(PseudoClasses::FirstChild);
1432                                            prev_sep = PrevSep::None
1433                                        }
1434                                        "last-child" => {
1435                                            cur_frag.set_pseudo_classes(PseudoClasses::LastChild);
1436                                            prev_sep = PrevSep::None
1437                                        }
1438                                        "only-child" => {
1439                                            cur_frag.set_pseudo_classes(PseudoClasses::OnlyChild);
1440                                            prev_sep = PrevSep::None
1441                                        }
1442                                        "empty" => {
1443                                            cur_frag.set_pseudo_classes(PseudoClasses::Empty);
1444                                            prev_sep = PrevSep::None
1445                                        }
1446                                        // 
1447                                        "before" => {
1448                                            cur_frag.set_pseudo_elements(PseudoElements::Before);
1449                                            prev_sep = PrevSep::End;
1450                                            st.add_warning_with_message(
1451                                                WarningKind::InvalidPseudoElement,
1452                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1453                                                item_start_loc,
1454                                                parser.current_source_location(),
1455                                            );
1456                                        }
1457                                        "after" => {
1458                                            cur_frag.set_pseudo_elements(PseudoElements::After);
1459                                            prev_sep = PrevSep::End;
1460                                            st.add_warning_with_message(
1461                                                WarningKind::InvalidPseudoElement,
1462                                                format!("pseudo-elements should begin with double colons (::): {}", parser.slice_from(item_start_pos).trim()),
1463                                                item_start_loc,
1464                                                parser.current_source_location(),
1465                                            );
1466                                        }
1467                                        _ => {
1468                                            st.add_warning_with_message(
1469                                                WarningKind::UnsupportedPseudoClass,
1470                                                format!("unsupported pseudo class: {}", parser.slice_from(item_start_pos).trim()),
1471                                                item_start_loc,
1472                                                parser.current_source_location(),
1473                                            );
1474                                            return Err(
1475                                                parser.new_custom_error(CustomError::Unsupported)
1476                                            );
1477                                        }
1478                                    }
1479                                }
1480                                Token::Function(ref name) => {
1481                                    let name: &str = name;
1482                                    match name {
1483                                        "not" => {
1484                                            parse_not_function(parser, st, &mut cur_frag, &mut prev_sep, item_start_pos, item_start_loc)?;
1485                                        },
1486                                        "nth-child" => {
1487                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::Child)?;
1488                                        },
1489                                        "nth-of-type" => {
1490                                            parse_nth_function(parser, st, &mut cur_frag, &mut prev_sep, NthType::OfType)?;
1491                                        }
1492                                        _ => {
1493                                            st.add_warning_with_message(
1494                                                WarningKind::UnsupportedSelector,
1495                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1496                                                item_start_loc,
1497                                                parser.current_source_location(),
1498                                            );
1499                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1500                                        }
1501                                    }
1502                                }
1503                                _ => {
1504                                    st.add_warning_with_message(
1505                                        WarningKind::UnsupportedSelector,
1506                                        format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1507                                        item_start_loc,
1508                                        parser.current_source_location(),
1509                                    );
1510                                    return Err(parser.new_custom_error(CustomError::Unsupported));
1511                                }
1512                            }
1513                        }
1514                        PrevSep::PseudoClassesNot => {
1515                            let next = parser.next_including_whitespace()?.clone();
1516                            match next {
1517                                Token::Function(ref name) => {
1518                                    let name: &str = name;
1519                                    match name {
1520                                        "not" => {
1521                                           parse_not_function(parser, st, &mut cur_frag, &mut prev_sep, item_start_pos, item_start_loc)?;
1522                                        },
1523                                        _ => {
1524                                            st.add_warning_with_message(
1525                                                WarningKind::UnsupportedSelector,
1526                                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1527                                                item_start_loc,
1528                                                parser.current_source_location(),
1529                                            );
1530                                            return Err(parser.new_custom_error(CustomError::Unsupported));
1531                                        }
1532                                    }
1533                                }
1534                                _ => {
1535                                    st.add_warning_with_message(
1536                                        WarningKind::UnsupportedSelector,
1537                                        format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1538                                        item_start_loc,
1539                                        parser.current_source_location(),
1540                                    );
1541                                    return Err(parser.new_custom_error(CustomError::Unsupported));
1542                                }
1543                            }
1544                        }
1545                        _ => {
1546                            st.add_warning_with_message(
1547                                WarningKind::UnsupportedSelector,
1548                                format!(r#"unsupported selector: {}"#, parser.slice_from(item_start_pos).trim()),
1549                                item_start_loc,
1550                                parser.current_source_location(),
1551                            );
1552                            return Err(parser.new_custom_error(CustomError::Unsupported));
1553                        }
1554                    },
1555                    Token::WhiteSpace(_) => {
1556                        prev_sep = PrevSep::Space;
1557                    }
1558                    Token::CDC => {}
1559                    Token::CDO => {}
1560                    Token::Comment(_) => {
1561                        prev_sep = PrevSep::Space;
1562                    }
1563                    Token::SquareBracketBlock => {
1564                        clear_prev_sep!();
1565                        let attr = parser.parse_nested_block(|parser| {
1566                            parse_attribute_selector(parser)
1567                        })?;
1568                        cur_frag.add_attribute(attr);
1569                    }
1570                    _ => {
1571                        st.add_warning_with_message(
1572                            WarningKind::UnsupportedSelector,
1573                            format!(r#"unsupported selector: {}"#, parser.slice_from(start_pos).trim()),
1574                            start_loc,
1575                            parser.current_source_location(),
1576                        );
1577                        return Err(parser.new_custom_error(CustomError::Unsupported));
1578                    }
1579                };
1580            }
1581            // if let PrevSep::Init = prev_sep {
1582            //     st.add_warning(
1583            //         format!(r#"Selector should be set"#),
1584            //         item_start_loc,
1585            //         parser.current_source_location(),
1586            //     );
1587            //     return Err(parser.new_custom_error(CustomError::Unsupported));
1588            // };
1589            if let PrevSep::Child = prev_sep {
1590                st.add_warning_with_message(
1591                    WarningKind::InvalidSelector,
1592                    format!(r#"selector not terminated: {}"#, parser.slice_from(item_start_pos).trim()),
1593                    item_start_loc,
1594                    parser.current_source_location(),
1595                );
1596                return Err(parser.new_custom_error(CustomError::Unsupported));
1597            };
1598            Ok(cur_frag)
1599        })
1600    })?;
1601    Ok(Selector::from_fragments(fragments))
1602}
1603
1604#[inline(always)]
1605fn parse_attribute_selector<'a, 't: 'a, 'i: 't>(
1606    parser: &'a mut Parser<'i, 't>,
1607) -> Result<Attribute, ParseError<'i, CustomError>> {
1608    parser.skip_whitespace();
1609
1610    // parse attribute name
1611    let name = parser.expect_ident()?.to_string();
1612
1613    // try parse operator
1614    let location: SourceLocation = parser.current_source_location();
1615    let operator = match parser.next() {
1616        // [name]
1617        Err(_) => return Ok(Attribute::new_set(name.to_string())),
1618        // [name=value]
1619        Ok(&Token::Delim('=')) => AttributeOperator::Exact,
1620        // [name~=value]
1621        Ok(&Token::IncludeMatch) => AttributeOperator::List,
1622        // [name|=value]
1623        Ok(&Token::DashMatch) => AttributeOperator::Hyphen,
1624        // [name^=value]
1625        Ok(&Token::PrefixMatch) => AttributeOperator::Begin,
1626        // [name$=value]
1627        Ok(&Token::SuffixMatch) => AttributeOperator::End,
1628        // [name*=value]
1629        Ok(&Token::SubstringMatch) => AttributeOperator::Contain,
1630        Ok(_) => {
1631            return Err(location.new_custom_error(CustomError::UnexpectedTokenInAttributeSelector))
1632        }
1633    };
1634
1635    let value = match parser.expect_ident_or_string() {
1636        Ok(t) => t.clone(),
1637        Err(BasicParseError {
1638            kind: BasicParseErrorKind::UnexpectedToken(_),
1639            location,
1640        }) => return Err(location.new_custom_error(CustomError::BadValueInAttr)),
1641        Err(e) => return Err(e.into()),
1642    }
1643    .to_string();
1644    let never_matches = match operator {
1645        AttributeOperator::Exact | AttributeOperator::Hyphen => false,
1646        AttributeOperator::Begin | AttributeOperator::End | AttributeOperator::List => {
1647            value.is_empty()
1648        }
1649        AttributeOperator::Contain => value.is_empty() || value.contains(SELECTOR_WHITESPACE),
1650        AttributeOperator::Set => unreachable!(),
1651    };
1652    let attribute_flags = parse_attribute_flags(parser)?;
1653    Ok(Attribute {
1654        operator,
1655        case_insensitive: attribute_flags,
1656        never_matches,
1657        name,
1658        value: Some(value),
1659    })
1660}
1661
1662#[inline(always)]
1663fn parse_attribute_flags<'a, 't: 'a, 'i: 't>(
1664    parser: &'a mut Parser<'i, 't>,
1665) -> Result<AttributeFlags, BasicParseError<'i>> {
1666    let location = parser.current_source_location();
1667    match parser.next() {
1668        Ok(t) => {
1669            if let Token::Ident(ref i) = t {
1670                Ok(match_ignore_ascii_case! {
1671                    i,
1672                    "i" => AttributeFlags::CaseInsensitive,
1673                    "s" => AttributeFlags::CaseSensitive,
1674                    _ => return Err(location.new_basic_unexpected_token_error(t.clone())),
1675                })
1676            } else {
1677                Err(location.new_basic_unexpected_token_error(t.clone()))
1678            }
1679        }
1680        Err(_) => Ok(AttributeFlags::CaseSensitivityDependsOnName),
1681    }
1682}
1683
1684#[inline(always)]
1685fn parse_property_list<'a, 't: 'a, 'i: 't>(
1686    parser: &'a mut Parser<'i, 't>,
1687    properties: &'a mut Vec<PropertyMeta>,
1688    st: &mut ParseState,
1689    close_curly_block_position: Option<SourcePosition>,
1690) {
1691    loop {
1692        if st.debug_mode != StyleParsingDebugMode::None
1693            && parser
1694                .try_parse(|parser| loop {
1695                    let token = parser.next_including_whitespace_and_comments()?;
1696                    match token {
1697                        Token::Comment(s) => {
1698                            let mut commented_props =
1699                                parse_inline_style(s, StyleParsingDebugMode::DebugAndDisabled).0;
1700                            properties.append(&mut commented_props);
1701                            break Ok(());
1702                        }
1703                        Token::WhiteSpace(_) => {
1704                            continue;
1705                        }
1706                        _ => {
1707                            let token = token.clone();
1708                            break Err(parser.new_basic_unexpected_token_error(token));
1709                        }
1710                    }
1711                })
1712                .is_ok()
1713        {
1714            continue;
1715        }
1716        while parser.try_parse(|parser| parser.expect_semicolon()).is_ok() {}
1717        parser.skip_whitespace();
1718        if parser.is_exhausted() {
1719            break;
1720        }
1721        let prev_properties_len = properties.len();
1722        let start_loc = parser.current_source_location();
1723        let start_pos = parser.position();
1724
1725        let mut rule_end_position = None;
1726        let current_state = parser.state();
1727        while !parser.is_exhausted() {
1728            if let Ok(&Token::Semicolon) = parser.next() {
1729                rule_end_position = Some(parser.position());
1730                break;
1731            }
1732        }
1733        if rule_end_position.is_none() {
1734            rule_end_position = close_curly_block_position;
1735        }
1736        parser.reset(&current_state);
1737        parser
1738            .parse_until_after(Delimiter::Semicolon, |parser| {
1739                let mut ret = if st.debug_mode != StyleParsingDebugMode::None {
1740                    parse_property_item_debug(
1741                        parser,
1742                        properties,
1743                        st,
1744                        st.debug_mode == StyleParsingDebugMode::DebugAndDisabled,
1745                        rule_end_position,
1746                    )
1747                } else {
1748                    parse_property_item(parser, properties, st, rule_end_position)
1749                };
1750                if ret.is_err() {
1751                    while !parser.is_exhausted() {
1752                        let _ = parser.next();
1753                    }
1754                    return ret;
1755                }
1756                if !parser.is_exhausted() {
1757                    ret = Err(parser.new_custom_error(CustomError::UnsupportedProperty));
1758                }
1759                ret
1760            })
1761            .unwrap_or_else(|err| {
1762                // restore properties state
1763                properties.drain(prev_properties_len..);
1764                let end_pos = parser.position();
1765                let end_loc = parser.current_source_location();
1766                let mut kind = WarningKind::UnsupportedProperty;
1767                let mut warning_tmpl = "unsupported property".to_string();
1768                if let ParseErrorKind::Custom(CustomError::Reason(s)) = err.kind {
1769                    kind = WarningKind::InvalidProperty;
1770                    warning_tmpl = s;
1771                }
1772                st.add_warning_with_message(
1773                    kind,
1774                    format!(
1775                        "{}: {}",
1776                        warning_tmpl,
1777                        parser.slice(start_pos..end_pos).trim()
1778                    ),
1779                    start_loc,
1780                    end_loc,
1781                );
1782            });
1783    }
1784}
1785
1786#[inline(always)]
1787fn parse_property_item<'a, 't: 'a, 'i: 't>(
1788    parser: &'a mut Parser<'i, 't>,
1789    properties: &'a mut Vec<PropertyMeta>,
1790    st: &mut ParseState,
1791    rule_end_position: Option<SourcePosition>,
1792) -> Result<(), ParseError<'i, CustomError>> {
1793    parser.skip_whitespace();
1794    let prop_name_start_loc = parser.current_source_location();
1795    let prop_name_start_pos = parser.position();
1796    let (name, is_custom_property) =
1797        parse_property_name(parser, prop_name_start_loc, prop_name_start_pos, st)?;
1798    if is_custom_property {
1799        parse_custom_property_value_with_important(parser, &name, properties, rule_end_position)?;
1800    } else {
1801        parse_property_value_with_important(
1802            parser,
1803            &name,
1804            properties,
1805            prop_name_start_loc,
1806            st,
1807            rule_end_position,
1808        )?;
1809    }
1810    Ok(())
1811}
1812
1813#[inline(always)]
1814fn parse_property_item_debug<'a, 't: 'a, 'i: 't>(
1815    parser: &'a mut Parser<'i, 't>,
1816    properties: &'a mut Vec<PropertyMeta>,
1817    st: &mut ParseState,
1818    disabled: bool,
1819    rule_end_position: Option<SourcePosition>,
1820) -> Result<(), ParseError<'i, CustomError>> {
1821    parser.skip_whitespace();
1822    let prev_properties_len = properties.len();
1823    let prop_name_start_index = parser.position();
1824    let prop_name_start_loc = parser.current_source_location();
1825    let (name, is_custom_property) =
1826        parse_property_name(parser, prop_name_start_loc, prop_name_start_index, st)?;
1827    let prop_value_start_index = parser.position();
1828    if is_custom_property {
1829        parse_custom_property_value_with_important(parser, &name, properties, rule_end_position)?;
1830    } else {
1831        parse_property_value_with_important(
1832            parser,
1833            &name,
1834            properties,
1835            prop_name_start_loc,
1836            st,
1837            rule_end_position,
1838        )?;
1839    }
1840    let mut is_important = false;
1841    let grouped_properties = properties
1842        .drain(prev_properties_len..)
1843        .map(|p| match p {
1844            PropertyMeta::Normal { property } => property,
1845            PropertyMeta::Important { property } => {
1846                is_important = true;
1847                property
1848            }
1849            PropertyMeta::DebugGroup { .. } => unreachable!(),
1850        })
1851        .collect::<Box<_>>();
1852    let name_with_colon = parser.slice(prop_name_start_index..prop_value_start_index);
1853    let name = &name_with_colon[0..(name_with_colon.len() - 1)];
1854    let value = parser.slice_from(prop_value_start_index);
1855    properties.push(PropertyMeta::DebugGroup {
1856        original_name_value: Box::new((name.into(), value.into())),
1857        properties: grouped_properties,
1858        important: is_important,
1859        disabled,
1860    });
1861    Ok(())
1862}
1863
1864#[inline(always)]
1865fn parse_property_name<'a, 't: 'a, 'i: 't>(
1866    parser: &'a mut Parser<'i, 't>,
1867    prop_name_start_loc: SourceLocation,
1868    prop_name_start_pos: SourcePosition,
1869    st: &mut ParseState,
1870) -> Result<(CowRcStr<'i>, bool), ParseError<'i, CustomError>> {
1871    let t = parser.expect_ident().cloned();
1872    let name = t.inspect_err(|_| {
1873        st.add_warning_with_message(
1874            WarningKind::InvalidProperty,
1875            format!(
1876                r#"invalid property: {}"#,
1877                parser.slice_from(prop_name_start_pos).trim()
1878            ),
1879            prop_name_start_loc,
1880            parser.current_source_location(),
1881        );
1882    })?;
1883    parser.expect_colon().inspect_err(|_| {
1884        st.add_warning_with_message(
1885            WarningKind::MissingColonAfterProperty,
1886            format!(
1887                r#"expect colon after property: {}"#,
1888                parser.slice_from(prop_name_start_pos).trim()
1889            ),
1890            prop_name_start_loc,
1891            parser.current_source_location(),
1892        );
1893    })?;
1894    let is_custom_property = name.starts_with("--");
1895    Ok((name, is_custom_property))
1896}
1897
1898#[inline(always)]
1899fn parse_property_value_with_important<'a, 't: 'a, 'i: 't>(
1900    parser: &'a mut Parser<'i, 't>,
1901    name: &str,
1902    properties: &'a mut Vec<PropertyMeta>,
1903    prop_name_start_loc: SourceLocation,
1904    st: &mut ParseState,
1905    rule_end_position: Option<SourcePosition>,
1906) -> Result<(), ParseError<'i, CustomError>> {
1907    let prev_properties_len = properties.len();
1908    let skip_parse_important =
1909        parse_property_value(parser, name, properties, st, rule_end_position)?;
1910    if !skip_parse_important {
1911        let is_important = parser.try_parse(parse_important).is_ok();
1912        if is_important {
1913            for pm in &mut properties[prev_properties_len..] {
1914                let mut pm2: PropertyMeta = PropertyMeta::Normal {
1915                    property: Property::Unknown,
1916                };
1917                core::mem::swap(&mut pm2, pm);
1918                *pm = match pm2 {
1919                    PropertyMeta::Normal { property } => PropertyMeta::Important { property },
1920                    PropertyMeta::Important { .. } => unreachable!(),
1921                    PropertyMeta::DebugGroup { .. } => unreachable!(),
1922                };
1923            }
1924        };
1925    }
1926    for mut pm in &mut properties[prev_properties_len..] {
1927        let ParseState {
1928            ref mut warnings,
1929            ref mut hooks,
1930            ..
1931        } = st;
1932        if let Some(hooks) = hooks.as_mut() {
1933            if let Some(p) = match &mut pm {
1934                PropertyMeta::Normal { property } => Some(property),
1935                PropertyMeta::Important { property } => Some(property),
1936                PropertyMeta::DebugGroup { .. } => None,
1937            } {
1938                let ctx = &mut hooks::ParserHooksContext {
1939                    warnings,
1940                    start_loc: prop_name_start_loc,
1941                    end_loc: parser.current_source_location(),
1942                };
1943                hooks.parsed_property(ctx, p);
1944            }
1945        }
1946    }
1947    Ok(())
1948}
1949
1950#[inline(always)]
1951fn parse_custom_property_value_with_important<'a, 't: 'a, 'i: 't>(
1952    parser: &'a mut Parser<'i, 't>,
1953    name: &str,
1954    properties: &'a mut Vec<PropertyMeta>,
1955    rule_end_position: Option<SourcePosition>,
1956) -> Result<(), ParseError<'i, CustomError>> {
1957    if name.len() <= 2 {
1958        return Err(parser.new_custom_error(CustomError::Unmatched));
1959    }
1960    let value_start_pos = parser.position();
1961    parser
1962        .parse_until_before::<_, _, CustomError>(Delimiter::Semicolon, |parser| {
1963            while !parser.is_exhausted() {
1964                parser.next()?;
1965            }
1966            let mut value: &str = parser
1967                .slice(value_start_pos..rule_end_position.unwrap_or_else(|| parser.position()));
1968            value = value.trim_end_matches(['\n', '}', ';']);
1969            if value.trim_end().ends_with("!important") {
1970                value = value.trim_end().trim_end_matches("!important");
1971                if value.trim_end().ends_with("!important") {
1972                    return Err(parser.new_custom_error(CustomError::Unmatched));
1973                }
1974                properties.push(PropertyMeta::Important {
1975                    property: Property::CustomProperty(CustomPropertyType::Expr(
1976                        name.trim().into(),
1977                        value.into(),
1978                    )),
1979                })
1980            } else {
1981                properties.push(PropertyMeta::Normal {
1982                    property: Property::CustomProperty(CustomPropertyType::Expr(
1983                        name.trim().into(),
1984                        value.into(),
1985                    )),
1986                });
1987            }
1988            // TODO impl debug
1989            Ok(())
1990        })
1991        .map_err(|_| parser.new_custom_error(CustomError::Unsupported))
1992}
1993
1994#[cfg(test)]
1995mod test {
1996
1997    use super::{is_url, parse_color_to_rgba, resolve_relative_path};
1998
1999    #[test]
2000    fn parse_color_test() {
2001        let source = "#FFFFFF";
2002        let ret = parse_color_to_rgba(source);
2003        assert_eq!(ret.0, 255);
2004        assert_eq!(ret.1, 255);
2005        assert_eq!(ret.2, 255);
2006        assert_eq!(ret.3, 255);
2007
2008        let source = "red";
2009        let ret = parse_color_to_rgba(source);
2010        assert_eq!(ret.0, 255);
2011        assert_eq!(ret.1, 0);
2012        assert_eq!(ret.2, 0);
2013        assert_eq!(ret.3, 255);
2014    }
2015
2016    #[test]
2017    fn resolve_relative_path_test() {
2018        assert_eq!(
2019            resolve_relative_path("/src/components/a.wxss", "./hello.wxss", ".wxss", ".css"),
2020            "src/components/hello.css"
2021        );
2022
2023        assert_eq!(
2024            resolve_relative_path("src/components/a.wxss", "./hello.wxss", ".wxss", ".css"),
2025            "src/components/hello.css"
2026        );
2027
2028        assert_eq!(
2029            resolve_relative_path("src/components/a.wxss", "../hello.wxss", ".wxss", ".css"),
2030            "src/hello.css"
2031        );
2032
2033        assert_eq!(
2034            resolve_relative_path("src/components/a.wxss", ".././hello.wxss", ".wxss", ".css"),
2035            "src/hello.css"
2036        );
2037
2038        assert_eq!(
2039            resolve_relative_path(
2040                "src/components/test/a.wxss",
2041                "../../test/../hello.wxss",
2042                ".wxss",
2043                ".css"
2044            ),
2045            "src/hello.css"
2046        );
2047
2048        assert_eq!(
2049            resolve_relative_path(
2050                "src/components/test/a.wxss",
2051                "../../test/../hello.wxss",
2052                "",
2053                ".css"
2054            ),
2055            "src/hello.wxss.css"
2056        );
2057
2058        assert_eq!(
2059            resolve_relative_path(
2060                "src/components/a.wxss",
2061                "../../../../../hello.wxss",
2062                ".wxss",
2063                ".css"
2064            ),
2065            "../../../hello.css"
2066        );
2067
2068        assert_eq!(
2069            resolve_relative_path("src/components/a.wxss", "/hello.wxss", ".wxss", ".css"),
2070            "hello.css"
2071        );
2072
2073        assert_eq!(
2074            resolve_relative_path("src/components/././a.wxss", "/hello.wxss", ".wxss", ".css"),
2075            "hello.css"
2076        );
2077
2078        assert_eq!(
2079            resolve_relative_path(
2080                "src/components/.\\.\\a.wxss",
2081                "/hello.wxss",
2082                ".wxss",
2083                ".css"
2084            ),
2085            "hello.css"
2086        );
2087
2088        assert!(is_url("https://wxweb/float-pigment"));
2089        assert!(is_url("http://wxweb/float-pigment"));
2090        assert!(is_url("data:application/octet-stream;base64,AAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzJAKEx+AAABfAAAAFZjbWFw65cFHQAAAhwAAAJQZ2x5ZvCRR/EAAASUAAAKtGhlYWQLKIN9AAAA4AAAADZoaGVhCCwD+gAAALwAAAAkaG10eEJo//8AA="));
2091        assert!(!is_url("www.wxweb/float-pigment"));
2092        assert!(!is_url("www.wxweb/float-pigment"));
2093    }
2094
2095    #[cfg(test)]
2096    mod parse_inline_style {
2097        use crate::{
2098            parser::{parse_inline_style, StyleParsingDebugMode},
2099            typing::LengthType,
2100        };
2101
2102        #[test]
2103        fn single_prop_ends_without_semicolon() {
2104            let (props, warnings) = parse_inline_style("width: 100px", StyleParsingDebugMode::None);
2105            assert!(warnings.is_empty());
2106            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2107            assert_eq!(width, LengthType::Px(100.));
2108        }
2109
2110        #[test]
2111        fn single_prop_ends_with_semicolon() {
2112            let (props, warnings) =
2113                parse_inline_style("width: 100px;", StyleParsingDebugMode::None);
2114            assert!(warnings.is_empty());
2115            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2116            assert_eq!(width, LengthType::Px(100.));
2117        }
2118
2119        #[test]
2120        fn multi_props_ends_with_semicolon() {
2121            let (props, warnings) =
2122                parse_inline_style("width: 100px;height: 200px;", 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            let height = props.get(1).unwrap().property().unwrap().height().unwrap();
2127            assert_eq!(height, LengthType::Px(200.));
2128        }
2129
2130        #[test]
2131        fn multi_props_ends_without_semicolon() {
2132            let (props, warnings) =
2133                parse_inline_style("width: 100px;height: 200px ", StyleParsingDebugMode::None);
2134            assert!(warnings.is_empty());
2135            let width = props.get(0).unwrap().property().unwrap().width().unwrap();
2136            assert_eq!(width, LengthType::Px(100.));
2137            let height = props.get(1).unwrap().property().unwrap().height().unwrap();
2138            assert_eq!(height, LengthType::Px(200.));
2139        }
2140    }
2141}