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