[librsvg: 18/26] Internals documentation for the css module
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 18/26] Internals documentation for the css module
- Date: Sun, 10 Nov 2019 20:18:13 +0000 (UTC)
commit c2cb0d5d6e60edd9d09154693429eb89a5773013
Author: Federico Mena Quintero <federico gnome org>
Date: Fri Nov 8 19:28:23 2019 -0600
Internals documentation for the css module
rsvg_internals/src/css.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 147 insertions(+), 3 deletions(-)
---
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
index 39a0289c..0d8f2536 100644
--- a/rsvg_internals/src/css.rs
+++ b/rsvg_internals/src/css.rs
@@ -1,3 +1,75 @@
+//! Representation of CSS types, and the CSS parsing and matching engine.
+//!
+//! # Terminology
+//!
+//! Consider a CSS **stylesheet** like this:
+//!
+//! ```ignore
+//! @import url("another.css");
+//!
+//! foo, .bar {
+//! fill: red;
+//! stroke: green;
+//! }
+//!
+//! #baz { stroke-width: 42; }
+//!
+//! ```
+//! Let's look at each part:
+//!
+//! `@import` is an **at-rule**. There are other at-rules like
+//! `@media`, but librsvg doesn't support those yet.
+//!
+//! `foo, .bar` is a **selector list** with two **selectors**, one for
+//! `foo` elements and one for elements that have the `bar` class.
+//!
+//! The curly braces begin a **block**; the stuff between `{}` is a
+//! **declaration list**. The first block contains two
+//! **declarations**, one for the `fill` **property** and one for the
+//! `stroke` property.
+//!
+//! A selector list plus a declaration list inside a block is a
+//! **ruleset** or just **rule**.
+//!
+//! After ther first rule, we have a second rule with a single
+//! selector for the `#baz` id, with a single declaration for the
+//! `stroke-width` property.
+//!
+//! # Helper crates we use
+//!
+//! * `cssparser` crate as a CSS tokenizer, and some utilities to
+//! parse CSS rules and declarations.
+//!
+//! * `selectors` crate for the representation of selectors and
+//! selector lists, and for the matching engine.
+//!
+//! Both crates provide very generic implementations of their concepts,
+//! and expect the caller to provide implementations of various traits,
+//! and to provide types that represent certain things.
+//!
+//! For example, `cssparser` expects one to provide representations of
+//! the following types:
+//!
+//! * A parsed CSS rule. For `fill: blue;` we have
+//! `ParsedProperty::Fill(...)`.
+//!
+//! * A declaration list; we use `DeclarationList`.
+//!
+//! * A parsed selector list; we use `SelectorList` from the
+//! `selectors` crate.
+//!
+//! In turn, the `selectors` crate needs a way to navigate and examine
+//! one's implementation of an element tree. We provide `impl
+//! selectors::Element for RsvgElement` for this. This implementation
+//! has methods like "does this element have the id `#foo`", or "give
+//! me the next sibling element".
+//!
+//! Finally, the matching engine ties all of this together with
+//! `matches_selector_list()`. This takes an opaque representation of
+//! an element, plus a selector list, and returns a bool. We iterate
+//! through the rules in a stylesheet and apply each rule that matches
+//! to each element node.
+
use cssparser::{
self,
parse_important,
@@ -31,25 +103,44 @@ use crate::node::{NodeType, RsvgNode};
use crate::properties::{parse_attribute_value_into_parsed_property, ParsedProperty};
use crate::text::NodeChars;
-/// A parsed CSS declaration (`name: value [!important]`)
+/// A parsed CSS declaration
+///
+/// For example, in the declaration `fill: green !important`, the
+/// `attribute` would be `fill`, the `property` would be the parsed
+/// value of the green color, and `important` would be `true`.
pub struct Declaration {
pub attribute: QualName,
pub property: ParsedProperty,
pub important: bool,
}
+/// A list of property/value declarations, hashed by the property name
+///
+/// For example, in a CSS rule:
+///
+/// ```ignore
+/// foo { fill: red; stroke: green; }
+/// ```
+///
+/// The stuff between braces is the declaration list; this example has two
+/// declarations, one for `fill`, and one for `stroke`, each with its own value.
#[derive(Default)]
pub struct DeclarationList {
// Maps property_name -> Declaration
declarations: HashMap<QualName, Declaration>,
}
+/// Dummy struct required to use `cssparser::DeclarationListParser`
+///
+/// It implements `cssparser::DeclarationParser`, which knows how to parse
+/// the property/value pairs from a CSS declaration.
pub struct DeclParser;
impl<'i> DeclarationParser<'i> for DeclParser {
type Declaration = Declaration;
type Error = ValueErrorKind;
+ /// Parses a CSS declaration like `name: input_value [!important]`
fn parse_value<'t>(
&mut self,
name: CowRcStr<'i>,
@@ -69,6 +160,10 @@ impl<'i> DeclarationParser<'i> for DeclParser {
}
}
+// cssparser's DeclarationListParser requires this; we just use the dummy
+// implementations from cssparser itself. We may want to provide a real
+// implementation in the future, although this may require keeping track of the
+// CSS parsing state like Servo does.
impl<'i> AtRuleParser<'i> for DeclParser {
type PreludeNoBlock = ();
type PreludeBlock = ();
@@ -79,6 +174,7 @@ impl<'i> AtRuleParser<'i> for DeclParser {
/// Dummy struct to implement cssparser::QualifiedRuleParser
pub struct QualRuleParser;
+/// Errors from the CSS parsing process
pub enum CssParseErrorKind<'i> {
Selector(selectors::parser::SelectorParseErrorKind<'i>),
Value(ValueErrorKind),
@@ -91,11 +187,21 @@ impl<'i> From<selectors::parser::SelectorParseErrorKind<'i>> for CssParseErrorKi
}
/// A CSS ruleset (or rule)
+///
+/// This is a complete ruleset or rule:
+///
+/// ```ignore
+/// foo, .bar { fill: red; stroke: green; }
+/// ```
+///
+/// Here, we have a list with two selectors (`foo`, `.bar`), and a block with a
+/// declaration list with two declarations, one for `fill` and one for `stroke`.
pub struct Rule {
selectors: SelectorList<RsvgSelectors>,
declarations: DeclarationList,
}
+// Required to implement the `Prelude` associated type in `cssparser::QualifiedRuleParser`
impl<'i> selectors::Parser<'i> for QualRuleParser {
type Impl = RsvgSelectors;
type Error = CssParseErrorKind<'i>;
@@ -116,6 +222,25 @@ impl<'i> selectors::Parser<'i> for QualRuleParser {
}
}
+// `cssparser::RuleListParser` is a struct which requires that we
+// provide a type that implements `cssparser::QualifiedRuleParser`.
+//
+// In turn, `cssparser::QualifiedRuleParser` requires that we
+// implement a way to parse the `Prelude` of a ruleset or rule. For
+// example, in this ruleset:
+//
+// ```ignore
+// foo, .bar { fill: red; stroke: green; }
+// ```
+//
+// The prelude is the selector list with the `foo` and `.bar` selectors.
+//
+// The `parse_prelude` method just uses `selectors::SelectorList`. This
+// is what requires the `impl selectors::Parser for QualRuleParser`.
+//
+// Next, the `parse_block` method takes an already-parsed prelude (a selector list),
+// and tries to parse the block between braces - a `DeclarationList`. It creates
+// a `Rule` out of the selector list and the declaration list.
impl<'i> QualifiedRuleParser<'i> for QualRuleParser {
type Prelude = SelectorList<RsvgSelectors>;
type QualifiedRule = Rule;
@@ -154,6 +279,9 @@ impl<'i> QualifiedRuleParser<'i> for QualRuleParser {
}
}
+// Required by `cssparser::RuleListParser`.
+//
+// This does not handle at-rules like `@import`; we should do so.
impl<'i> AtRuleParser<'i> for QualRuleParser {
type PreludeNoBlock = ();
type PreludeBlock = ();
@@ -161,7 +289,7 @@ impl<'i> AtRuleParser<'i> for QualRuleParser {
type Error = CssParseErrorKind<'i>;
}
-/// Dummy type required by the SelectorImpl trait
+/// Dummy type required by the SelectorImpl trait.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NonTSPseudoClass;
@@ -216,7 +344,12 @@ impl SelectorImpl for RsvgSelectors {
type PseudoElement = PseudoElement;
}
-// We need a newtype because RsvgNode is an alias for rctree::Node
+/// Wraps an `RsvgNode` with a locally-defined type, so we can implement
+/// a foreign trait on it.
+///
+/// RsvgNode is an alias for rctree::Node, so we can't implement
+/// `selectors::Element` directly on it. We implement it on the
+/// `RsvgElement` wrapper instead.
#[derive(Clone)]
pub struct RsvgElement(RsvgNode);
@@ -232,6 +365,7 @@ impl fmt::Debug for RsvgElement {
}
}
+// The selectors crate uses this to examine our tree of elements.
impl selectors::Element for RsvgElement {
type Impl = RsvgSelectors;
@@ -419,6 +553,7 @@ impl DeclarationList {
}
}
+/// Iterator for a `DeclarationList`, created with `decl_list.iter()`
pub struct DeclarationListIter<'a>(HashMapIter<'a, QualName, Declaration>);
impl<'a> Iterator for DeclarationListIter<'a> {
@@ -430,6 +565,10 @@ impl<'a> Iterator for DeclarationListIter<'a> {
}
impl Stylesheet {
+ /// Parses a CSS stylesheet from a string
+ ///
+ /// The `base_url` is required for `@import` rules, so that librsvg
+ /// can determine if the requested path is allowed.
pub fn parse(&mut self, base_url: Option<&Url>, buf: &str) {
let mut input = ParserInput::new(buf);
let mut parser = Parser::new(&mut input);
@@ -444,6 +583,7 @@ impl Stylesheet {
}
}
+ /// Parses a stylesheet referenced by an URL
pub fn load_css(&mut self, aurl: &AllowedUrl) -> Result<(), LoadingError> {
io::acquire_data(aurl, None)
.and_then(|data| {
@@ -474,6 +614,10 @@ impl Stylesheet {
})
}
+ /// The main CSS matching function.
+ ///
+ /// Takes a `node` and modifies its `specified_values` with the
+ /// CSS rules that match the node.
pub fn apply_matches_to_node(&self, node: &mut RsvgNode) {
let mut match_ctx = MatchingContext::new(
MatchingMode::Normal,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]