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