[librsvg: 5/8] Use ComputedValues for xml:lang




commit aecee4990fd53353a600711db66ae0a163364947
Author: Michael Howell <michael notriddle com>
Date:   Thu Oct 14 18:06:00 2021 -0700

    Use ComputedValues for xml:lang
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/606>

 src/css.rs           | 10 +++++++++-
 src/document.rs      | 16 +---------------
 src/element.rs       | 36 ++++--------------------------------
 src/properties.rs    | 16 +++++++++++++++-
 src/property_defs.rs | 12 +++++++++---
 src/text.rs          |  2 +-
 6 files changed, 39 insertions(+), 53 deletions(-)
---
diff --git a/src/css.rs b/src/css.rs
index 417c1219..6803b875 100644
--- a/src/css.rs
+++ b/src/css.rs
@@ -557,7 +557,10 @@ impl selectors::Element for RsvgElement {
             NonTSPseudoClass::Lang(css_lang) => self
                 .0
                 .borrow_element()
-                .get_language()
+                .get_computed_values()
+                .xml_lang()
+                .0
+                .as_ref()
                 .map_or(false, |e_lang| css_lang.iter().any(|l| l.matches(e_lang))),
         }
     }
@@ -838,6 +841,11 @@ pub fn cascade(
     for mut node in root.descendants().filter(|n| n.is_element()) {
         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.
+        node.borrow_element_mut().inherit_xml_lang();
+
         let mut match_ctx = MatchingContext::new(
             MatchingMode::Normal,
             // FIXME: how the fuck does one set up a bloom filter here?
diff --git a/src/document.rs b/src/document.rs
index 1db4e7c8..406a6d79 100644
--- a/src/document.rs
+++ b/src/document.rs
@@ -487,7 +487,7 @@ impl DocumentBuilder {
         attrs: Attributes,
         parent: Option<Node>,
     ) -> Node {
-        let mut node = Node::new(NodeData::new_element(name, attrs));
+        let node = Node::new(NodeData::new_element(name, attrs));
 
         if let Some(id) = node.borrow_element().get_id() {
             // This is so we don't overwrite an existing id
@@ -498,20 +498,6 @@ impl DocumentBuilder {
 
         if let Some(mut parent) = parent {
             parent.append(node.clone());
-
-            // If no language is specified on an element,
-            // it implicitly gets one from the parent,
-            // https://www.w3.org/International/questions/qa-when-xmllang#bytheway
-            //
-            // "It's important to remember that xml:lang has scope: lower-level elements inherit
-            // the language attribute. This can be used to identify the language for a lot of
-            // content (without having redundant language tags on every element)."
-            if let Some(parent_language) = parent.borrow_element().get_language() {
-                let mut child = node.borrow_element_mut();
-                if child.get_language().is_none() {
-                    child.set_language(parent_language.clone());
-                }
-            }
         } else if self.tree.is_none() {
             self.tree = Some(node.clone());
         } else {
diff --git a/src/element.rs b/src/element.rs
index 4eb8c1b4..5b19ecd8 100644
--- a/src/element.rs
+++ b/src/element.rs
@@ -1,12 +1,10 @@
 //! SVG Elements.
 
-use language_tags::LanguageTag;
 use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
 use once_cell::sync::Lazy;
 use std::collections::{HashMap, HashSet};
 use std::fmt;
 use std::ops::Deref;
-use std::str::FromStr;
 
 use crate::accept_language::UserLanguage;
 use crate::bbox::BoundingBox;
@@ -106,7 +104,6 @@ pub struct ElementInner<T: SetAttributes + Draw> {
     required_extensions: Option<RequiredExtensions>,
     required_features: Option<RequiredFeatures>,
     system_language: Option<SystemLanguage>,
-    language: Option<LanguageTag>,
     pub element_impl: T,
 }
 
@@ -131,13 +128,11 @@ impl<T: SetAttributes + Draw> ElementInner<T> {
             required_extensions: Default::default(),
             required_features: Default::default(),
             system_language: Default::default(),
-            language: Default::default(),
             element_impl,
         };
 
         let mut set_attributes = || -> Result<(), ElementError> {
             e.set_conditional_processing_attributes()?;
-            e.set_language_attribute()?;
             e.set_presentation_attributes()?;
             Ok(())
         };
@@ -165,12 +160,8 @@ impl<T: SetAttributes + Draw> ElementInner<T> {
         self.class.as_deref()
     }
 
-    fn get_language(&self) -> Option<&LanguageTag> {
-        self.language.as_ref()
-    }
-
-    fn set_language(&mut self, language_tag: LanguageTag) {
-        self.language = Some(language_tag);
+    fn inherit_xml_lang(&mut self) {
+        self.specified_values.inherit_xml_lang(&mut self.values);
     }
 
     fn get_specified_values(&self) -> &SpecifiedValues {
@@ -202,21 +193,6 @@ impl<T: SetAttributes + Draw> ElementInner<T> {
                 .unwrap_or(true)
     }
 
-    fn set_language_attribute(&mut self) -> Result<(), ElementError> {
-        for (attr, value) in self.attributes.iter() {
-            if attr.expanded() == expanded_name!(xml "lang") {
-                self.language = Some(
-                    LanguageTag::from_str(value)
-                        .map_err(|e| {
-                            ValueErrorKind::parse_error(&format!("invalid language tag: \"{}\"", e))
-                        })
-                        .attribute(attr)?,
-                );
-            }
-        }
-        Ok(())
-    }
-
     fn set_conditional_processing_attributes(&mut self) -> Result<(), ElementError> {
         for (attr, value) in self.attributes.iter() {
             match attr.expanded() {
@@ -525,12 +501,8 @@ impl Element {
         call_inner!(self, get_class)
     }
 
-    pub fn get_language(&self) -> Option<&LanguageTag> {
-        call_inner!(self, get_language)
-    }
-
-    pub fn set_language(&mut self, language: LanguageTag) {
-        call_inner!(self, set_language, language)
+    pub fn inherit_xml_lang(&mut self) {
+        call_inner!(self, inherit_xml_lang)
     }
 
     pub fn get_specified_values(&self) -> &SpecifiedValues {
diff --git a/src/properties.rs b/src/properties.rs
index d2ea9f85..d661fb0e 100644
--- a/src/properties.rs
+++ b/src/properties.rs
@@ -728,7 +728,6 @@ impl SpecifiedValues {
         compute!(UnicodeBidi, unicode_bidi);
         compute!(Visibility, visibility);
         compute!(WritingMode, writing_mode);
-        compute!(XmlLang, xml_lang);
         compute!(XmlSpace, xml_space);
 
         computed.transform = self.transform.unwrap_or_else(|| {
@@ -741,6 +740,21 @@ impl SpecifiedValues {
         });
     }
 
+    /// This is a somewhat egregious hack to allow xml:lang to be stored as a presentational
+    /// attribute. Presentational attributes can often be influenced by stylesheets,
+    /// so they're cascaded after selector matching is done, but xml:lang can be queried by
+    /// CSS selectors, so they need to be cascaded *first*.
+    pub fn inherit_xml_lang(&self, computed: &mut ComputedValues) {
+        let prop_val = self.get_property(PropertyId::XmlLang);
+        if let ParsedProperty::XmlLang(s) = prop_val {
+            computed.set_value(ComputedValue::XmlLang(
+                s.compute(&computed.xml_lang(), computed),
+            ));
+        } else {
+            unreachable!();
+        }
+    }
+
     pub fn is_overflow(&self) -> bool {
         if let Some(overflow_index) = self.property_index(PropertyId::Overflow) {
             match self.props[overflow_index] {
diff --git a/src/property_defs.rs b/src/property_defs.rs
index 6eb8c713..4923667e 100644
--- a/src/property_defs.rs
+++ b/src/property_defs.rs
@@ -35,8 +35,10 @@
 //!
 //! * An implementation of the [`Parse`] trait for the underlying type.
 use std::convert::TryInto;
+use std::str::FromStr;
 
 use cssparser::{Parser, Token};
+use language_tags::LanguageTag;
 
 use crate::dasharray::Dasharray;
 use crate::error::*;
@@ -1088,13 +1090,17 @@ make_property!(
     XmlLang,
     default: None,
     inherits_automatically: true,
-    newtype: Option<String>,
+    newtype: Option<LanguageTag>,
     parse_impl: {
         impl Parse for XmlLang {
             fn parse<'i>(
                 parser: &mut Parser<'i, '_>,
             ) -> Result<XmlLang, ParseError<'i>> {
-                Ok(XmlLang(Some(parser.expect_ident()?.to_string())))
+                let language_tag = parser.expect_ident()?;
+                let language_tag = LanguageTag::from_str(language_tag).map_err(|_| {
+                    parser.new_custom_error(ValueErrorKind::parse_error("invalid syntax for 'xml:lang' 
parameter"))
+                })?;
+                Ok(XmlLang(Some(language_tag)))
             }
         }
     },
@@ -1105,7 +1111,7 @@ make_property!(
 fn parses_xml_lang() {
     assert_eq!(
         XmlLang::parse_str("es-MX").unwrap(),
-        XmlLang(Some("es-MX".to_string()))
+        XmlLang(Some(LanguageTag::from_str("es-MX").unwrap()))
     );
 
     assert!(XmlLang::parse_str("").is_err());
diff --git a/src/text.rs b/src/text.rs
index f5d715f9..7f185972 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -869,7 +869,7 @@ fn create_pango_layout(draw_ctx: &DrawingCtx, props: &FontProperties, text: &str
     let pango_context = draw_ctx.create_pango_context();
 
     if let XmlLang(Some(ref lang)) = props.xml_lang {
-        pango_context.set_language(&pango::Language::from_string(lang));
+        pango_context.set_language(&pango::Language::from_string(lang.as_str()));
     }
 
     pango_context.set_base_gravity(pango::Gravity::from(props.writing_mode));


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]