rsvg/
css.rs

1//! Representation of CSS types, and the CSS parsing and matching engine.
2//!
3//! # Terminology
4//!
5//! Consider a CSS **stylesheet** like this:
6//!
7//! ```css
8//! @import url("another.css");
9//!
10//! foo, .bar {
11//!         fill: red;
12//!         stroke: green;
13//! }
14//!
15//! #baz { stroke-width: 42; }
16//! ```
17//! The example contains three **rules**, the first one is an **at-rule*,
18//! the other two are **qualified rules**.
19//!
20//! Each rule is made of two parts, a **prelude** and an optional **block**
21//! The prelude is the part until the first `{` or until `;`, depending on
22//! whether a block is present.  The block is the part between curly braces.
23//!
24//! Let's look at each rule:
25//!
26//! `@import` is an **at-rule**.  This rule has a prelude, but no block.
27//! There are other at-rules like `@media` and some of them may have a block,
28//! but librsvg doesn't support those yet.
29//!
30//! The prelude of the following rule is `foo, .bar`.
31//! It is a **selector list** with two **selectors**, one for
32//! `foo` elements and one for elements that have the `bar` class.
33//!
34//! The content of the block between `{}` for a qualified rule is a
35//! **declaration list**.  The block of the first qualified rule contains two
36//! **declarations**, one for the `fill` **property** and one for the
37//! `stroke` property.
38//!
39//! After the first qualified rule, we have a second qualified rule with
40//! a single selector for the `#baz` id, with a single declaration for the
41//! `stroke-width` property.
42//!
43//! # Helper crates we use
44//!
45//! * `cssparser` crate as a CSS tokenizer, and some utilities to
46//!   parse CSS rules and declarations.
47//!
48//! * `selectors` crate for the representation of selectors and
49//!   selector lists, and for the matching engine.
50//!
51//! Both crates provide very generic implementations of their concepts,
52//! and expect the caller to provide implementations of various traits,
53//! and to provide types that represent certain things.
54//!
55//! For example, `cssparser` expects one to provide representations of
56//! the following types:
57//!
58//! * A parsed CSS rule.  For `fill: blue;` we have
59//!   `ParsedProperty::Fill(...)`.
60//!
61//! * A parsed selector list; we use `SelectorList` from the
62//!   `selectors` crate.
63//!
64//! In turn, the `selectors` crate needs a way to navigate and examine
65//! one's implementation of an element tree.  We provide `impl
66//! selectors::Element for RsvgElement` for this.  This implementation
67//! has methods like "does this element have the id `#foo`", or "give
68//! me the next sibling element".
69//!
70//! Finally, the matching engine ties all of this together with
71//! `matches_selector()`.  This takes an opaque representation of an
72//! element, plus a selector, and returns a bool.  We iterate through
73//! the rules in the stylesheets and gather the matches; then sort the
74//! matches by specificity and apply the result to each element.
75
76use 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
108/// A parsed CSS declaration
109///
110/// For example, in the declaration `fill: green !important`, the
111/// `prop_name` would be `fill`, the `property` would be
112/// `ParsedProperty::Fill(...)` with the green value, and `important`
113/// would be `true`.
114pub struct Declaration {
115    pub prop_name: QualName,
116    pub property: ParsedProperty,
117    pub important: bool,
118}
119
120/// This enum represents the fact that a rule body can be either a
121/// declaration or a nested rule.
122pub enum RuleBodyItem {
123    Decl(Declaration),
124    #[allow(dead_code)] // We don't support nested rules yet
125    Rule(Rule),
126}
127
128/// Dummy struct required to use `cssparser::DeclarationListParser`
129///
130/// It implements `cssparser::DeclarationParser`, which knows how to parse
131/// the property/value pairs from a CSS declaration.
132pub struct DeclParser;
133
134impl<'i> DeclarationParser<'i> for DeclParser {
135    type Declaration = RuleBodyItem;
136    type Error = ValueErrorKind;
137
138    /// Parses a CSS declaration like `name: input_value [!important]`
139    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
158// cssparser's DeclarationListParser requires this; we just use the dummy
159// implementations from cssparser itself.  We may want to provide a real
160// implementation in the future, although this may require keeping track of the
161// CSS parsing state like Servo does.
162impl<'i> AtRuleParser<'i> for DeclParser {
163    type Prelude = ();
164    type AtRule = RuleBodyItem;
165    type Error = ValueErrorKind;
166}
167
168/// We need this dummy implementation as well.
169impl<'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    /// We want to parse declarations.
177    fn parse_declarations(&self) -> bool {
178        true
179    }
180
181    /// We don't wanto parse qualified rules though.
182    fn parse_qualified(&self) -> bool {
183        false
184    }
185}
186
187/// Struct to implement cssparser::QualifiedRuleParser and cssparser::AtRuleParser
188pub struct RuleParser {
189    session: Session,
190}
191
192/// Errors from the CSS parsing process
193#[allow(dead_code)] // looks like we are not actually using this yet?
194#[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
205/// A CSS qualified rule (or ruleset)
206pub struct QualifiedRule {
207    selectors: SelectorList<Selector>,
208    declarations: Vec<Declaration>,
209}
210
211/// Prelude of at-rule used in the AtRuleParser.
212pub enum AtRulePrelude {
213    Import(String),
214}
215
216/// A CSS at-rule (or ruleset)
217pub enum AtRule {
218    Import(String),
219}
220
221/// A CSS rule (or ruleset)
222pub enum Rule {
223    AtRule(AtRule),
224    QualifiedRule(QualifiedRule),
225}
226
227// Required to implement the `Prelude` associated type in `cssparser::QualifiedRuleParser`
228impl<'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        // FIXME: Do we need to keep a lookup table extracted from libxml2's
241        // XML namespaces?
242        //
243        // Or are CSS namespaces completely different, declared elsewhere?
244        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                // Comma-separated lists of languages are a Selectors 4 feature,
268                // but a pretty stable one that hasn't changed in a long time.
269                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
285// `cssparser::StyleSheetParser` is a struct which requires that we provide a type that
286// implements `cssparser::QualifiedRuleParser` and `cssparser::AtRuleParser`.
287//
288// In turn, `cssparser::QualifiedRuleParser` requires that we
289// implement a way to parse the `Prelude` of a ruleset or rule.  For
290// example, in this ruleset:
291//
292// ```css
293// foo, .bar { fill: red; stroke: green; }
294// ```
295//
296// The prelude is the selector list with the `foo` and `.bar` selectors.
297//
298// The `parse_prelude` method just uses `selectors::SelectorList`.  This
299// is what requires the `impl selectors::Parser for RuleParser`.
300//
301// Next, the `parse_block` method takes an already-parsed prelude (a selector list),
302// and tries to parse the block between braces.  It creates a `Rule` out of
303// the selector list and the declaration list.
304impl<'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
345// Required by `cssparser::StyleSheetParser`.
346//
347// This only handles the `@import` at-rule.
348impl<'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            // FIXME: at the moment we ignore media queries
363
364            "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    // When we implement at-rules with blocks, implement the trait's parse_block() method here.
383}
384
385/// Dummy type required by the SelectorImpl trait.
386#[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/// Dummy type required by the SelectorImpl trait
427#[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/// Holds all the types for the SelectorImpl trait
444#[derive(Debug, Clone)]
445pub struct Selector;
446
447/// Wrapper for attribute values.
448///
449/// We use a newtype because the associated type Selector::AttrValue
450/// must implement `From<&str>` and `ToCss`, which are foreign traits.
451///
452/// The `derive` requirements come from the `selectors` crate.
453#[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/// Wrapper for identifier values.
480///
481/// Used to implement `ToCss` on the `LocalName` foreign type.
482#[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/// Wrapper for local names.
507///
508/// Used to implement `ToCss` on the `LocalName` foreign type.
509#[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/// Wrapper for namespace prefixes.
534///
535/// Used to implement `ToCss` on the `markup5ever::Prefix` foreign type.
536#[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/// Newtype wrapper around `Node` so we can implement [`selectors::Element`] for it.
568///
569/// `Node` is an alias for [`rctree::Node`], so we can't implement
570/// `selectors::Element` directly on it.  We implement it on the
571/// `RsvgElement` wrapper instead.
572#[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
587// The selectors crate uses this to examine our tree of elements.
588impl selectors::Element for RsvgElement {
589    type Impl = Selector;
590
591    /// Converts self into an opaque representation.
592    fn opaque(&self) -> OpaqueElement {
593        // The `selectors` crate uses this value just for pointer comparisons, to answer
594        // the question, "is this element the same as that one?".  So, we'll give it a
595        // reference to our actual node's data, i.e. skip over whatever wrappers there
596        // are in rctree.
597        //
598        // We use an explicit type here to make it clear what the type is; otherwise you
599        // may be fooled by the fact that borrow_element() returns a Ref<Element>, not a
600        // plain reference: &Ref<T> is transient and would get dropped at the end of this
601        // function, but we want something long-lived.
602        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    /// Whether the parent node of this element is a shadow root.
611    fn parent_node_is_shadow_root(&self) -> bool {
612        // unsupported
613        false
614    }
615
616    /// The host of the containing shadow root, if any.
617    fn containing_shadow_host(&self) -> Option<Self> {
618        // unsupported
619        None
620    }
621
622    /// Whether we're matching on a pseudo-element.
623    fn is_pseudo_element(&self) -> bool {
624        // unsupported
625        false
626    }
627
628    /// Skips non-element nodes
629    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    /// Skips non-element nodes
644    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    /// Empty string for no namespace
667    fn has_namespace(&self, ns: &Namespace) -> bool {
668        self.0.borrow_element().element_name().ns == *ns
669    }
670
671    /// Whether this element and the `other` element have the same local name and namespace.
672    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                // do we have an attribute that matches the namespace and local_name?
688                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                // we have one; does the attribute's value match the expected operation?
697                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        // unsupported
732        false
733    }
734
735    /// Whether this element is a `link`.
736    fn is_link(&self) -> bool {
737        // Style as link only if href is specified at all.
738        //
739        // The SVG and CSS specifications do not seem to clearly
740        // say what happens when you have an `<svg:a>` tag with no
741        // `(xlink:|svg:)href` attribute. However, both Firefox and Chromium
742        // consider a bare `<svg:a>` element with no href to be NOT
743        // a link, so to avoid nasty surprises, we do the same.
744        // Empty href's, however, ARE considered links.
745        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    /// Returns whether the element is an HTML `<slot>` element.
753    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        // unsupported
783        None
784    }
785
786    fn is_part(&self, _name: &Identifier) -> bool {
787        // unsupported
788        false
789    }
790
791    /// Returns whether this element matches `:empty`.
792    ///
793    /// That is, whether it does not contain any child element or any non-zero-length text node.
794    /// See <http://dev.w3.org/csswg/selectors-3/#empty-pseudo>.
795    fn is_empty(&self) -> bool {
796        // .all() returns true for the empty iterator
797        self.0
798            .children()
799            .all(|child| child.is_chars() && child.borrow_chars().is_empty())
800    }
801
802    /// Returns whether this element matches `:root`,
803    /// i.e. whether it is the root element of a document.
804    ///
805    /// Note: this can be false even if `.parent_element()` is `None`
806    /// if the parent node is a `DocumentFragment`.
807    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    /// Returns the first child element of this element.
816    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    /// Applies the given selector flags to this element.
824    fn apply_selector_flags(&self, _: ElementSelectorFlags) {
825        todo!()
826    }
827}
828
829/// Origin for a stylesheet, per CSS 2.2.
830///
831/// This is used when sorting selector matches according to their origin and specificity.
832///
833/// CSS2.2: <https://www.w3.org/TR/CSS22/cascade.html#cascading-order>
834#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
835pub enum Origin {
836    UserAgent,
837    User,
838    Author,
839}
840
841/// A parsed CSS stylesheet.
842pub struct Stylesheet {
843    origin: Origin,
844    qualified_rules: Vec<QualifiedRule>,
845}
846
847/// A match during the selector matching process
848///
849/// This struct comes from [`Stylesheet::get_matches`], and represents
850/// that a certain node matched a CSS rule which has a selector with a
851/// certain `specificity`.  The stylesheet's `origin` is also given here.
852///
853/// This type implements [`Ord`] so a list of `Match` can be sorted.
854/// That implementation does ordering based on origin and specificity
855/// as per <https://www.w3.org/TR/CSS22/cascade.html#cascading-order>.
856struct 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    /// Parses a new stylesheet from CSS data in a string.
894    ///
895    /// The `url_resolver_url` is required for `@import` rules, so that librsvg can determine if
896    /// the requested path is allowed.
897    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    /// Parses a new stylesheet by loading CSS data from a URL.
910    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    /// Parses the CSS rules in `buf` and appends them to the stylesheet.
922    ///
923    /// The `url_resolver_url` is required for `@import` rules, so that librsvg can determine if
924    /// the requested path is allowed.
925    ///
926    /// If there is an `@import` rule, its rules will be recursively added into the
927    /// stylesheet, in the order in which they appear.
928    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    /// Parses a stylesheet referenced by an URL
969    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    /// Appends the style declarations that match a specified node to a given vector
1005    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                // This magic call is stolen from selectors::matching::matches_selector_list()
1014                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
1036/// Runs the CSS cascade on the specified tree from all the stylesheets
1037pub 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        // xml:lang needs to be inherited before selector matching, so it
1048        // can't be done in the usual SpecifiedValues::to_computed_values,
1049        // which is called by cascade() and runs after matching.
1050        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            // FIXME: how the fuck does one set up a bloom filter here?
1057            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        // Node types
1145        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        // Tree navigation
1156
1157        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        // Other operations
1171
1172        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}