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 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
401fn 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 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 "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 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(¤t_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 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 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(¤t_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 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 "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 "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::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 let name = parser.expect_ident()?.to_string();
1605
1606 let location: SourceLocation = parser.current_source_location();
1608 let operator = match parser.next() {
1609 Err(_) => return Ok(Attribute::new_set(name.to_string())),
1611 Ok(&Token::Delim('=')) => AttributeOperator::Exact,
1613 Ok(&Token::IncludeMatch) => AttributeOperator::List,
1615 Ok(&Token::DashMatch) => AttributeOperator::Hyphen,
1617 Ok(&Token::PrefixMatch) => AttributeOperator::Begin,
1619 Ok(&Token::SuffixMatch) => AttributeOperator::End,
1621 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(¤t_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 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 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}