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

            
76
use cssparser::{
77
    self, match_ignore_ascii_case, parse_important, AtRuleParser, BasicParseErrorKind, CowRcStr,
78
    DeclarationParser, Parser, ParserInput, ParserState, QualifiedRuleParser, RuleBodyItemParser,
79
    RuleBodyParser, SourceLocation, StyleSheetParser, ToCss,
80
};
81
use data_url::mime::Mime;
82
use language_tags::LanguageTag;
83
use markup5ever::{self, namespace_url, ns, Namespace, QualName};
84
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
85
use selectors::matching::{
86
    ElementSelectorFlags, IgnoreNthChildForInvalidation, MatchingContext, MatchingMode,
87
    NeedsSelectorFlags, QuirksMode,
88
};
89
use selectors::parser::ParseRelative;
90
use selectors::{NthIndexCache, OpaqueElement, SelectorImpl, SelectorList};
91
use std::cmp::Ordering;
92
use std::fmt;
93
use std::str;
94
use std::str::FromStr;
95

            
96
use crate::element::Element;
97
use crate::error::*;
98
use crate::io::{self, BinaryData};
99
use crate::node::{Node, NodeBorrow, NodeCascade};
100
use crate::properties::{parse_value, ComputedValues, ParseAs, ParsedProperty};
101
use crate::rsvg_log;
102
use crate::session::Session;
103
use crate::url_resolver::{AllowedUrl, UrlResolver};
104

            
105
/// A parsed CSS declaration
106
///
107
/// For example, in the declaration `fill: green !important`, the
108
/// `prop_name` would be `fill`, the `property` would be
109
/// `ParsedProperty::Fill(...)` with the green value, and `important`
110
/// would be `true`.
111
pub struct Declaration {
112
    pub prop_name: QualName,
113
    pub property: ParsedProperty,
114
    pub important: bool,
115
}
116

            
117
/// This enum represents the fact that a rule body can be either a
118
/// declaration or a nested rule.
119
pub enum RuleBodyItem {
120
    Decl(Declaration),
121
    #[allow(dead_code)] // We don't support nested rules yet
122
    Rule(Rule),
123
}
124

            
125
/// Dummy struct required to use `cssparser::DeclarationListParser`
126
///
127
/// It implements `cssparser::DeclarationParser`, which knows how to parse
128
/// the property/value pairs from a CSS declaration.
129
pub struct DeclParser;
130

            
131
impl<'i> DeclarationParser<'i> for DeclParser {
132
    type Declaration = RuleBodyItem;
133
    type Error = ValueErrorKind;
134

            
135
    /// Parses a CSS declaration like `name: input_value [!important]`
136
40072
    fn parse_value<'t>(
137
        &mut self,
138
        name: CowRcStr<'i>,
139
        input: &mut Parser<'i, 't>,
140
    ) -> Result<RuleBodyItem, cssparser::ParseError<'i, Self::Error>> {
141
40072
        let prop_name = QualName::new(None, ns!(), markup5ever::LocalName::from(name.as_ref()));
142
40072
        let property = parse_value(&prop_name, input, ParseAs::Property)?;
143

            
144
34680
        let important = input.try_parse(parse_important).is_ok();
145

            
146
34646
        Ok(RuleBodyItem::Decl(Declaration {
147
34646
            prop_name,
148
34646
            property,
149
            important,
150
        }))
151
39930
    }
152
}
153

            
154
// cssparser's DeclarationListParser requires this; we just use the dummy
155
// implementations from cssparser itself.  We may want to provide a real
156
// implementation in the future, although this may require keeping track of the
157
// CSS parsing state like Servo does.
158
impl<'i> AtRuleParser<'i> for DeclParser {
159
    type Prelude = ();
160
    type AtRule = RuleBodyItem;
161
    type Error = ValueErrorKind;
162
}
163

            
164
/// We need this dummy implementation as well.
165
impl<'i> QualifiedRuleParser<'i> for DeclParser {
166
    type Prelude = ();
167
    type QualifiedRule = RuleBodyItem;
168
    type Error = ValueErrorKind;
169
}
170

            
171
impl<'i> RuleBodyItemParser<'i, RuleBodyItem, ValueErrorKind> for DeclParser {
172
    /// We want to parse declarations.
173
39899
    fn parse_declarations(&self) -> bool {
174
        true
175
39899
    }
176

            
177
    /// We don't wanto parse qualified rules though.
178
5283
    fn parse_qualified(&self) -> bool {
179
        false
180
5283
    }
181
}
182

            
183
/// Struct to implement cssparser::QualifiedRuleParser and cssparser::AtRuleParser
184
pub struct RuleParser {
185
    session: Session,
186
}
187

            
188
/// Errors from the CSS parsing process
189
#[derive(Debug)]
190
pub enum ParseErrorKind<'i> {
191
    Selector(selectors::parser::SelectorParseErrorKind<'i>),
192
}
193

            
194
impl<'i> From<selectors::parser::SelectorParseErrorKind<'i>> for ParseErrorKind<'i> {
195
    fn from(e: selectors::parser::SelectorParseErrorKind<'_>) -> ParseErrorKind<'_> {
196
        ParseErrorKind::Selector(e)
197
    }
198
}
199

            
200
/// A CSS qualified rule (or ruleset)
201
pub struct QualifiedRule {
202
    selectors: SelectorList<Selector>,
203
    declarations: Vec<Declaration>,
204
}
205

            
206
/// Prelude of at-rule used in the AtRuleParser.
207
pub enum AtRulePrelude {
208
    Import(String),
209
}
210

            
211
/// A CSS at-rule (or ruleset)
212
pub enum AtRule {
213
    Import(String),
214
}
215

            
216
/// A CSS rule (or ruleset)
217
pub enum Rule {
218
    AtRule(AtRule),
219
    QualifiedRule(QualifiedRule),
220
}
221

            
222
// Required to implement the `Prelude` associated type in `cssparser::QualifiedRuleParser`
223
impl<'i> selectors::Parser<'i> for RuleParser {
224
    type Impl = Selector;
225
    type Error = ParseErrorKind<'i>;
226

            
227
2693
    fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
228
2693
        Some(ns!(svg))
229
2693
    }
230

            
231
    fn namespace_for_prefix(
232
        &self,
233
        _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix,
234
    ) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
235
        // FIXME: Do we need to keep a lookup table extracted from libxml2's
236
        // XML namespaces?
237
        //
238
        // Or are CSS namespaces completely different, declared elsewhere?
239
        None
240
    }
241
2
    fn parse_non_ts_pseudo_class(
242
        &self,
243
        location: SourceLocation,
244
        name: CowRcStr<'i>,
245
    ) -> Result<NonTSPseudoClass, cssparser::ParseError<'i, Self::Error>> {
246
2
        match &*name {
247
2
            "link" => Ok(NonTSPseudoClass::Link),
248
            "visited" => Ok(NonTSPseudoClass::Visited),
249
            _ => Err(location.new_custom_error(
250
                selectors::parser::SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
251
            )),
252
        }
253
2
    }
254
6
    fn parse_non_ts_functional_pseudo_class(
255
        &self,
256
        name: CowRcStr<'i>,
257
        arguments: &mut Parser<'i, '_>,
258
    ) -> Result<NonTSPseudoClass, cssparser::ParseError<'i, Self::Error>> {
259
6
        match &*name {
260
6
            "lang" => {
261
                // Comma-separated lists of languages are a Selectors 4 feature,
262
                // but a pretty stable one that hasn't changed in a long time.
263
13
                let tags = arguments.parse_comma_separated(|arg| {
264
7
                    let language_tag = arg.expect_ident_or_string()?.clone();
265
7
                    LanguageTag::from_str(&language_tag).map_err(|_| {
266
                        arg.new_custom_error(selectors::parser::SelectorParseErrorKind::UnsupportedPseudoClassOrElement(language_tag))
267
                    })
268
7
                })?;
269
12
                arguments.expect_exhausted()?;
270
6
                Ok(NonTSPseudoClass::Lang(tags))
271
6
            }
272
            _ => Err(arguments.new_custom_error(
273
                selectors::parser::SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
274
            )),
275
        }
276
6
    }
277
}
278

            
279
// `cssparser::StyleSheetParser` is a struct which requires that we provide a type that
280
// implements `cssparser::QualifiedRuleParser` and `cssparser::AtRuleParser`.
281
//
282
// In turn, `cssparser::QualifiedRuleParser` requires that we
283
// implement a way to parse the `Prelude` of a ruleset or rule.  For
284
// example, in this ruleset:
285
//
286
// ```css
287
// foo, .bar { fill: red; stroke: green; }
288
// ```
289
//
290
// The prelude is the selector list with the `foo` and `.bar` selectors.
291
//
292
// The `parse_prelude` method just uses `selectors::SelectorList`.  This
293
// is what requires the `impl selectors::Parser for RuleParser`.
294
//
295
// Next, the `parse_block` method takes an already-parsed prelude (a selector list),
296
// and tries to parse the block between braces.  It creates a `Rule` out of
297
// the selector list and the declaration list.
298
impl<'i> QualifiedRuleParser<'i> for RuleParser {
299
    type Prelude = SelectorList<Selector>;
300
    type QualifiedRule = Rule;
301
    type Error = ValueErrorKind;
302

            
303
494
    fn parse_prelude<'t>(
304
        &mut self,
305
        input: &mut Parser<'i, 't>,
306
    ) -> Result<Self::Prelude, cssparser::ParseError<'i, Self::Error>> {
307
494
        SelectorList::parse(self, input, ParseRelative::No).map_err(|e| ParseError {
308
            kind: cssparser::ParseErrorKind::Custom(ValueErrorKind::parse_error(
309
                "Could not parse selector",
310
            )),
311
            location: e.location,
312
        })
313
494
    }
314

            
315
494
    fn parse_block<'t>(
316
        &mut self,
317
        prelude: Self::Prelude,
318
        _start: &ParserState,
319
        input: &mut Parser<'i, 't>,
320
    ) -> Result<Self::QualifiedRule, cssparser::ParseError<'i, Self::Error>> {
321
494
        let declarations = RuleBodyParser::<_, _, Self::Error>::new(input, &mut DeclParser)
322
1040
            .filter_map(|r| match r {
323
545
                Ok(RuleBodyItem::Decl(decl)) => Some(decl),
324
                Ok(RuleBodyItem::Rule(_)) => None,
325
1
                Err(e) => {
326
1
                    rsvg_log!(self.session, "Invalid declaration; ignoring: {:?}", e);
327
1
                    None
328
1
                }
329
546
            })
330
            .collect();
331

            
332
494
        Ok(Rule::QualifiedRule(QualifiedRule {
333
494
            selectors: prelude,
334
            declarations,
335
        }))
336
494
    }
337
}
338

            
339
// Required by `cssparser::StyleSheetParser`.
340
//
341
// This only handles the `@import` at-rule.
342
impl<'i> AtRuleParser<'i> for RuleParser {
343
    type Prelude = AtRulePrelude;
344
    type AtRule = Rule;
345
    type Error = ValueErrorKind;
346

            
347
    #[allow(clippy::type_complexity)]
348
4
    fn parse_prelude<'t>(
349
        &mut self,
350
        name: CowRcStr<'i>,
351
        input: &mut Parser<'i, 't>,
352
    ) -> Result<Self::Prelude, cssparser::ParseError<'i, Self::Error>> {
353
4
        match_ignore_ascii_case! {
354
            &name,
355

            
356
            // FIXME: at the moment we ignore media queries
357

            
358
            "import" => {
359
                let url = input.expect_url_or_string()?.as_ref().to_owned();
360
2
                Ok(AtRulePrelude::Import(url))
361
            },
362

            
363
            _ => Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))),
364
        }
365
4
    }
366

            
367
2
    fn rule_without_block(
368
        &mut self,
369
        prelude: Self::Prelude,
370
        _start: &ParserState,
371
    ) -> Result<Self::AtRule, ()> {
372
2
        let AtRulePrelude::Import(url) = prelude;
373
2
        Ok(Rule::AtRule(AtRule::Import(url)))
374
2
    }
375

            
376
    // When we implement at-rules with blocks, implement the trait's parse_block() method here.
377
}
378

            
379
/// Dummy type required by the SelectorImpl trait.
380
#[allow(clippy::upper_case_acronyms)]
381
#[derive(Clone, Debug, Eq, PartialEq)]
382
pub enum NonTSPseudoClass {
383
    Link,
384
    Visited,
385
    Lang(Vec<LanguageTag>),
386
}
387

            
388
impl ToCss for NonTSPseudoClass {
389
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
390
    where
391
        W: fmt::Write,
392
    {
393
        match self {
394
            NonTSPseudoClass::Link => write!(dest, "link"),
395
            NonTSPseudoClass::Visited => write!(dest, "visited"),
396
            NonTSPseudoClass::Lang(lang) => write!(
397
                dest,
398
                "lang(\"{}\")",
399
                lang.iter()
400
                    .map(ToString::to_string)
401
                    .collect::<Vec<_>>()
402
                    .join("\",\"")
403
            ),
404
        }
405
    }
406
}
407

            
408
impl selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
409
    type Impl = Selector;
410

            
411
    fn is_active_or_hover(&self) -> bool {
412
        false
413
    }
414

            
415
    fn is_user_action_state(&self) -> bool {
416
        false
417
    }
418
}
419

            
420
/// Dummy type required by the SelectorImpl trait
421
#[derive(Clone, Debug, Eq, PartialEq)]
422
pub struct PseudoElement;
423

            
424
impl ToCss for PseudoElement {
425
    fn to_css<W>(&self, _dest: &mut W) -> fmt::Result
426
    where
427
        W: fmt::Write,
428
    {
429
        Ok(())
430
    }
431
}
432

            
433
impl selectors::parser::PseudoElement for PseudoElement {
434
    type Impl = Selector;
435
}
436

            
437
/// Holds all the types for the SelectorImpl trait
438
#[derive(Debug, Clone)]
439
pub struct Selector;
440

            
441
/// Wrapper for attribute values.
442
///
443
/// We use a newtype because the associated type Selector::AttrValue
444
/// must implement `From<&str>` and `ToCss`, which are foreign traits.
445
///
446
/// The `derive` requirements come from the `selectors` crate.
447
#[derive(Clone, PartialEq, Eq)]
448
pub struct AttributeValue(String);
449

            
450
impl From<&str> for AttributeValue {
451
13
    fn from(s: &str) -> AttributeValue {
452
13
        AttributeValue(s.to_owned())
453
13
    }
454
}
455

            
456
impl ToCss for AttributeValue {
457
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
458
    where
459
        W: fmt::Write,
460
    {
461
        use std::fmt::Write;
462

            
463
        write!(cssparser::CssStringWriter::new(dest), "{}", &self.0)
464
    }
465
}
466

            
467
impl AsRef<str> for AttributeValue {
468
59
    fn as_ref(&self) -> &str {
469
59
        self.0.as_ref()
470
59
    }
471
}
472

            
473
/// Wrapper for identifier values.
474
///
475
/// Used to implement `ToCss` on the `LocalName` foreign type.
476
#[derive(Clone, PartialEq, Eq)]
477
pub struct Identifier(markup5ever::LocalName);
478

            
479
impl From<&str> for Identifier {
480
110
    fn from(s: &str) -> Identifier {
481
110
        Identifier(markup5ever::LocalName::from(s))
482
110
    }
483
}
484

            
485
impl ToCss for Identifier {
486
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
487
    where
488
        W: fmt::Write,
489
    {
490
        cssparser::serialize_identifier(&self.0, dest)
491
    }
492
}
493

            
494
/// Wrapper for local names.
495
///
496
/// Used to implement `ToCss` on the `LocalName` foreign type.
497
646270
#[derive(Clone, PartialEq, Eq)]
498
323135
pub struct LocalName(markup5ever::LocalName);
499

            
500
impl From<&str> for LocalName {
501
4741
    fn from(s: &str) -> LocalName {
502
4741
        LocalName(markup5ever::LocalName::from(s))
503
4741
    }
504
}
505

            
506
impl ToCss for LocalName {
507
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
508
    where
509
        W: fmt::Write,
510
    {
511
        cssparser::serialize_identifier(&self.0, dest)
512
    }
513
}
514

            
515
/// Wrapper for namespace prefixes.
516
///
517
/// Used to implement `ToCss` on the `markup5ever::Prefix` foreign type.
518
#[derive(Clone, Default, PartialEq, Eq)]
519
pub struct NamespacePrefix(markup5ever::Prefix);
520

            
521
impl From<&str> for NamespacePrefix {
522
    fn from(s: &str) -> NamespacePrefix {
523
        NamespacePrefix(markup5ever::Prefix::from(s))
524
    }
525
}
526

            
527
impl ToCss for NamespacePrefix {
528
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
529
    where
530
        W: fmt::Write,
531
    {
532
        cssparser::serialize_identifier(&self.0, dest)
533
    }
534
}
535

            
536
impl SelectorImpl for Selector {
537
    type ExtraMatchingData<'a> = ();
538
    type AttrValue = AttributeValue;
539
    type Identifier = Identifier;
540
    type LocalName = LocalName;
541
    type NamespaceUrl = Namespace;
542
    type NamespacePrefix = NamespacePrefix;
543
    type BorrowedNamespaceUrl = Namespace;
544
    type BorrowedLocalName = LocalName;
545
    type NonTSPseudoClass = NonTSPseudoClass;
546
    type PseudoElement = PseudoElement;
547
}
548

            
549
/// Newtype wrapper around `Node` so we can implement [`selectors::Element`] for it.
550
///
551
/// `Node` is an alias for [`rctree::Node`], so we can't implement
552
/// `selectors::Element` directly on it.  We implement it on the
553
/// `RsvgElement` wrapper instead.
554
1072
#[derive(Clone, PartialEq)]
555
536
pub struct RsvgElement(Node);
556

            
557
impl From<Node> for RsvgElement {
558
786
    fn from(n: Node) -> RsvgElement {
559
786
        RsvgElement(n)
560
786
    }
561
}
562

            
563
impl fmt::Debug for RsvgElement {
564
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
565
        write!(f, "{}", self.0.borrow())
566
    }
567
}
568

            
569
// The selectors crate uses this to examine our tree of elements.
570
impl selectors::Element for RsvgElement {
571
    type Impl = Selector;
572

            
573
    /// Converts self into an opaque representation.
574
19
    fn opaque(&self) -> OpaqueElement {
575
        // The `selectors` crate uses this value just for pointer comparisons, to answer
576
        // the question, "is this element the same as that one?".  So, we'll give it a
577
        // reference to our actual node's data, i.e. skip over whatever wrappers there
578
        // are in rctree.
579
        //
580
        // We use an explicit type here to make it clear what the type is; otherwise you
581
        // may be fooled by the fact that borrow_element() returns a Ref<Element>, not a
582
        // plain reference: &Ref<T> is transient and would get dropped at the end of this
583
        // function, but we want something long-lived.
584
19
        let element: &Element = &self.0.borrow_element();
585
19
        OpaqueElement::new::<Element>(element)
586
19
    }
587

            
588
789
    fn parent_element(&self) -> Option<Self> {
589
1457
        self.0.parent().map(|n| n.into())
590
789
    }
591

            
592
    /// Whether the parent node of this element is a shadow root.
593
120
    fn parent_node_is_shadow_root(&self) -> bool {
594
        // unsupported
595
        false
596
120
    }
597

            
598
    /// The host of the containing shadow root, if any.
599
    fn containing_shadow_host(&self) -> Option<Self> {
600
        // unsupported
601
        None
602
    }
603

            
604
    /// Whether we're matching on a pseudo-element.
605
    fn is_pseudo_element(&self) -> bool {
606
        // unsupported
607
        false
608
    }
609

            
610
    /// Skips non-element nodes
611
179
    fn prev_sibling_element(&self) -> Option<Self> {
612
179
        let mut sibling = self.0.previous_sibling();
613

            
614
357
        while let Some(ref sib) = sibling {
615
282
            if sib.is_element() {
616
208
                return sibling.map(|n| n.into());
617
            }
618

            
619
178
            sibling = sib.previous_sibling();
620
        }
621

            
622
75
        None
623
179
    }
624

            
625
    /// Skips non-element nodes
626
23
    fn next_sibling_element(&self) -> Option<Self> {
627
23
        let mut sibling = self.0.next_sibling();
628

            
629
46
        while let Some(ref sib) = sibling {
630
37
            if sib.is_element() {
631
28
                return sibling.map(|n| n.into());
632
            }
633

            
634
23
            sibling = sib.next_sibling();
635
        }
636

            
637
9
        None
638
23
    }
639

            
640
51285
    fn is_html_element_in_html_document(&self) -> bool {
641
        false
642
51285
    }
643

            
644
322440
    fn has_local_name(&self, local_name: &LocalName) -> bool {
645
322440
        self.0.borrow_element().element_name().local == local_name.0
646
322078
    }
647

            
648
    /// Empty string for no namespace
649
421543
    fn has_namespace(&self, ns: &Namespace) -> bool {
650
421543
        self.0.borrow_element().element_name().ns == *ns
651
420961
    }
652

            
653
    /// Whether this element and the `other` element have the same local name and namespace.
654
20
    fn is_same_type(&self, other: &Self) -> bool {
655
20
        self.0.borrow_element().element_name() == other.0.borrow_element().element_name()
656
20
    }
657

            
658
456
    fn attr_matches(
659
        &self,
660
        ns: &NamespaceConstraint<&Namespace>,
661
        local_name: &LocalName,
662
        operation: &AttrSelectorOperation<&AttributeValue>,
663
    ) -> bool {
664
456
        self.0
665
            .borrow_element()
666
            .get_attributes()
667
            .iter()
668
1859
            .find(|(attr, _)| {
669
                // do we have an attribute that matches the namespace and local_name?
670
1403
                match *ns {
671
                    NamespaceConstraint::Any => local_name.0 == attr.local,
672
1403
                    NamespaceConstraint::Specific(ns) => {
673
1403
                        QualName::new(None, ns.clone(), local_name.0.clone()) == *attr
674
1403
                    }
675
                }
676
1403
            })
677
525
            .map(|(_, value)| {
678
                // we have one; does the attribute's value match the expected operation?
679
69
                operation.eval_str(value)
680
69
            })
681
            .unwrap_or(false)
682
456
    }
683

            
684
54
    fn match_non_ts_pseudo_class(
685
        &self,
686
        pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
687
        _context: &mut MatchingContext<'_, Self::Impl>,
688
    ) -> bool
689
where {
690
54
        match pc {
691
3
            NonTSPseudoClass::Link => self.is_link(),
692
            NonTSPseudoClass::Visited => false,
693
51
            NonTSPseudoClass::Lang(css_lang) => self
694
                .0
695
                .borrow_element()
696
                .get_computed_values()
697
                .xml_lang()
698
                .0
699
                .as_ref()
700
86
                .map_or(false, |e_lang| {
701
70
                    css_lang
702
                        .iter()
703
74
                        .any(|l| l.is_language_range() && l.matches(e_lang))
704
86
                }),
705
        }
706
54
    }
707

            
708
    fn match_pseudo_element(
709
        &self,
710
        _pe: &<Self::Impl as SelectorImpl>::PseudoElement,
711
        _context: &mut MatchingContext<'_, Self::Impl>,
712
    ) -> bool {
713
        // unsupported
714
        false
715
    }
716

            
717
    /// Whether this element is a `link`.
718
876
    fn is_link(&self) -> bool {
719
        // Style as link only if href is specified at all.
720
        //
721
        // The SVG and CSS specifications do not seem to clearly
722
        // say what happens when you have an `<svg:a>` tag with no
723
        // `(xlink:|svg:)href` attribute. However, both Firefox and Chromium
724
        // consider a bare `<svg:a>` element with no href to be NOT
725
        // a link, so to avoid nasty surprises, we do the same.
726
        // Empty href's, however, ARE considered links.
727
876
        self.0.is_element()
728
876
            && match *self.0.borrow_element_data() {
729
3
                crate::element::ElementData::Link(ref link) => link.link.is_some(),
730
873
                _ => false,
731
876
            }
732
876
    }
733

            
734
    /// Returns whether the element is an HTML `<slot>` element.
735
    fn is_html_slot_element(&self) -> bool {
736
        false
737
    }
738

            
739
718
    fn has_id(&self, id: &Identifier, case_sensitivity: CaseSensitivity) -> bool {
740
718
        self.0
741
            .borrow_element()
742
            .get_id()
743
1130
            .map(|self_id| case_sensitivity.eq(self_id.as_bytes(), id.0.as_bytes()))
744
            .unwrap_or(false)
745
718
    }
746

            
747
925
    fn has_class(&self, name: &Identifier, case_sensitivity: CaseSensitivity) -> bool {
748
925
        self.0
749
            .borrow_element()
750
            .get_class()
751
1256
            .map(|classes| {
752
662
                classes
753
                    .split_whitespace()
754
766
                    .any(|class| case_sensitivity.eq(class.as_bytes(), name.0.as_bytes()))
755
331
            })
756
            .unwrap_or(false)
757
923
    }
758

            
759
    fn imported_part(&self, _name: &Identifier) -> Option<Identifier> {
760
        // unsupported
761
        None
762
    }
763

            
764
    fn is_part(&self, _name: &Identifier) -> bool {
765
        // unsupported
766
        false
767
    }
768

            
769
    /// Returns whether this element matches `:empty`.
770
    ///
771
    /// That is, whether it does not contain any child element or any non-zero-length text node.
772
    /// See <http://dev.w3.org/csswg/selectors-3/#empty-pseudo>.
773
4
    fn is_empty(&self) -> bool {
774
        // .all() returns true for the empty iterator
775
4
        self.0
776
            .children()
777
1
            .all(|child| child.is_chars() && child.borrow_chars().is_empty())
778
4
    }
779

            
780
    /// Returns whether this element matches `:root`,
781
    /// i.e. whether it is the root element of a document.
782
    ///
783
    /// Note: this can be false even if `.parent_element()` is `None`
784
    /// if the parent node is a `DocumentFragment`.
785
1119
    fn is_root(&self) -> bool {
786
1119
        self.0.parent().is_none()
787
1119
    }
788

            
789
    /// Returns the first child element of this element.
790
    fn first_element_child(&self) -> Option<Self> {
791
        self.0
792
            .children()
793
            .find(|child| child.is_element())
794
            .map(|n| n.into())
795
    }
796

            
797
    /// Applies the given selector flags to this element.
798
    fn apply_selector_flags(&self, _: ElementSelectorFlags) {
799
        todo!()
800
    }
801
}
802

            
803
/// Origin for a stylesheet, per CSS 2.2.
804
///
805
/// This is used when sorting selector matches according to their origin and specificity.
806
///
807
/// CSS2.2: <https://www.w3.org/TR/CSS22/cascade.html#cascading-order>
808
37994
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
809
pub enum Origin {
810
    UserAgent,
811
    User,
812
    Author,
813
}
814

            
815
/// A parsed CSS stylesheet.
816
pub struct Stylesheet {
817
    origin: Origin,
818
    qualified_rules: Vec<QualifiedRule>,
819
}
820

            
821
/// A match during the selector matching process
822
///
823
/// This struct comes from [`Stylesheet::get_matches`], and represents
824
/// that a certain node matched a CSS rule which has a selector with a
825
/// certain `specificity`.  The stylesheet's `origin` is also given here.
826
///
827
/// This type implements [`Ord`] so a list of `Match` can be sorted.
828
/// That implementation does ordering based on origin and specificity
829
/// as per <https://www.w3.org/TR/CSS22/cascade.html#cascading-order>.
830
struct Match<'a> {
831
    specificity: u32,
832
    origin: Origin,
833
    declaration: &'a Declaration,
834
}
835

            
836
impl<'a> Ord for Match<'a> {
837
479
    fn cmp(&self, other: &Self) -> Ordering {
838
479
        match self.origin.cmp(&other.origin) {
839
471
            Ordering::Equal => self.specificity.cmp(&other.specificity),
840
8
            o => o,
841
        }
842
479
    }
843
}
844

            
845
impl<'a> PartialOrd for Match<'a> {
846
479
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
847
479
        Some(self.cmp(other))
848
479
    }
849
}
850

            
851
impl<'a> PartialEq for Match<'a> {
852
    fn eq(&self, other: &Self) -> bool {
853
        self.origin == other.origin && self.specificity == other.specificity
854
    }
855
}
856

            
857
impl<'a> Eq for Match<'a> {}
858

            
859
impl Stylesheet {
860
159
    fn empty(origin: Origin) -> Stylesheet {
861
159
        Stylesheet {
862
            origin,
863
159
            qualified_rules: Vec::new(),
864
        }
865
159
    }
866

            
867
    /// Parses a new stylesheet from CSS data in a string.
868
    ///
869
    /// The `url_resolver_url` is required for `@import` rules, so that librsvg can determine if
870
    /// the requested path is allowed.
871
158
    pub fn from_data(
872
        buf: &str,
873
        url_resolver: &UrlResolver,
874
        origin: Origin,
875
        session: Session,
876
    ) -> Result<Self, LoadingError> {
877
158
        let mut stylesheet = Stylesheet::empty(origin);
878
158
        stylesheet.add_rules_from_string(buf, url_resolver, session)?;
879
158
        Ok(stylesheet)
880
158
    }
881

            
882
    /// Parses a new stylesheet by loading CSS data from a URL.
883
1
    pub fn from_href(
884
        aurl: &AllowedUrl,
885
        origin: Origin,
886
        session: Session,
887
    ) -> Result<Self, LoadingError> {
888
1
        let mut stylesheet = Stylesheet::empty(origin);
889
1
        stylesheet.load(aurl, session)?;
890
1
        Ok(stylesheet)
891
1
    }
892

            
893
    /// Parses the CSS rules in `buf` and appends them to the stylesheet.
894
    ///
895
    /// The `url_resolver_url` is required for `@import` rules, so that librsvg can determine if
896
    /// the requested path is allowed.
897
    ///
898
    /// If there is an `@import` rule, its rules will be recursively added into the
899
    /// stylesheet, in the order in which they appear.
900
161
    fn add_rules_from_string(
901
        &mut self,
902
        buf: &str,
903
        url_resolver: &UrlResolver,
904
        session: Session,
905
    ) -> Result<(), LoadingError> {
906
161
        let mut input = ParserInput::new(buf);
907
161
        let mut parser = Parser::new(&mut input);
908
161
        let mut rule_parser = RuleParser {
909
161
            session: session.clone(),
910
        };
911

            
912
161
        StyleSheetParser::new(&mut parser, &mut rule_parser)
913
659
            .filter_map(|r| match r {
914
496
                Ok(rule) => Some(rule),
915
2
                Err(e) => {
916
2
                    rsvg_log!(session, "Invalid rule; ignoring: {:?}", e);
917
2
                    None
918
2
                }
919
498
            })
920
657
            .for_each(|rule| match rule {
921
2
                Rule::AtRule(AtRule::Import(url)) => match url_resolver.resolve_href(&url) {
922
2
                    Ok(aurl) => {
923
                        // ignore invalid imports
924
2
                        let _ = self.load(&aurl, session.clone());
925
2
                    }
926

            
927
                    Err(e) => {
928
                        rsvg_log!(session, "Not loading stylesheet from \"{}\": {}", url, e);
929
                    }
930
2
                },
931

            
932
494
                Rule::QualifiedRule(qr) => self.qualified_rules.push(qr),
933
496
            });
934

            
935
161
        Ok(())
936
161
    }
937

            
938
    /// Parses a stylesheet referenced by an URL
939
3
    fn load(&mut self, aurl: &AllowedUrl, session: Session) -> Result<(), LoadingError> {
940
3
        io::acquire_data(aurl, None)
941
            .map_err(LoadingError::from)
942
6
            .and_then(|data| {
943
                let BinaryData {
944
3
                    data: bytes,
945
3
                    mime_type,
946
3
                } = data;
947

            
948
3
                if is_text_css(&mime_type) {
949
3
                    Ok(bytes)
950
                } else {
951
                    rsvg_log!(session, "\"{}\" is not of type text/css; ignoring", aurl);
952
                    Err(LoadingError::BadCss)
953
                }
954
3
            })
955
6
            .and_then(|bytes| {
956
3
                String::from_utf8(bytes).map_err(|_| {
957
                    rsvg_log!(
958
                        session,
959
                        "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
960
                        aurl
961
                    );
962
                    LoadingError::BadCss
963
                })
964
3
            })
965
6
            .and_then(|utf8| {
966
3
                let url = (**aurl).clone();
967
3
                self.add_rules_from_string(&utf8, &UrlResolver::new(Some(url)), session)
968
3
            })
969
3
    }
970

            
971
    /// Appends the style declarations that match a specified node to a given vector
972
23024
    fn get_matches<'a>(
973
        &'a self,
974
        node: &Node,
975
        match_ctx: &mut MatchingContext<'_, Selector>,
976
        acc: &mut Vec<Match<'a>>,
977
    ) {
978
93604
        for rule in &self.qualified_rules {
979
490331
            for selector in &rule.selectors.0 {
980
                // This magic call is stolen from selectors::matching::matches_selector_list()
981
419751
                let matches = selectors::matching::matches_selector(
982
                    selector,
983
                    0,
984
419751
                    None,
985
419751
                    &RsvgElement(node.clone()),
986
                    match_ctx,
987
419751
                );
988

            
989
419751
                if matches {
990
6703
                    for decl in rule.declarations.iter() {
991
3454
                        acc.push(Match {
992
                            declaration: decl,
993
3454
                            specificity: selector.specificity(),
994
3454
                            origin: self.origin,
995
                        });
996
                    }
997
                }
998
            }
999
        }
23024
    }
}
3
fn is_text_css(mime_type: &Mime) -> bool {
3
    mime_type.type_ == "text" && mime_type.subtype == "css"
3
}
/// Runs the CSS cascade on the specified tree from all the stylesheets
45719
pub fn cascade(
    root: &mut Node,
    ua_stylesheets: &[Stylesheet],
    author_stylesheets: &[Stylesheet],
    user_stylesheets: &[Stylesheet],
    session: &Session,
) {
99449
    for mut node in root.descendants().filter(|n| n.is_element()) {
22391
        let mut matches = Vec::new();
        // xml:lang needs to be inherited before selector matching, so it
        // can't be done in the usual SpecifiedValues::to_computed_values,
        // which is called by cascade() and runs after matching.
22391
        let parent = node.parent().clone();
22217
        node.borrow_element_mut().inherit_xml_lang(parent);
22483
        let mut cache = NthIndexCache::default();
22346
        let mut match_ctx = MatchingContext::new(
22346
            MatchingMode::Normal,
            // FIXME: how the fuck does one set up a bloom filter here?
22346
            None,
            &mut cache,
22346
            QuirksMode::NoQuirks,
22346
            NeedsSelectorFlags::No,
22346
            IgnoreNthChildForInvalidation::No,
        );
45258
        for s in ua_stylesheets
            .iter()
            .chain(author_stylesheets)
            .chain(user_stylesheets)
        {
23024
            s.get_matches(&node, &mut match_ctx, &mut matches);
        }
22243
        matches.as_mut_slice().sort();
22235
        let mut element = node.borrow_element_mut();
25705
        for m in matches {
3455
            element.apply_style_declaration(m.declaration, m.origin);
        }
22234
        element.set_style_attribute(session);
22232
    }
1099
    let values = ComputedValues::default();
1099
    root.cascade(&values);
1099
}
#[cfg(test)]
mod tests {
    use super::*;
    use selectors::Element;
    use crate::document::Document;
    use crate::is_element_of_type;
    #[test]
2
    fn xml_lang() {
1
        let document = Document::load_from_bytes(
            br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xml:lang="zh">
  <text id="a" x="10" y="10" width="30" height="30"></text>
  <text id="b" x="10" y="20" width="30" height="30" xml:lang="en"></text>
</svg>
"#,
        );
1
        let a = document.lookup_internal_node("a").unwrap();
2
        assert_eq!(
1
            a.borrow_element()
                .get_computed_values()
                .xml_lang()
                .0
                .unwrap()
                .as_str(),
            "zh"
        );
1
        let b = document.lookup_internal_node("b").unwrap();
2
        assert_eq!(
1
            b.borrow_element()
                .get_computed_values()
                .xml_lang()
                .0
                .unwrap()
                .as_str(),
            "en"
        );
2
    }
    #[test]
2
    fn impl_element() {
1
        let document = Document::load_from_bytes(
            br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="a">
  <rect id="b" x="10" y="10" width="30" height="30"/>
  <circle id="c" cx="10" cy="10" r="10"/>
  <rect id="d" class="foo bar"/>
</svg>
"#,
        );
1
        let a = document.lookup_internal_node("a").unwrap();
1
        let b = document.lookup_internal_node("b").unwrap();
1
        let c = document.lookup_internal_node("c").unwrap();
1
        let d = document.lookup_internal_node("d").unwrap();
        // Node types
1
        assert!(is_element_of_type!(a, Svg));
1
        assert!(is_element_of_type!(b, Rect));
1
        assert!(is_element_of_type!(c, Circle));
1
        assert!(is_element_of_type!(d, Rect));
1
        let a = RsvgElement(a);
1
        let b = RsvgElement(b);
1
        let c = RsvgElement(c);
1
        let d = RsvgElement(d);
        // Tree navigation
2
        assert_eq!(a.parent_element(), None);
2
        assert_eq!(b.parent_element(), Some(a.clone()));
2
        assert_eq!(c.parent_element(), Some(a.clone()));
2
        assert_eq!(d.parent_element(), Some(a.clone()));
2
        assert_eq!(b.next_sibling_element(), Some(c.clone()));
2
        assert_eq!(c.next_sibling_element(), Some(d.clone()));
2
        assert_eq!(d.next_sibling_element(), None);
2
        assert_eq!(b.prev_sibling_element(), None);
2
        assert_eq!(c.prev_sibling_element(), Some(b.clone()));
2
        assert_eq!(d.prev_sibling_element(), Some(c.clone()));
        // Other operations
1
        assert!(a.has_local_name(&LocalName::from("svg")));
1
        assert!(a.has_namespace(&ns!(svg)));
1
        assert!(!a.is_same_type(&b));
1
        assert!(b.is_same_type(&d));
1
        assert!(a.has_id(
1
            &Identifier::from("a"),
1
            CaseSensitivity::AsciiCaseInsensitive
        ));
1
        assert!(!b.has_id(
1
            &Identifier::from("foo"),
1
            CaseSensitivity::AsciiCaseInsensitive
        ));
1
        assert!(d.has_class(
1
            &Identifier::from("foo"),
1
            CaseSensitivity::AsciiCaseInsensitive
        ));
1
        assert!(d.has_class(
1
            &Identifier::from("bar"),
1
            CaseSensitivity::AsciiCaseInsensitive
        ));
1
        assert!(!a.has_class(
1
            &Identifier::from("foo"),
1
            CaseSensitivity::AsciiCaseInsensitive
        ));
1
        assert!(d.is_empty());
1
        assert!(!a.is_empty());
2
    }
}