1use cssparser::{
77 self, AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, ParserInput,
78 ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
79 StyleSheetParser, ToCss, match_ignore_ascii_case, parse_important,
80};
81use language_tags::LanguageTag;
82use markup5ever::{self, Namespace, QualName, ns};
83use precomputed_hash::PrecomputedHash;
84use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
85use selectors::bloom::BloomFilter;
86use selectors::context::SelectorCaches;
87use selectors::matching::{
88 ElementSelectorFlags, MatchingContext, MatchingForInvalidation, MatchingMode,
89 NeedsSelectorFlags, QuirksMode,
90};
91use selectors::parser::ParseRelative;
92use selectors::{OpaqueElement, SelectorImpl, SelectorList};
93use std::cmp::Ordering;
94use std::fmt;
95use std::str;
96use std::str::FromStr;
97
98use crate::document::LoadingDepthLimiter;
99use crate::element::Element;
100use crate::error::*;
101use crate::io;
102use crate::node::{Node, NodeBorrow, NodeCascade};
103use crate::properties::{ComputedValues, ParseAs, ParsedProperty, parse_value};
104use crate::rsvg_log;
105use crate::session::Session;
106use crate::url_resolver::{AllowedUrl, UrlResolver};
107
108pub struct Declaration {
115 pub prop_name: QualName,
116 pub property: ParsedProperty,
117 pub important: bool,
118}
119
120pub enum RuleBodyItem {
123 Decl(Declaration),
124 #[allow(dead_code)] Rule(Rule),
126}
127
128pub struct DeclParser;
133
134impl<'i> DeclarationParser<'i> for DeclParser {
135 type Declaration = RuleBodyItem;
136 type Error = ValueErrorKind;
137
138 fn parse_value<'t>(
140 &mut self,
141 name: CowRcStr<'i>,
142 input: &mut Parser<'i, 't>,
143 _declaration_start: &ParserState,
144 ) -> Result<RuleBodyItem, cssparser::ParseError<'i, Self::Error>> {
145 let prop_name = QualName::new(None, ns!(), markup5ever::LocalName::from(name.as_ref()));
146 let property = parse_value(&prop_name, input, ParseAs::Property)?;
147
148 let important = input.try_parse(parse_important).is_ok();
149
150 Ok(RuleBodyItem::Decl(Declaration {
151 prop_name,
152 property,
153 important,
154 }))
155 }
156}
157
158impl<'i> AtRuleParser<'i> for DeclParser {
163 type Prelude = ();
164 type AtRule = RuleBodyItem;
165 type Error = ValueErrorKind;
166}
167
168impl<'i> QualifiedRuleParser<'i> for DeclParser {
170 type Prelude = ();
171 type QualifiedRule = RuleBodyItem;
172 type Error = ValueErrorKind;
173}
174
175impl<'i> RuleBodyItemParser<'i, RuleBodyItem, ValueErrorKind> for DeclParser {
176 fn parse_declarations(&self) -> bool {
178 true
179 }
180
181 fn parse_qualified(&self) -> bool {
183 false
184 }
185}
186
187pub struct RuleParser {
189 session: Session,
190}
191
192#[allow(dead_code)] #[derive(Debug)]
195pub enum ParseErrorKind<'i> {
196 Selector(selectors::parser::SelectorParseErrorKind<'i>),
197}
198
199impl<'i> From<selectors::parser::SelectorParseErrorKind<'i>> for ParseErrorKind<'i> {
200 fn from(e: selectors::parser::SelectorParseErrorKind<'_>) -> ParseErrorKind<'_> {
201 ParseErrorKind::Selector(e)
202 }
203}
204
205pub struct QualifiedRule {
207 selectors: SelectorList<Selector>,
208 declarations: Vec<Declaration>,
209}
210
211pub enum AtRulePrelude {
213 Import(String),
214}
215
216pub enum AtRule {
218 Import(String),
219}
220
221pub enum Rule {
223 AtRule(AtRule),
224 QualifiedRule(QualifiedRule),
225}
226
227impl<'i> selectors::Parser<'i> for RuleParser {
229 type Impl = Selector;
230 type Error = ParseErrorKind<'i>;
231
232 fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
233 Some(ns!(svg))
234 }
235
236 fn namespace_for_prefix(
237 &self,
238 _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix,
239 ) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
240 None
245 }
246 fn parse_non_ts_pseudo_class(
247 &self,
248 location: SourceLocation,
249 name: CowRcStr<'i>,
250 ) -> Result<NonTSPseudoClass, cssparser::ParseError<'i, Self::Error>> {
251 match &*name {
252 "link" => Ok(NonTSPseudoClass::Link),
253 "visited" => Ok(NonTSPseudoClass::Visited),
254 _ => Err(location.new_custom_error(
255 selectors::parser::SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
256 )),
257 }
258 }
259 fn parse_non_ts_functional_pseudo_class(
260 &self,
261 name: CowRcStr<'i>,
262 arguments: &mut Parser<'i, '_>,
263 _after_part: bool,
264 ) -> Result<NonTSPseudoClass, cssparser::ParseError<'i, Self::Error>> {
265 match &*name {
266 "lang" => {
267 let tags = arguments.parse_comma_separated(|arg| {
270 let language_tag = arg.expect_ident_or_string()?.clone();
271 LanguageTag::from_str(&language_tag).map_err(|_| {
272 arg.new_custom_error(selectors::parser::SelectorParseErrorKind::UnsupportedPseudoClassOrElement(language_tag))
273 })
274 })?;
275 arguments.expect_exhausted()?;
276 Ok(NonTSPseudoClass::Lang(tags))
277 }
278 _ => Err(arguments.new_custom_error(
279 selectors::parser::SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
280 )),
281 }
282 }
283}
284
285impl<'i> QualifiedRuleParser<'i> for RuleParser {
305 type Prelude = SelectorList<Selector>;
306 type QualifiedRule = Rule;
307 type Error = ValueErrorKind;
308
309 fn parse_prelude<'t>(
310 &mut self,
311 input: &mut Parser<'i, 't>,
312 ) -> Result<Self::Prelude, cssparser::ParseError<'i, Self::Error>> {
313 SelectorList::parse(self, input, ParseRelative::No).map_err(|e| ParseError {
314 kind: cssparser::ParseErrorKind::Custom(ValueErrorKind::parse_error(
315 "Could not parse selector",
316 )),
317 location: e.location,
318 })
319 }
320
321 fn parse_block<'t>(
322 &mut self,
323 prelude: Self::Prelude,
324 _start: &ParserState,
325 input: &mut Parser<'i, 't>,
326 ) -> Result<Self::QualifiedRule, cssparser::ParseError<'i, Self::Error>> {
327 let declarations = RuleBodyParser::<_, _, Self::Error>::new(input, &mut DeclParser)
328 .filter_map(|r| match r {
329 Ok(RuleBodyItem::Decl(decl)) => Some(decl),
330 Ok(RuleBodyItem::Rule(_)) => None,
331 Err(e) => {
332 rsvg_log!(self.session, "Invalid declaration; ignoring: {:?}", e);
333 None
334 }
335 })
336 .collect();
337
338 Ok(Rule::QualifiedRule(QualifiedRule {
339 selectors: prelude,
340 declarations,
341 }))
342 }
343}
344
345impl<'i> AtRuleParser<'i> for RuleParser {
349 type Prelude = AtRulePrelude;
350 type AtRule = Rule;
351 type Error = ValueErrorKind;
352
353 #[allow(clippy::type_complexity)]
354 fn parse_prelude<'t>(
355 &mut self,
356 name: CowRcStr<'i>,
357 input: &mut Parser<'i, 't>,
358 ) -> Result<Self::Prelude, cssparser::ParseError<'i, Self::Error>> {
359 match_ignore_ascii_case! {
360 &name,
361
362 "import" => {
365 let url = input.expect_url_or_string()?.as_ref().to_owned();
366 Ok(AtRulePrelude::Import(url))
367 },
368
369 _ => Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))),
370 }
371 }
372
373 fn rule_without_block(
374 &mut self,
375 prelude: Self::Prelude,
376 _start: &ParserState,
377 ) -> Result<Self::AtRule, ()> {
378 let AtRulePrelude::Import(url) = prelude;
379 Ok(Rule::AtRule(AtRule::Import(url)))
380 }
381
382 }
384
385#[allow(clippy::upper_case_acronyms)]
387#[derive(Clone, Debug, Eq, PartialEq)]
388pub enum NonTSPseudoClass {
389 Link,
390 Visited,
391 Lang(Vec<LanguageTag>),
392}
393
394impl ToCss for NonTSPseudoClass {
395 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
396 where
397 W: fmt::Write,
398 {
399 match self {
400 NonTSPseudoClass::Link => write!(dest, "link"),
401 NonTSPseudoClass::Visited => write!(dest, "visited"),
402 NonTSPseudoClass::Lang(lang) => write!(
403 dest,
404 "lang(\"{}\")",
405 lang.iter()
406 .map(ToString::to_string)
407 .collect::<Vec<_>>()
408 .join("\",\"")
409 ),
410 }
411 }
412}
413
414impl selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
415 type Impl = Selector;
416
417 fn is_active_or_hover(&self) -> bool {
418 false
419 }
420
421 fn is_user_action_state(&self) -> bool {
422 false
423 }
424}
425
426#[derive(Clone, Debug, Eq, PartialEq)]
428pub struct PseudoElement;
429
430impl ToCss for PseudoElement {
431 fn to_css<W>(&self, _dest: &mut W) -> fmt::Result
432 where
433 W: fmt::Write,
434 {
435 Ok(())
436 }
437}
438
439impl selectors::parser::PseudoElement for PseudoElement {
440 type Impl = Selector;
441}
442
443#[derive(Debug, Clone)]
445pub struct Selector;
446
447#[derive(Clone, PartialEq, Eq)]
454pub struct AttributeValue(String);
455
456impl From<&str> for AttributeValue {
457 fn from(s: &str) -> AttributeValue {
458 AttributeValue(s.to_owned())
459 }
460}
461
462impl ToCss for AttributeValue {
463 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
464 where
465 W: fmt::Write,
466 {
467 use std::fmt::Write;
468
469 write!(cssparser::CssStringWriter::new(dest), "{}", &self.0)
470 }
471}
472
473impl AsRef<str> for AttributeValue {
474 fn as_ref(&self) -> &str {
475 self.0.as_ref()
476 }
477}
478
479#[derive(Clone, PartialEq, Eq)]
483pub struct Identifier(markup5ever::LocalName);
484
485impl From<&str> for Identifier {
486 fn from(s: &str) -> Identifier {
487 Identifier(markup5ever::LocalName::from(s))
488 }
489}
490
491impl ToCss for Identifier {
492 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
493 where
494 W: fmt::Write,
495 {
496 cssparser::serialize_identifier(&self.0, dest)
497 }
498}
499
500impl PrecomputedHash for Identifier {
501 fn precomputed_hash(&self) -> u32 {
502 self.0.precomputed_hash()
503 }
504}
505
506#[derive(Clone, PartialEq, Eq)]
510pub struct LocalName(markup5ever::LocalName);
511
512impl From<&str> for LocalName {
513 fn from(s: &str) -> LocalName {
514 LocalName(markup5ever::LocalName::from(s))
515 }
516}
517
518impl ToCss for LocalName {
519 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
520 where
521 W: fmt::Write,
522 {
523 cssparser::serialize_identifier(&self.0, dest)
524 }
525}
526
527impl PrecomputedHash for LocalName {
528 fn precomputed_hash(&self) -> u32 {
529 self.0.precomputed_hash()
530 }
531}
532
533#[derive(Clone, Default, PartialEq, Eq)]
537pub struct NamespacePrefix(markup5ever::Prefix);
538
539impl From<&str> for NamespacePrefix {
540 fn from(s: &str) -> NamespacePrefix {
541 NamespacePrefix(markup5ever::Prefix::from(s))
542 }
543}
544
545impl ToCss for NamespacePrefix {
546 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
547 where
548 W: fmt::Write,
549 {
550 cssparser::serialize_identifier(&self.0, dest)
551 }
552}
553
554impl SelectorImpl for Selector {
555 type ExtraMatchingData<'a> = ();
556 type AttrValue = AttributeValue;
557 type Identifier = Identifier;
558 type LocalName = LocalName;
559 type NamespaceUrl = Namespace;
560 type NamespacePrefix = NamespacePrefix;
561 type BorrowedNamespaceUrl = Namespace;
562 type BorrowedLocalName = LocalName;
563 type NonTSPseudoClass = NonTSPseudoClass;
564 type PseudoElement = PseudoElement;
565}
566
567#[derive(Clone, PartialEq)]
573pub struct RsvgElement(Node);
574
575impl From<Node> for RsvgElement {
576 fn from(n: Node) -> RsvgElement {
577 RsvgElement(n)
578 }
579}
580
581impl fmt::Debug for RsvgElement {
582 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583 write!(f, "{}", self.0.borrow())
584 }
585}
586
587impl selectors::Element for RsvgElement {
589 type Impl = Selector;
590
591 fn opaque(&self) -> OpaqueElement {
593 let element: &Element = &self.0.borrow_element();
603 OpaqueElement::new::<Element>(element)
604 }
605
606 fn parent_element(&self) -> Option<Self> {
607 self.0.parent().map(|n| n.into())
608 }
609
610 fn parent_node_is_shadow_root(&self) -> bool {
612 false
614 }
615
616 fn containing_shadow_host(&self) -> Option<Self> {
618 None
620 }
621
622 fn is_pseudo_element(&self) -> bool {
624 false
626 }
627
628 fn prev_sibling_element(&self) -> Option<Self> {
630 let mut sibling = self.0.previous_sibling();
631
632 while let Some(ref sib) = sibling {
633 if sib.is_element() {
634 return sibling.map(|n| n.into());
635 }
636
637 sibling = sib.previous_sibling();
638 }
639
640 None
641 }
642
643 fn next_sibling_element(&self) -> Option<Self> {
645 let mut sibling = self.0.next_sibling();
646
647 while let Some(ref sib) = sibling {
648 if sib.is_element() {
649 return sibling.map(|n| n.into());
650 }
651
652 sibling = sib.next_sibling();
653 }
654
655 None
656 }
657
658 fn is_html_element_in_html_document(&self) -> bool {
659 false
660 }
661
662 fn has_local_name(&self, local_name: &LocalName) -> bool {
663 self.0.borrow_element().element_name().local == local_name.0
664 }
665
666 fn has_namespace(&self, ns: &Namespace) -> bool {
668 self.0.borrow_element().element_name().ns == *ns
669 }
670
671 fn is_same_type(&self, other: &Self) -> bool {
673 self.0.borrow_element().element_name() == other.0.borrow_element().element_name()
674 }
675
676 fn attr_matches(
677 &self,
678 ns: &NamespaceConstraint<&Namespace>,
679 local_name: &LocalName,
680 operation: &AttrSelectorOperation<&AttributeValue>,
681 ) -> bool {
682 self.0
683 .borrow_element()
684 .get_attributes()
685 .iter()
686 .find(|(attr, _)| {
687 match *ns {
689 NamespaceConstraint::Any => local_name.0 == attr.local,
690 NamespaceConstraint::Specific(ns) => {
691 QualName::new(None, ns.clone(), local_name.0.clone()) == *attr
692 }
693 }
694 })
695 .map(|(_, value)| {
696 operation.eval_str(value)
698 })
699 .unwrap_or(false)
700 }
701
702 fn match_non_ts_pseudo_class(
703 &self,
704 pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
705 _context: &mut MatchingContext<'_, Self::Impl>,
706 ) -> bool
707where {
708 match pc {
709 NonTSPseudoClass::Link => self.is_link(),
710 NonTSPseudoClass::Visited => false,
711 NonTSPseudoClass::Lang(css_lang) => self
712 .0
713 .borrow_element()
714 .get_computed_values()
715 .xml_lang()
716 .0
717 .as_ref()
718 .is_some_and(|e_lang| {
719 css_lang
720 .iter()
721 .any(|l| l.is_language_range() && l.matches(e_lang))
722 }),
723 }
724 }
725
726 fn match_pseudo_element(
727 &self,
728 _pe: &<Self::Impl as SelectorImpl>::PseudoElement,
729 _context: &mut MatchingContext<'_, Self::Impl>,
730 ) -> bool {
731 false
733 }
734
735 fn is_link(&self) -> bool {
737 self.0.is_element()
746 && match *self.0.borrow_element_data() {
747 crate::element::ElementData::Link(ref link) => link.link.is_some(),
748 _ => false,
749 }
750 }
751
752 fn is_html_slot_element(&self) -> bool {
754 false
755 }
756
757 fn has_id(&self, id: &Identifier, case_sensitivity: CaseSensitivity) -> bool {
758 self.0
759 .borrow_element()
760 .get_id()
761 .map(|self_id| case_sensitivity.eq(self_id.as_bytes(), id.0.as_bytes()))
762 .unwrap_or(false)
763 }
764
765 fn has_class(&self, name: &Identifier, case_sensitivity: CaseSensitivity) -> bool {
766 self.0
767 .borrow_element()
768 .get_class()
769 .map(|classes| {
770 classes
771 .split_whitespace()
772 .any(|class| case_sensitivity.eq(class.as_bytes(), name.0.as_bytes()))
773 })
774 .unwrap_or(false)
775 }
776
777 fn has_custom_state(&self, _name: &<Self::Impl as SelectorImpl>::Identifier) -> bool {
778 false
779 }
780
781 fn imported_part(&self, _name: &Identifier) -> Option<Identifier> {
782 None
784 }
785
786 fn is_part(&self, _name: &Identifier) -> bool {
787 false
789 }
790
791 fn is_empty(&self) -> bool {
796 self.0
798 .children()
799 .all(|child| child.is_chars() && child.borrow_chars().is_empty())
800 }
801
802 fn is_root(&self) -> bool {
808 self.0.parent().is_none()
809 }
810
811 fn add_element_unique_hashes(&self, _filter: &mut BloomFilter) -> bool {
812 false
813 }
814
815 fn first_element_child(&self) -> Option<Self> {
817 self.0
818 .children()
819 .find(|child| child.is_element())
820 .map(|n| n.into())
821 }
822
823 fn apply_selector_flags(&self, _: ElementSelectorFlags) {
825 todo!()
826 }
827}
828
829#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
835pub enum Origin {
836 UserAgent,
837 User,
838 Author,
839}
840
841pub struct Stylesheet {
843 origin: Origin,
844 qualified_rules: Vec<QualifiedRule>,
845}
846
847struct Match<'a> {
857 specificity: u32,
858 origin: Origin,
859 declaration: &'a Declaration,
860}
861
862impl<'a> Ord for Match<'a> {
863 fn cmp(&self, other: &Self) -> Ordering {
864 match self.origin.cmp(&other.origin) {
865 Ordering::Equal => self.specificity.cmp(&other.specificity),
866 o => o,
867 }
868 }
869}
870
871impl<'a> PartialOrd for Match<'a> {
872 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
873 Some(self.cmp(other))
874 }
875}
876
877impl<'a> PartialEq for Match<'a> {
878 fn eq(&self, other: &Self) -> bool {
879 self.origin == other.origin && self.specificity == other.specificity
880 }
881}
882
883impl<'a> Eq for Match<'a> {}
884
885impl Stylesheet {
886 fn empty(origin: Origin) -> Stylesheet {
887 Stylesheet {
888 origin,
889 qualified_rules: Vec::new(),
890 }
891 }
892
893 pub fn from_data(
898 buf: &str,
899 url_resolver: &UrlResolver,
900 origin: Origin,
901 load_limiter: LoadingDepthLimiter,
902 session: Session,
903 ) -> Result<Self, LoadingError> {
904 let mut stylesheet = Stylesheet::empty(origin);
905 stylesheet.add_rules_from_string(buf, url_resolver, load_limiter, session)?;
906 Ok(stylesheet)
907 }
908
909 pub fn from_href(
911 aurl: &AllowedUrl,
912 origin: Origin,
913 load_limiter: LoadingDepthLimiter,
914 session: Session,
915 ) -> Result<Self, LoadingError> {
916 let mut stylesheet = Stylesheet::empty(origin);
917 stylesheet.load(aurl, load_limiter, session)?;
918 Ok(stylesheet)
919 }
920
921 fn add_rules_from_string(
929 &mut self,
930 buf: &str,
931 url_resolver: &UrlResolver,
932 load_limiter: LoadingDepthLimiter,
933 session: Session,
934 ) -> Result<(), LoadingError> {
935 let mut input = ParserInput::new(buf);
936 let mut parser = Parser::new(&mut input);
937 let mut rule_parser = RuleParser {
938 session: session.clone(),
939 };
940
941 StyleSheetParser::new(&mut parser, &mut rule_parser)
942 .filter_map(|r| match r {
943 Ok(rule) => Some(rule),
944 Err(e) => {
945 rsvg_log!(session, "Invalid rule; ignoring: {:?}", e);
946 None
947 }
948 })
949 .for_each(|rule| match rule {
950 Rule::AtRule(AtRule::Import(url)) => match url_resolver.resolve_href(&url) {
951 Ok(aurl) => {
952 if let Err(e) = self.load(&aurl, load_limiter.clone(), session.clone()) {
953 rsvg_log!(session, "Could not load stylesheet from \"{}\": {}", url, e);
954 }
955 }
956
957 Err(e) => {
958 rsvg_log!(session, "Not loading stylesheet from \"{}\": {}", url, e);
959 }
960 },
961
962 Rule::QualifiedRule(qr) => self.qualified_rules.push(qr),
963 });
964
965 Ok(())
966 }
967
968 fn load(
970 &mut self,
971 aurl: &AllowedUrl,
972 load_limiter: LoadingDepthLimiter,
973 session: Session,
974 ) -> Result<(), LoadingError> {
975 load_limiter.increment()?;
976
977 let res = io::acquire_data(aurl, None)
978 .map_err(LoadingError::from)
979 .and_then(|data| {
980 String::from_utf8(data.data).map_err(|_| {
981 rsvg_log!(
982 session,
983 "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
984 aurl
985 );
986 LoadingError::BadCss
987 })
988 })
989 .and_then(|utf8| {
990 let url = (**aurl).clone();
991 self.add_rules_from_string(
992 &utf8,
993 &UrlResolver::new(Some(url)),
994 load_limiter.clone(),
995 session,
996 )
997 });
998
999 load_limiter.decrement();
1000
1001 res
1002 }
1003
1004 fn get_matches<'a>(
1006 &'a self,
1007 node: &Node,
1008 match_ctx: &mut MatchingContext<'_, Selector>,
1009 acc: &mut Vec<Match<'a>>,
1010 ) {
1011 for rule in &self.qualified_rules {
1012 for selector in rule.selectors.slice() {
1013 let matches = selectors::matching::matches_selector(
1015 selector,
1016 0,
1017 None,
1018 &RsvgElement(node.clone()),
1019 match_ctx,
1020 );
1021
1022 if matches {
1023 for decl in rule.declarations.iter() {
1024 acc.push(Match {
1025 declaration: decl,
1026 specificity: selector.specificity(),
1027 origin: self.origin,
1028 });
1029 }
1030 }
1031 }
1032 }
1033 }
1034}
1035
1036pub fn cascade(
1038 root: &mut Node,
1039 ua_stylesheets: &[Stylesheet],
1040 author_stylesheets: &[Stylesheet],
1041 user_stylesheets: &[Stylesheet],
1042 session: &Session,
1043) {
1044 for mut node in root.descendants().filter(|n| n.is_element()) {
1045 let mut matches = Vec::new();
1046
1047 let parent = node.parent().clone();
1051 node.borrow_element_mut().inherit_xml_lang(parent);
1052
1053 let mut caches = SelectorCaches::default();
1054 let mut match_ctx = MatchingContext::new(
1055 MatchingMode::Normal,
1056 None,
1058 &mut caches,
1059 QuirksMode::NoQuirks,
1060 NeedsSelectorFlags::No,
1061 MatchingForInvalidation::No,
1062 );
1063
1064 for s in ua_stylesheets
1065 .iter()
1066 .chain(author_stylesheets)
1067 .chain(user_stylesheets)
1068 {
1069 s.get_matches(&node, &mut match_ctx, &mut matches);
1070 }
1071
1072 matches.as_mut_slice().sort();
1073
1074 let mut element = node.borrow_element_mut();
1075
1076 for m in matches {
1077 element.apply_style_declaration(m.declaration, m.origin);
1078 }
1079
1080 element.set_style_attribute(session);
1081 }
1082
1083 let values = ComputedValues::default();
1084 root.cascade(&values);
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 use super::*;
1090 use selectors::Element;
1091
1092 use crate::document::Document;
1093 use crate::is_element_of_type;
1094
1095 #[test]
1096 fn xml_lang() {
1097 let document = Document::load_from_bytes(
1098 br#"<?xml version="1.0" encoding="UTF-8"?>
1099<svg xmlns="http://www.w3.org/2000/svg" xml:lang="zh">
1100 <text id="a" x="10" y="10" width="30" height="30"></text>
1101 <text id="b" x="10" y="20" width="30" height="30" xml:lang="en"></text>
1102</svg>
1103"#,
1104 );
1105 let a = document.lookup_internal_node("a").unwrap();
1106 assert_eq!(
1107 a.borrow_element()
1108 .get_computed_values()
1109 .xml_lang()
1110 .0
1111 .unwrap()
1112 .as_str(),
1113 "zh"
1114 );
1115 let b = document.lookup_internal_node("b").unwrap();
1116 assert_eq!(
1117 b.borrow_element()
1118 .get_computed_values()
1119 .xml_lang()
1120 .0
1121 .unwrap()
1122 .as_str(),
1123 "en"
1124 );
1125 }
1126
1127 #[test]
1128 fn impl_element() {
1129 let document = Document::load_from_bytes(
1130 br#"<?xml version="1.0" encoding="UTF-8"?>
1131<svg xmlns="http://www.w3.org/2000/svg" id="a">
1132 <rect id="b" x="10" y="10" width="30" height="30"/>
1133 <circle id="c" cx="10" cy="10" r="10"/>
1134 <rect id="d" class="foo bar"/>
1135</svg>
1136"#,
1137 );
1138
1139 let a = document.lookup_internal_node("a").unwrap();
1140 let b = document.lookup_internal_node("b").unwrap();
1141 let c = document.lookup_internal_node("c").unwrap();
1142 let d = document.lookup_internal_node("d").unwrap();
1143
1144 assert!(is_element_of_type!(a, Svg));
1146 assert!(is_element_of_type!(b, Rect));
1147 assert!(is_element_of_type!(c, Circle));
1148 assert!(is_element_of_type!(d, Rect));
1149
1150 let a = RsvgElement(a);
1151 let b = RsvgElement(b);
1152 let c = RsvgElement(c);
1153 let d = RsvgElement(d);
1154
1155 assert_eq!(a.parent_element(), None);
1158 assert_eq!(b.parent_element(), Some(a.clone()));
1159 assert_eq!(c.parent_element(), Some(a.clone()));
1160 assert_eq!(d.parent_element(), Some(a.clone()));
1161
1162 assert_eq!(b.next_sibling_element(), Some(c.clone()));
1163 assert_eq!(c.next_sibling_element(), Some(d.clone()));
1164 assert_eq!(d.next_sibling_element(), None);
1165
1166 assert_eq!(b.prev_sibling_element(), None);
1167 assert_eq!(c.prev_sibling_element(), Some(b.clone()));
1168 assert_eq!(d.prev_sibling_element(), Some(c.clone()));
1169
1170 assert!(a.has_local_name(&LocalName::from("svg")));
1173
1174 assert!(a.has_namespace(&ns!(svg)));
1175
1176 assert!(!a.is_same_type(&b));
1177 assert!(b.is_same_type(&d));
1178
1179 assert!(a.has_id(
1180 &Identifier::from("a"),
1181 CaseSensitivity::AsciiCaseInsensitive
1182 ));
1183 assert!(!b.has_id(
1184 &Identifier::from("foo"),
1185 CaseSensitivity::AsciiCaseInsensitive
1186 ));
1187
1188 assert!(d.has_class(
1189 &Identifier::from("foo"),
1190 CaseSensitivity::AsciiCaseInsensitive
1191 ));
1192 assert!(d.has_class(
1193 &Identifier::from("bar"),
1194 CaseSensitivity::AsciiCaseInsensitive
1195 ));
1196
1197 assert!(!a.has_class(
1198 &Identifier::from("foo"),
1199 CaseSensitivity::AsciiCaseInsensitive
1200 ));
1201
1202 assert!(d.is_empty());
1203 assert!(!a.is_empty());
1204 }
1205}