[librsvg: 1/2] Rework element representation



commit d7b564525528b9f07f4ec619785b7f8a250e32af
Author: Paolo Borelli <pborelli gnome org>
Date:   Sat Apr 4 12:10:07 2020 +0200

    Rework element representation
    
    Use an emum for the Element type and static dispacth.
    Each variant in the enum contains an ElementInner which implements
    what is common among all elements. ElementInner is generic over
    the actual implementation of each element type. We do not need
    to use the downcast trait anyore. ElementInner derefs to
    the corresonding element implementation.
    Each ElementInner in Element is boxed in order to keep Element small.
    We do not need to box in Node anymore.
    We do not need a separate ElementType enum anymore.
    
    This patch adds the "matches" crate as a dependency: rust 1.42
    includes the matches! macro, but we do not want to bump the
    requirement yet and some of our dependencies like rust-cssparser
    already pull in the same crate,

 Cargo.lock                                       |   8 +-
 rsvg_internals/Cargo.toml                        |   2 +-
 rsvg_internals/src/css.rs                        |  16 +-
 rsvg_internals/src/document.rs                   |  13 +-
 rsvg_internals/src/drawing_ctx.rs                | 158 ++++-----
 rsvg_internals/src/element.rs                    | 425 +++++++++++++++++------
 rsvg_internals/src/filters/blend.rs              |   2 -
 rsvg_internals/src/filters/color_matrix.rs       |   2 -
 rsvg_internals/src/filters/component_transfer.rs |  44 +--
 rsvg_internals/src/filters/composite.rs          |   2 -
 rsvg_internals/src/filters/context.rs            |  10 +-
 rsvg_internals/src/filters/convolve_matrix.rs    |   2 -
 rsvg_internals/src/filters/displacement_map.rs   |   2 -
 rsvg_internals/src/filters/flood.rs              |   3 -
 rsvg_internals/src/filters/gaussian_blur.rs      |   2 -
 rsvg_internals/src/filters/image.rs              |   2 -
 rsvg_internals/src/filters/light/lighting.rs     |  29 +-
 rsvg_internals/src/filters/merge.rs              |  42 +--
 rsvg_internals/src/filters/mod.rs                |  22 +-
 rsvg_internals/src/filters/morphology.rs         |   2 -
 rsvg_internals/src/filters/offset.rs             |   2 -
 rsvg_internals/src/filters/tile.rs               |   2 -
 rsvg_internals/src/filters/turbulence.rs         |   3 -
 rsvg_internals/src/gradient.rs                   |  67 ++--
 rsvg_internals/src/handle.rs                     |  20 +-
 rsvg_internals/src/lib.rs                        |   4 +-
 rsvg_internals/src/marker.rs                     |   7 +-
 rsvg_internals/src/node.rs                       |  34 +-
 rsvg_internals/src/pattern.rs                    |  16 +-
 rsvg_internals/src/text.rs                       |  20 +-
 rsvg_internals/src/xml.rs                        |   9 +-
 31 files changed, 530 insertions(+), 442 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 1d014455..4dee0b7a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -318,11 +318,6 @@ name = "doc-comment"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 
-[[package]]
-name = "downcast-rs"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-
 [[package]]
 name = "dtoa"
 version = "0.4.5"
@@ -1320,7 +1315,6 @@ dependencies = [
  "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.27.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "data-url 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "float-cmp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gdk-pixbuf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1334,6 +1328,7 @@ dependencies = [
  "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
  "locale_config 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "markup5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "nalgebra 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1713,7 +1708,6 @@ dependencies = [
 "checksum derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)" = 
"e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7"
 "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 "checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = 
"fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
-"checksum downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = 
"52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6"
 "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = 
"4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
 "checksum dtoa-short 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = 
"59020b8513b76630c49d918c33db9f4c91638e7d3404a28084083b87e33f76f2"
 "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = 
"bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
diff --git a/rsvg_internals/Cargo.toml b/rsvg_internals/Cargo.toml
index b7dee729..794dc555 100644
--- a/rsvg_internals/Cargo.toml
+++ b/rsvg_internals/Cargo.toml
@@ -11,7 +11,6 @@ cairo-rs = { version="0.8.0", features=["v1_16"] }
 cairo-sys-rs = "0.9.0"
 cssparser = "0.27.1"
 data-url = "0.1"
-downcast-rs = "^1.0.0"
 encoding = "0.2.33"
 float-cmp = "0.6.0"
 gdk-pixbuf = "0.8.0"
@@ -25,6 +24,7 @@ language-tags = "0.2.2"
 libc = "0.2"
 locale_config = "*" # recommended explicitly by locale_config's README.md
 markup5ever = "0.10"
+matches = "0.1"
 nalgebra = "0.20.0"
 num-traits = "0.2"
 once_cell = "1.2.0"
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
index 79e11b73..15a5a101 100644
--- a/rsvg_internals/src/css.rs
+++ b/rsvg_internals/src/css.rs
@@ -78,19 +78,16 @@ use cssparser::{
     CowRcStr, DeclarationListParser, DeclarationParser, Parser, ParserInput, QualifiedRuleParser,
     RuleListParser, SourceLocation, ToCss, _cssparser_internal_to_lowercase,
 };
+use markup5ever::{namespace_url, ns, LocalName, Namespace, Prefix, QualName};
 use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
 use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, QuirksMode};
 use selectors::{OpaqueElement, SelectorImpl, SelectorList};
-
 use std::cmp::Ordering;
 use std::fmt;
 use std::str;
-
-use markup5ever::{namespace_url, ns, LocalName, Namespace, Prefix, QualName};
 use url::Url;
 
 use crate::allowed_url::AllowedUrl;
-use crate::element::ElementType;
 use crate::error::*;
 use crate::io::{self, BinaryData};
 use crate::node::{Node, NodeBorrow, NodeCascade};
@@ -499,7 +496,7 @@ impl selectors::Element for RsvgElement {
     /// Whether this element is a `link`.
     fn is_link(&self) -> bool {
         // FIXME: is this correct for SVG <a>, not HTML <a>?
-        self.0.is_element() && self.0.borrow_element().get_type() == ElementType::Link
+        self.0.is_element() && is_element_of_type!(self.0, Link)
     }
 
     /// Returns whether the element is an HTML <slot> element.
@@ -823,11 +820,10 @@ mod tests {
             .unwrap();
 
         // Node types
-
-        assert!(a.borrow_element().get_type() == ElementType::Svg);
-        assert!(b.borrow_element().get_type() == ElementType::Rect);
-        assert!(c.borrow_element().get_type() == ElementType::Circle);
-        assert!(d.borrow_element().get_type() == ElementType::Rect);
+        assert!(is_element_of_type!(a, Svg));
+        assert!(is_element_of_type!(b, Rect));
+        assert!(is_element_of_type!(c, Circle));
+        assert!(is_element_of_type!(d, Rect));
 
         let a = RsvgElement(a);
         let b = RsvgElement(b);
diff --git a/rsvg_internals/src/document.rs b/rsvg_internals/src/document.rs
index b4ea99a1..76250817 100644
--- a/rsvg_internals/src/document.rs
+++ b/rsvg_internals/src/document.rs
@@ -11,14 +11,12 @@ use std::rc::Rc;
 
 use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
 use crate::css::{self, Origin, Stylesheet};
-use crate::element::ElementType;
 use crate::error::{AcquireError, LoadingError};
 use crate::handle::LoadOptions;
 use crate::io::{self, BinaryData};
 use crate::limits;
 use crate::node::{Node, NodeBorrow, NodeData};
 use crate::property_bag::PropertyBag;
-use crate::structure::{IntrinsicDimensions, Svg};
 use crate::surface_utils::shared_surface::SharedImageSurface;
 use crate::xml::xml_load_from_possibly_compressed_stream;
 
@@ -98,15 +96,6 @@ impl Document {
         self.images.borrow_mut().lookup(&self.load_options, &aurl)
     }
 
-    /// Gets the dimension parameters of the toplevel `<svg>`.
-    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
-        let root = self.root();
-        let elt = root.borrow_element();
-
-        assert!(elt.get_type() == ElementType::Svg);
-        elt.get_impl::<Svg>().get_intrinsic_dimensions()
-    }
-
     /// Runs the CSS cascade on the document tree
     ///
     /// This uses the deafault UserAgent stylesheet, the document's internal stylesheets,
@@ -477,7 +466,7 @@ impl DocumentBuilder {
 
         match tree {
             Some(root) if root.is_element() => {
-                if root.borrow_element().get_type() == ElementType::Svg {
+                if is_element_of_type!(root, Svg) {
                     let mut document = Document {
                         tree: root,
                         ids,
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index beaadb37..1e876144 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -14,15 +14,13 @@ use crate::coord_units::CoordUnits;
 use crate::dasharray::Dasharray;
 use crate::document::AcquiredNodes;
 use crate::dpi::Dpi;
-use crate::element::ElementType;
+use crate::element::Element;
 use crate::error::{AcquireError, RenderingError};
 use crate::filters;
-use crate::gradient::{LinearGradient, RadialGradient};
 use crate::marker;
 use crate::node::{CascadedValues, Node, NodeBorrow, NodeDraw};
 use crate::paint_server::{PaintServer, PaintSource};
 use crate::path_builder::*;
-use crate::pattern::Pattern;
 use crate::properties::ComputedValues;
 use crate::property_defs::{
     ClipRule, FillRule, Opacity, Overflow, ShapeRendering, StrokeDasharray, StrokeLinecap,
@@ -30,7 +28,7 @@ use crate::property_defs::{
 };
 use crate::rect::Rect;
 use crate::shapes::Markers;
-use crate::structure::{ClipPath, Mask, Symbol, Use};
+use crate::structure::Mask;
 use crate::surface_utils::{
     shared_surface::ExclusiveImageSurface, shared_surface::SharedImageSurface,
     shared_surface::SurfaceType,
@@ -323,7 +321,7 @@ impl DrawingCtx {
         }
 
         let node = clip_node.as_ref().unwrap();
-        let units = node.borrow_element().get_impl::<ClipPath>().get_units();
+        let units = borrow_element_as!(node, ClipPath).get_units();
 
         if units == CoordUnits::ObjectBoundingBox && bbox.rect.is_none() {
             // The node being clipped is empty / doesn't have a
@@ -480,12 +478,14 @@ impl DrawingCtx {
                 let mask = mask_value.0.get();
 
                 // The `filter` property does not apply to masks.
-                let filter =
-                    if node.is_element() && node.borrow_element().get_type() == ElementType::Mask {
-                        None
-                    } else {
-                        values.filter().0.get().cloned()
-                    };
+                let filter = if node.is_element() {
+                    match *node.borrow_element() {
+                        Element::Mask(_) => None,
+                        _ => values.filter().0.get().cloned(),
+                    }
+                } else {
+                    values.filter().0.get().cloned()
+                };
 
                 let UnitInterval(opacity) = values.opacity().0;
 
@@ -580,11 +580,11 @@ impl DrawingCtx {
                         if let Ok(acquired) = acquired_nodes.acquire(fragment) {
                             let mask_node = acquired.get();
 
-                            match mask_node.borrow_element().get_type() {
-                                ElementType::Mask => {
+                            match *mask_node.borrow_element() {
+                                Element::Mask(ref m) => {
                                     res = res.and_then(|bbox| {
                                         dc.generate_cairo_mask(
-                                            &mask_node.borrow_element().get_impl::<Mask>(),
+                                            &m,
                                             &mask_node,
                                             affines.for_temporary_surface,
                                             &bbox,
@@ -760,8 +760,8 @@ impl DrawingCtx {
                 let filter_node = acquired.get();
                 let filter_elt = filter_node.borrow_element();
 
-                match filter_elt.get_type() {
-                    ElementType::Filter => {
+                match *filter_elt {
+                    Element::Filter(_) => {
                         if filter_elt.is_in_error() {
                             return Ok(child_surface);
                         }
@@ -842,37 +842,28 @@ impl DrawingCtx {
 
                         assert!(node.is_element());
 
-                        had_paint_server = match node.borrow_element().get_type() {
-                            ElementType::LinearGradient => node
-                                .borrow_element()
-                                .get_impl::<LinearGradient>()
-                                .resolve_fallbacks_and_set_pattern(
-                                    &node,
-                                    acquired_nodes,
-                                    self,
-                                    opacity,
-                                    bbox,
-                                )?,
-                            ElementType::RadialGradient => node
-                                .borrow_element()
-                                .get_impl::<RadialGradient>()
-                                .resolve_fallbacks_and_set_pattern(
-                                    &node,
-                                    acquired_nodes,
-                                    self,
-                                    opacity,
-                                    bbox,
-                                )?,
-                            ElementType::Pattern => node
-                                .borrow_element()
-                                .get_impl::<Pattern>()
-                                .resolve_fallbacks_and_set_pattern(
-                                    &node,
-                                    acquired_nodes,
-                                    self,
-                                    opacity,
-                                    bbox,
-                                )?,
+                        had_paint_server = match *node.borrow_element() {
+                            Element::LinearGradient(ref g) => g.resolve_fallbacks_and_set_pattern(
+                                &node,
+                                acquired_nodes,
+                                self,
+                                opacity,
+                                bbox,
+                            )?,
+                            Element::RadialGradient(ref g) => g.resolve_fallbacks_and_set_pattern(
+                                &node,
+                                acquired_nodes,
+                                self,
+                                opacity,
+                                bbox,
+                            )?,
+                            Element::Pattern(ref p) => p.resolve_fallbacks_and_set_pattern(
+                                &node,
+                                acquired_nodes,
+                                self,
+                                opacity,
+                                bbox,
+                            )?,
                             _ => false,
                         }
                     }
@@ -1150,8 +1141,7 @@ impl DrawingCtx {
             }
         })?;
 
-        let elt = node.borrow_element();
-        let use_ = elt.get_impl::<Use>();
+        let use_ = borrow_element_as!(node, Use);
         let link = use_.get_link();
 
         if link.is_none() {
@@ -1191,41 +1181,42 @@ impl DrawingCtx {
         let child = acquired.get();
 
         // if it is a symbol
-        if child.is_element() && child.borrow_element().get_type() == ElementType::Symbol {
+        if child.is_element() {
             let elt = child.borrow_element();
-            let symbol = elt.get_impl::<Symbol>();
 
-            let clip_mode = if !values.is_overflow()
-                || (values.overflow() == Overflow::Visible
-                    && child.borrow_element().get_specified_values().is_overflow())
-            {
-                Some(ClipMode::ClipToVbox)
-            } else {
-                None
-            };
+            if let Element::Symbol(ref symbol) = *elt {
+                let clip_mode = if !values.is_overflow()
+                    || (values.overflow() == Overflow::Visible
+                        && elt.get_specified_values().is_overflow())
+                {
+                    Some(ClipMode::ClipToVbox)
+                } else {
+                    None
+                };
 
-            return self.with_discrete_layer(
-                node,
-                acquired_nodes,
-                values,
-                clipping,
-                &mut |an, dc| {
-                    let _params = dc.push_new_viewport(
-                        symbol.get_viewbox(),
-                        use_rect,
-                        symbol.get_preserve_aspect_ratio(),
-                        clip_mode,
-                    );
+                return self.with_discrete_layer(
+                    node,
+                    acquired_nodes,
+                    values,
+                    clipping,
+                    &mut |an, dc| {
+                        let _params = dc.push_new_viewport(
+                            symbol.get_viewbox(),
+                            use_rect,
+                            symbol.get_preserve_aspect_ratio(),
+                            clip_mode,
+                        );
 
-                    child.draw_children(
-                        an,
-                        &CascadedValues::new_from_values(&child, values),
-                        dc,
-                        clipping,
-                    )
-                },
-            );
-        }
+                        child.draw_children(
+                            an,
+                            &CascadedValues::new_from_values(&child, values),
+                            dc,
+                            clipping,
+                        )
+                    },
+                );
+            }
+        };
 
         // all other nodes
         let cr = self.get_cairo_context();
@@ -1301,15 +1292,12 @@ fn get_clip_in_user_and_object_space(
             acquired_nodes
                 .acquire(fragment)
                 .ok()
-                .filter(|a| a.get().borrow_element().get_type() == ElementType::ClipPath)
+                .filter(|a| is_element_of_type!(*a.get(), ClipPath))
         })
         .and_then(|acquired| {
             let clip_node = acquired.get().clone();
 
-            let units = clip_node
-                .borrow_element()
-                .get_impl::<ClipPath>()
-                .get_units();
+            let units = borrow_element_as!(clip_node, ClipPath).get_units();
 
             match units {
                 CoordUnits::UserSpaceOnUse => Some((Some(clip_node), None)),
diff --git a/rsvg_internals/src/element.rs b/rsvg_internals/src/element.rs
index 53335df7..2b306f0c 100644
--- a/rsvg_internals/src/element.rs
+++ b/rsvg_internals/src/element.rs
@@ -5,12 +5,13 @@
 //!
 //! [`create_element`]: fn.create_element.html
 
-use downcast_rs::*;
 use locale_config::{LanguageRange, Locale};
 use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
+use matches::matches;
 use once_cell::sync::Lazy;
 use std::collections::{HashMap, HashSet};
 use std::fmt;
+use std::ops::Deref;
 
 use crate::bbox::BoundingBox;
 use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
@@ -54,63 +55,6 @@ use crate::style::Style;
 use crate::text::{TRef, TSpan, Text};
 use crate::transform::Transform;
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub enum ElementType {
-    Circle,
-    ClipPath,
-    Ellipse,
-    Filter,
-    Group,
-    Image,
-    Line,
-    LinearGradient,
-    Link,
-    Marker,
-    Mask,
-    NonRendering,
-    Path,
-    Pattern,
-    Polygon,
-    Polyline,
-    RadialGradient,
-    Rect,
-    Stop,
-    Style,
-    Svg,
-    Switch,
-    Symbol,
-    Text,
-    TRef,
-    TSpan,
-    Use,
-
-    // Filter primitives, these start with "Fe" as element names are e.g. "feBlend"
-    FeBlend,
-    FeColorMatrix,
-    FeComponentTransfer,
-    FeComposite,
-    FeConvolveMatrix,
-    FeDiffuseLighting,
-    FeDisplacementMap,
-    FeDistantLight,
-    FeFlood,
-    FeFuncA,
-    FeFuncB,
-    FeFuncG,
-    FeFuncR,
-    FeGaussianBlur,
-    FeImage,
-    FeMerge,
-    FeMergeNode,
-    FeMorphology,
-    FeOffset,
-    FePointLight,
-    FeSpecularLighting,
-    FeSpotLight,
-    FeTile,
-    FeTurbulence,
-}
-
 // After creating/parsing a Element, it will be in a success or an error state.
 // We represent this with a Result, aliased as a ElementResult.  There is no
 // extra information for the Ok case; all the interesting stuff is in the
@@ -134,7 +78,7 @@ pub enum ElementType {
 pub type ElementResult = Result<(), ElementError>;
 
 /// The basic trait that all elements must implement
-pub trait ElementTrait: Downcast {
+pub trait ElementTrait {
     /// Sets per-element attributes from the `pbag`
     ///
     /// Each element is supposed to iterate the `pbag`, and parse any attributes it needs.
@@ -157,18 +101,9 @@ pub trait ElementTrait: Downcast {
         // by default elements don't draw themselves
         Ok(draw_ctx.empty_bbox())
     }
-
-    /// Returns the FilterEffect trait if this element is a filter primitive
-    fn as_filter_effect(&self) -> Option<&dyn FilterEffect> {
-        None
-    }
 }
 
-impl_downcast!(ElementTrait);
-
-/// Contents of an element node in the DOM
-pub struct Element {
-    element_type: ElementType,
+pub struct ElementInner<T: ElementTrait> {
     element_name: QualName,
     id: Option<String>,    // id attribute from XML element
     class: Option<String>, // class attribute from XML element
@@ -179,51 +114,39 @@ pub struct Element {
     values: ComputedValues,
     cond: bool,
     style_attr: String,
-    element_impl: Box<dyn ElementTrait>,
+    pub element_impl: T,
 }
 
-impl Element {
-    pub fn get_type(&self) -> ElementType {
-        self.element_type
-    }
-
-    pub fn get_impl<T: ElementTrait>(&self) -> &T {
-        if let Some(t) = (&self.element_impl).downcast_ref::<T>() {
-            t
-        } else {
-            panic!("could not downcast");
-        }
-    }
-
-    pub fn element_name(&self) -> &QualName {
+impl<T: ElementTrait> ElementInner<T> {
+    fn element_name(&self) -> &QualName {
         &self.element_name
     }
 
-    pub fn get_id(&self) -> Option<&str> {
+    fn get_id(&self) -> Option<&str> {
         self.id.as_ref().map(String::as_str)
     }
 
-    pub fn get_class(&self) -> Option<&str> {
+    fn get_class(&self) -> Option<&str> {
         self.class.as_ref().map(String::as_str)
     }
 
-    pub fn get_specified_values(&self) -> &SpecifiedValues {
+    fn get_specified_values(&self) -> &SpecifiedValues {
         &self.specified_values
     }
 
-    pub fn get_computed_values(&self) -> &ComputedValues {
+    fn get_computed_values(&self) -> &ComputedValues {
         &self.values
     }
 
-    pub fn set_computed_values(&mut self, values: &ComputedValues) {
+    fn set_computed_values(&mut self, values: &ComputedValues) {
         self.values = values.clone();
     }
 
-    pub fn get_cond(&self) -> bool {
+    fn get_cond(&self) -> bool {
         self.cond
     }
 
-    pub fn get_transform(&self) -> Transform {
+    fn get_transform(&self) -> Transform {
         self.transform
     }
 
@@ -257,9 +180,9 @@ impl Element {
     fn set_conditional_processing_attributes(
         &mut self,
         pbag: &PropertyBag<'_>,
-        locale: &Locale,
     ) -> Result<(), ElementError> {
         let mut cond = self.cond;
+        let locale = locale_from_environment();
 
         for (attr, value) in pbag.iter() {
             let mut parse = || -> Result<_, ValueErrorKind> {
@@ -275,7 +198,7 @@ impl Element {
                     }
 
                     expanded_name!("", "systemLanguage") if cond => {
-                        cond = SystemLanguage::from_attribute(value, locale)
+                        cond = SystemLanguage::from_attribute(value, &locale)
                             .map(|SystemLanguage(res)| res)?;
                     }
 
@@ -326,7 +249,7 @@ impl Element {
     }
 
     // Applies a style declaration to the node's specified_values
-    pub fn apply_style_declaration(&mut self, declaration: &Declaration, origin: Origin) {
+    fn apply_style_declaration(&mut self, declaration: &Declaration, origin: Origin) {
         self.specified_values.set_property_from_declaration(
             declaration,
             origin,
@@ -335,7 +258,7 @@ impl Element {
     }
 
     /// Applies CSS styles from the saved value of the "style" attribute
-    pub fn set_style_attribute(&mut self) {
+    fn set_style_attribute(&mut self) {
         if !self.style_attr.is_empty() {
             if let Err(e) = self.specified_values.parse_style_declarations(
                 self.style_attr.as_str(),
@@ -355,11 +278,11 @@ impl Element {
         self.result = Err(error);
     }
 
-    pub fn is_in_error(&self) -> bool {
+    fn is_in_error(&self) -> bool {
         self.result.is_err()
     }
 
-    pub fn draw(
+    fn draw(
         &self,
         node: &Node,
         acquired_nodes: &mut AcquiredNodes,
@@ -368,7 +291,7 @@ impl Element {
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         if !self.is_in_error() {
-            draw_ctx.with_saved_transform(Some(self.transform), &mut |dc| {
+            draw_ctx.with_saved_transform(Some(self.get_transform()), &mut |dc| {
                 self.element_impl
                     .draw(node, acquired_nodes, cascaded, dc, clipping)
             })
@@ -379,37 +302,311 @@ impl Element {
             Ok(draw_ctx.empty_bbox())
         }
     }
+}
+
+impl<T: ElementTrait> fmt::Display for ElementInner<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.element_name().local)?;
+        write!(f, " id={}", self.get_id().unwrap_or("None"))?;
+        Ok(())
+    }
+}
+
+impl<T: ElementTrait> Deref for ElementInner<T> {
+    type Target = T;
+
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        &self.element_impl
+    }
+}
+
+/// Contents of an element node in the DOM
+/// This enum uses `Box<ElementInner>` in order to make each `Element`
+/// the size of a pointer.
+
+pub enum Element {
+    Circle(Box<ElementInner<Circle>>),
+    ClipPath(Box<ElementInner<ClipPath>>),
+    Ellipse(Box<ElementInner<Ellipse>>),
+    Filter(Box<ElementInner<Filter>>),
+    Group(Box<ElementInner<Group>>),
+    Image(Box<ElementInner<Image>>),
+    Line(Box<ElementInner<Line>>),
+    LinearGradient(Box<ElementInner<LinearGradient>>),
+    Link(Box<ElementInner<Link>>),
+    Marker(Box<ElementInner<Marker>>),
+    Mask(Box<ElementInner<Mask>>),
+    NonRendering(Box<ElementInner<NonRendering>>),
+    Path(Box<ElementInner<Path>>),
+    Pattern(Box<ElementInner<Pattern>>),
+    Polygon(Box<ElementInner<Polygon>>),
+    Polyline(Box<ElementInner<Polyline>>),
+    RadialGradient(Box<ElementInner<RadialGradient>>),
+    Rect(Box<ElementInner<Rect>>),
+    Stop(Box<ElementInner<Stop>>),
+    Style(Box<ElementInner<Style>>),
+    Svg(Box<ElementInner<Svg>>),
+    Switch(Box<ElementInner<Switch>>),
+    Symbol(Box<ElementInner<Symbol>>),
+    Text(Box<ElementInner<Text>>),
+    TRef(Box<ElementInner<TRef>>),
+    TSpan(Box<ElementInner<TSpan>>),
+    Use(Box<ElementInner<Use>>),
+
+    // Filter primitives, these start with "Fe" as element names are e.g. "feBlend"
+    FeBlend(Box<ElementInner<FeBlend>>),
+    FeColorMatrix(Box<ElementInner<FeColorMatrix>>),
+    FeComponentTransfer(Box<ElementInner<FeComponentTransfer>>),
+    FeComposite(Box<ElementInner<FeComposite>>),
+    FeConvolveMatrix(Box<ElementInner<FeConvolveMatrix>>),
+    FeDiffuseLighting(Box<ElementInner<FeDiffuseLighting>>),
+    FeDisplacementMap(Box<ElementInner<FeDisplacementMap>>),
+    FeDistantLight(Box<ElementInner<FeDistantLight>>),
+    FeFlood(Box<ElementInner<FeFlood>>),
+    FeFuncA(Box<ElementInner<FeFuncA>>),
+    FeFuncB(Box<ElementInner<FeFuncB>>),
+    FeFuncG(Box<ElementInner<FeFuncG>>),
+    FeFuncR(Box<ElementInner<FeFuncR>>),
+    FeGaussianBlur(Box<ElementInner<FeGaussianBlur>>),
+    FeImage(Box<ElementInner<FeImage>>),
+    FeMerge(Box<ElementInner<FeMerge>>),
+    FeMergeNode(Box<ElementInner<FeMergeNode>>),
+    FeMorphology(Box<ElementInner<FeMorphology>>),
+    FeOffset(Box<ElementInner<FeOffset>>),
+    FePointLight(Box<ElementInner<FePointLight>>),
+    FeSpecularLighting(Box<ElementInner<FeSpecularLighting>>),
+    FeSpotLight(Box<ElementInner<FeSpotLight>>),
+    FeTile(Box<ElementInner<FeTile>>),
+    FeTurbulence(Box<ElementInner<FeTurbulence>>),
+}
+
+macro_rules! call_inner {
+    // end recursion, call the method
+    ($element:ident, $method:ident [$($args:expr),*]) => {
+        match $element {
+            Element::Circle(i) => i.$method($($args),*),
+            Element::ClipPath(i) => i.$method($($args),*),
+            Element::Ellipse(i) => i.$method($($args),*),
+            Element::Filter(i) => i.$method($($args),*),
+            Element::Group(i) => i.$method($($args),*),
+            Element::Image(i) => i.$method($($args),*),
+            Element::Line(i) => i.$method($($args),*),
+            Element::LinearGradient(i) => i.$method($($args),*),
+            Element::Link(i) => i.$method($($args),*),
+            Element::Marker(i) => i.$method($($args),*),
+            Element::Mask(i) => i.$method($($args),*),
+            Element::NonRendering(i) => i.$method($($args),*),
+            Element::Path(i) => i.$method($($args),*),
+            Element::Pattern(i) => i.$method($($args),*),
+            Element::Polygon(i) => i.$method($($args),*),
+            Element::Polyline(i) => i.$method($($args),*),
+            Element::RadialGradient(i) => i.$method($($args),*),
+            Element::Rect(i) => i.$method($($args),*),
+            Element::Stop(i) => i.$method($($args),*),
+            Element::Style(i) => i.$method($($args),*),
+            Element::Svg(i) => i.$method($($args),*),
+            Element::Switch(i) => i.$method($($args),*),
+            Element::Symbol(i) => i.$method($($args),*),
+            Element::Text(i) => i.$method($($args),*),
+            Element::TRef(i) => i.$method($($args),*),
+            Element::TSpan(i) => i.$method($($args),*),
+            Element::Use(i) => i.$method($($args),*),
+            Element::FeBlend(i) => i.$method($($args),*),
+            Element::FeColorMatrix(i) => i.$method($($args),*),
+            Element::FeComponentTransfer(i) => i.$method($($args),*),
+            Element::FeComposite(i) => i.$method($($args),*),
+            Element::FeConvolveMatrix(i) => i.$method($($args),*),
+            Element::FeDiffuseLighting(i) => i.$method($($args),*),
+            Element::FeDisplacementMap(i) => i.$method($($args),*),
+            Element::FeDistantLight(i) => i.$method($($args),*),
+            Element::FeFlood(i) => i.$method($($args),*),
+            Element::FeFuncA(i) => i.$method($($args),*),
+            Element::FeFuncB(i) => i.$method($($args),*),
+            Element::FeFuncG(i) => i.$method($($args),*),
+            Element::FeFuncR(i) => i.$method($($args),*),
+            Element::FeGaussianBlur(i) => i.$method($($args),*),
+            Element::FeImage(i) => i.$method($($args),*),
+            Element::FeMerge(i) => i.$method($($args),*),
+            Element::FeMergeNode(i) => i.$method($($args),*),
+            Element::FeMorphology(i) => i.$method($($args),*),
+            Element::FeOffset(i) => i.$method($($args),*),
+            Element::FePointLight(i) => i.$method($($args),*),
+            Element::FeSpecularLighting(i) => i.$method($($args),*),
+            Element::FeSpotLight(i) => i.$method($($args),*),
+            Element::FeTile(i) => i.$method($($args),*),
+            Element::FeTurbulence(i) => i.$method($($args),*),
+        }
+    };
+
+    // munch munch
+    ($element:ident, $method:ident [$($args:expr),*] $arg:expr, $($rest:tt)*) => {
+        call_inner!($element, $method [$($args,)*$arg] $($rest)*)
+    };
+
+    // entry point with args
+    ($element:ident, $method:ident, $arg:expr, $($args:expr),*) => {
+        call_inner!($element, $method [$arg] $($args,)*)
+    };
+
+    // entry point with one arg
+    ($element:ident, $method:ident, $arg:expr) => {
+        call_inner!($element, $method [$arg])
+    };
+
+    // entry point without args
+    ($element:ident, $method:ident) => {
+        call_inner!($element, $method [])
+    };
+}
+
+impl Element {
+    pub fn element_name(&self) -> &QualName {
+        call_inner!(self, element_name)
+    }
+
+    pub fn get_id(&self) -> Option<&str> {
+        call_inner!(self, get_id)
+    }
+
+    pub fn get_class(&self) -> Option<&str> {
+        call_inner!(self, get_class)
+    }
+
+    pub fn get_specified_values(&self) -> &SpecifiedValues {
+        call_inner!(self, get_specified_values)
+    }
+
+    pub fn get_computed_values(&self) -> &ComputedValues {
+        call_inner!(self, get_computed_values)
+    }
+
+    pub fn set_computed_values(&mut self, values: &ComputedValues) {
+        call_inner!(self, set_computed_values, values);
+    }
+
+    pub fn get_cond(&self) -> bool {
+        call_inner!(self, get_cond)
+    }
+
+    pub fn get_transform(&self) -> Transform {
+        call_inner!(self, get_transform)
+    }
+
+    fn save_style_attribute(&mut self, pbag: &PropertyBag<'_>) {
+        call_inner!(self, save_style_attribute, pbag);
+    }
+
+    fn set_transform_attribute(&mut self, pbag: &PropertyBag<'_>) -> Result<(), ElementError> {
+        call_inner!(self, set_transform_attribute, pbag)
+    }
+
+    fn set_conditional_processing_attributes(
+        &mut self,
+        pbag: &PropertyBag<'_>,
+    ) -> Result<(), ElementError> {
+        call_inner!(self, set_conditional_processing_attributes, pbag)
+    }
+
+    fn set_presentation_attributes(&mut self, pbag: &PropertyBag<'_>) -> Result<(), ElementError> {
+        call_inner!(self, set_presentation_attributes, pbag)
+    }
+
+    fn set_element_specific_attributes(
+        &mut self,
+        pbag: &PropertyBag<'_>,
+    ) -> Result<(), ElementError> {
+        call_inner!(self, set_element_specific_attributes, pbag)
+    }
+
+    fn set_overridden_properties(&mut self) {
+        call_inner!(self, set_overridden_properties)
+    }
+
+    pub fn apply_style_declaration(&mut self, declaration: &Declaration, origin: Origin) {
+        call_inner!(self, apply_style_declaration, declaration, origin)
+    }
+
+    pub fn set_style_attribute(&mut self) {
+        call_inner!(self, set_style_attribute);
+    }
+
+    fn set_error(&mut self, error: ElementError) {
+        call_inner!(self, set_error, error);
+    }
+
+    pub fn is_in_error(&self) -> bool {
+        call_inner!(self, is_in_error)
+    }
+
+    pub fn draw(
+        &self,
+        node: &Node,
+        acquired_nodes: &mut AcquiredNodes,
+        cascaded: &CascadedValues<'_>,
+        draw_ctx: &mut DrawingCtx,
+        clipping: bool,
+    ) -> Result<BoundingBox, RenderingError> {
+        call_inner!(
+            self,
+            draw,
+            node,
+            acquired_nodes,
+            cascaded,
+            draw_ctx,
+            clipping
+        )
+    }
 
     pub fn as_filter_effect(&self) -> Option<&dyn FilterEffect> {
-        self.element_impl.as_filter_effect()
+        match self {
+            Element::FeBlend(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeColorMatrix(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeComponentTransfer(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeComposite(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeConvolveMatrix(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeDiffuseLighting(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeDisplacementMap(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeFlood(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeGaussianBlur(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeImage(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeMerge(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeMorphology(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeOffset(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeSpecularLighting(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeTile(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            Element::FeTurbulence(ref fe) => Some(&fe.element_impl as &dyn FilterEffect),
+            _ => None,
+        }
     }
 
     /// Returns whether an element of a particular type is only accessed by reference
     // from other elements' attributes.  The element could in turn cause other nodes
     // to get referenced, potentially causing reference cycles.
     pub fn is_accessed_by_reference(&self) -> bool {
-        use ElementType::*;
-
-        match self.element_type {
-            ClipPath | Filter | LinearGradient | Marker | Mask | Pattern | RadialGradient => true,
-            _ => false,
-        }
+        matches!(
+            self,
+            Element::ClipPath(_) |
+            Element::Filter(_) |
+            Element::LinearGradient(_) |
+            Element::Marker(_) |
+            Element::Mask(_) |
+            Element::Pattern(_) |
+            Element::RadialGradient(_)
+        )
     }
 }
 
 impl fmt::Display for Element {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", self.get_type())?;
-        write!(f, " id={}", self.get_id().unwrap_or("None"))?;
-        Ok(())
+        call_inner!(self, fmt, f)
     }
 }
 
 macro_rules! e {
     ($name:ident, $element_type:ident) => {
         pub fn $name(element_name: &QualName, id: Option<&str>, class: Option<&str>) -> Element {
-            Element {
-                element_type: ElementType::$element_type,
+            Element::$element_type(Box::new(ElementInner {
                 element_name: element_name.clone(),
                 id: id.map(str::to_string),
                 class: class.map(str::to_string),
@@ -420,8 +617,8 @@ macro_rules! e {
                 values: ComputedValues::default(),
                 cond: true,
                 style_attr: String::new(),
-                element_impl: Box::new(<$element_type>::default()),
-            }
+                element_impl: <$element_type>::default(),
+            }))
         }
     };
 }
@@ -654,9 +851,7 @@ pub fn create_element(name: &QualName, pbag: &PropertyBag) -> Element {
 
     if let Err(e) = element
         .set_transform_attribute(pbag)
-        .and_then(|_| {
-            element.set_conditional_processing_attributes(pbag, &locale_from_environment())
-        })
+        .and_then(|_| element.set_conditional_processing_attributes(pbag))
         .and_then(|_| element.set_element_specific_attributes(pbag))
         .and_then(|_| element.set_presentation_attributes(pbag))
     {
diff --git a/rsvg_internals/src/filters/blend.rs b/rsvg_internals/src/filters/blend.rs
index 2038b9ee..8f3e9bb2 100755
--- a/rsvg_internals/src/filters/blend.rs
+++ b/rsvg_internals/src/filters/blend.rs
@@ -53,8 +53,6 @@ impl Default for FeBlend {
 }
 
 impl ElementTrait for FeBlend {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/color_matrix.rs b/rsvg_internals/src/filters/color_matrix.rs
index 8130abf8..af178ea2 100644
--- a/rsvg_internals/src/filters/color_matrix.rs
+++ b/rsvg_internals/src/filters/color_matrix.rs
@@ -52,8 +52,6 @@ impl Default for FeColorMatrix {
 
 #[rustfmt::skip]
 impl ElementTrait for FeColorMatrix {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/component_transfer.rs 
b/rsvg_internals/src/filters/component_transfer.rs
index 940db4e8..d78b0e5c 100644
--- a/rsvg_internals/src/filters/component_transfer.rs
+++ b/rsvg_internals/src/filters/component_transfer.rs
@@ -5,7 +5,7 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::*;
 use crate::node::{Node, NodeBorrow};
 use crate::number_list::{NumberList, NumberListLength};
@@ -35,9 +35,6 @@ impl Default for FeComponentTransfer {
 }
 
 impl ElementTrait for FeComponentTransfer {
-    impl_node_as_filter_effect!();
-
-    #[inline]
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)
     }
@@ -251,17 +248,33 @@ func_x!(FeFuncB, Channel::B);
 func_x!(FeFuncA, Channel::A);
 
 macro_rules! func_or_default {
-    ($func_node:ident, $func_type:ty, $func_data:ident, $func_default:ident) => {
+    ($func_node:ident, $func_type:ident, $func_data:ident, $func_default:ident) => {
         match $func_node {
             Some(ref f) => {
                 $func_data = f.borrow_element();
-                $func_data.get_impl::<$func_type>()
+                match *$func_data {
+                    Element::$func_type(ref e) => &*e,
+                    _ => unreachable!(),
+                }
             }
             _ => &$func_default,
         };
     };
 }
 
+macro_rules! get_func_x_node {
+    ($func_node:ident, $func_type:ident, $channel:expr) => {
+        $func_node
+            .children()
+            .rev()
+            .filter(|c| c.is_element())
+            .find(|c| match *c.borrow_element() {
+                Element::$func_type(ref f) => f.channel() == $channel,
+                _ => false,
+            })
+    };
+}
+
 impl FilterEffect for FeComponentTransfer {
     fn render(
         &self,
@@ -284,21 +297,10 @@ impl FilterEffect for FeComponentTransfer {
             input.surface().surface_type(),
         )?;
 
-        // Get a node for every pixel component.
-        fn get_node<F>(node: &Node, element_type: ElementType, channel: Channel) -> Option<Node>
-        where
-            F: FeComponentTransferFunc + ElementTrait,
-        {
-            node.children()
-                .rev()
-                .filter(|c| c.is_element() && c.borrow_element().get_type() == element_type)
-                .find(|c| c.borrow_element().get_impl::<F>().channel() == channel)
-        };
-
-        let func_r_node = get_node::<FeFuncR>(node, ElementType::FeFuncR, Channel::R);
-        let func_g_node = get_node::<FeFuncG>(node, ElementType::FeFuncG, Channel::G);
-        let func_b_node = get_node::<FeFuncB>(node, ElementType::FeFuncB, Channel::B);
-        let func_a_node = get_node::<FeFuncA>(node, ElementType::FeFuncA, Channel::A);
+        let func_r_node = get_func_x_node!(node, FeFuncR, Channel::R);
+        let func_g_node = get_func_x_node!(node, FeFuncG, Channel::G);
+        let func_b_node = get_func_x_node!(node, FeFuncB, Channel::B);
+        let func_a_node = get_func_x_node!(node, FeFuncA, Channel::A);
 
         for node in [&func_r_node, &func_g_node, &func_b_node, &func_a_node]
             .iter()
diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs
index aa1ae921..6a5c9b6d 100644
--- a/rsvg_internals/src/filters/composite.rs
+++ b/rsvg_internals/src/filters/composite.rs
@@ -51,8 +51,6 @@ impl Default for FeComposite {
 }
 
 impl ElementTrait for FeComposite {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/context.rs b/rsvg_internals/src/filters/context.rs
index a14033d4..d5f109b5 100644
--- a/rsvg_internals/src/filters/context.rs
+++ b/rsvg_internals/src/filters/context.rs
@@ -6,7 +6,6 @@ use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::{DrawingCtx, ViewParams};
-use crate::filter::Filter;
 use crate::node::{Node, NodeBorrow};
 use crate::paint_server::PaintServer;
 use crate::parsers::CustomIdent;
@@ -112,8 +111,7 @@ impl FilterContext {
         // However, with userSpaceOnUse it's still possible to create images with a filter.
         let bbox_rect = node_bbox.rect.unwrap_or_default();
 
-        let elt = filter_node.borrow_element();
-        let filter = elt.get_impl::<Filter>();
+        let filter = borrow_element_as!(filter_node, Filter);
 
         let affine = match filter.get_filter_units() {
             CoordUnits::UserSpaceOnUse => draw_transform,
@@ -257,11 +255,9 @@ impl FilterContext {
 
     /// Pushes the viewport size based on the value of `primitiveUnits`.
     pub fn get_view_params(&self, draw_ctx: &mut DrawingCtx) -> ViewParams {
-        let elt = self.node.borrow_element();
-        let filter = elt.get_impl::<Filter>();
-
         // See comments in compute_effects_region() for how this works.
-        if filter.get_primitive_units() == CoordUnits::ObjectBoundingBox {
+        let units = borrow_element_as!(self.node, Filter).get_primitive_units();
+        if units == CoordUnits::ObjectBoundingBox {
             draw_ctx.push_view_box(1.0, 1.0)
         } else {
             draw_ctx.get_view_params()
diff --git a/rsvg_internals/src/filters/convolve_matrix.rs b/rsvg_internals/src/filters/convolve_matrix.rs
index 76b3b7c1..74b08881 100644
--- a/rsvg_internals/src/filters/convolve_matrix.rs
+++ b/rsvg_internals/src/filters/convolve_matrix.rs
@@ -55,8 +55,6 @@ impl Default for FeConvolveMatrix {
 }
 
 impl ElementTrait for FeConvolveMatrix {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/displacement_map.rs b/rsvg_internals/src/filters/displacement_map.rs
index aa8ba833..99882d66 100644
--- a/rsvg_internals/src/filters/displacement_map.rs
+++ b/rsvg_internals/src/filters/displacement_map.rs
@@ -46,8 +46,6 @@ impl Default for FeDisplacementMap {
 }
 
 impl ElementTrait for FeDisplacementMap {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/flood.rs b/rsvg_internals/src/filters/flood.rs
index 7cb4da94..61fece23 100644
--- a/rsvg_internals/src/filters/flood.rs
+++ b/rsvg_internals/src/filters/flood.rs
@@ -23,9 +23,6 @@ impl Default for FeFlood {
 }
 
 impl ElementTrait for FeFlood {
-    impl_node_as_filter_effect!();
-
-    #[inline]
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)
     }
diff --git a/rsvg_internals/src/filters/gaussian_blur.rs b/rsvg_internals/src/filters/gaussian_blur.rs
index d19ad332..2083ff61 100644
--- a/rsvg_internals/src/filters/gaussian_blur.rs
+++ b/rsvg_internals/src/filters/gaussian_blur.rs
@@ -43,8 +43,6 @@ impl Default for FeGaussianBlur {
 }
 
 impl ElementTrait for FeGaussianBlur {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs
index 4bb4d960..06e8fe58 100644
--- a/rsvg_internals/src/filters/image.rs
+++ b/rsvg_internals/src/filters/image.rs
@@ -111,8 +111,6 @@ impl FeImage {
 }
 
 impl ElementTrait for FeImage {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/light/lighting.rs b/rsvg_internals/src/filters/light/lighting.rs
index 0df64e1c..4ed96a37 100644
--- a/rsvg_internals/src/filters/light/lighting.rs
+++ b/rsvg_internals/src/filters/light/lighting.rs
@@ -1,20 +1,19 @@
-use std::cmp::max;
-
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
+use matches::matches;
 use nalgebra::Vector3;
 use num_traits::identities::Zero;
 use rayon::prelude::*;
+use std::cmp::max;
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::*;
 use crate::filters::{
     context::{FilterContext, FilterOutput, FilterResult},
     light::{
         bottom_left_normal, bottom_right_normal, bottom_row_normal, interior_normal,
-        left_column_normal, light_source::FeDistantLight, light_source::FePointLight,
-        light_source::FeSpotLight, light_source::LightSource, right_column_normal, top_left_normal,
+        left_column_normal, light_source::LightSource, right_column_normal, top_left_normal,
         top_right_normal, top_row_normal, Normal,
     },
     FilterEffect, FilterError, PrimitiveWithInput,
@@ -94,8 +93,6 @@ impl Default for FeDiffuseLighting {
 }
 
 impl ElementTrait for FeDiffuseLighting {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.common.set_atts(pbag)?;
 
@@ -163,8 +160,6 @@ impl Default for FeSpecularLighting {
 }
 
 impl ElementTrait for FeSpecularLighting {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.common.set_atts(pbag)?;
 
@@ -492,13 +487,7 @@ impl_lighting_filter!(FeSpecularLighting, specular_alpha);
 
 fn find_light_source(node: &Node, ctx: &FilterContext) -> Result<LightSource, FilterError> {
     let mut light_sources = node.children().rev().filter(|c| {
-        c.is_element()
-            && match c.borrow_element().get_type() {
-                ElementType::FeDistantLight
-                | ElementType::FePointLight
-                | ElementType::FeSpotLight => true,
-                _ => false,
-            }
+        c.is_element() && matches!(*c.borrow_element(), Element::FeDistantLight(_) | 
Element::FePointLight(_) | Element::FeSpotLight(_))
     });
 
     let node = light_sources.next();
@@ -513,10 +502,10 @@ fn find_light_source(node: &Node, ctx: &FilterContext) -> Result<LightSource, Fi
         return Err(FilterError::ChildNodeInError);
     }
 
-    let light_source = match elt.get_type() {
-        ElementType::FeDistantLight => elt.get_impl::<FeDistantLight>().transform(ctx),
-        ElementType::FePointLight => elt.get_impl::<FePointLight>().transform(ctx),
-        ElementType::FeSpotLight => elt.get_impl::<FeSpotLight>().transform(ctx),
+    let light_source = match *elt {
+        Element::FeDistantLight(ref l) => l.transform(ctx),
+        Element::FePointLight(ref l) => l.transform(ctx),
+        Element::FeSpotLight(ref l) => l.transform(ctx),
         _ => unreachable!(),
     };
 
diff --git a/rsvg_internals/src/filters/merge.rs b/rsvg_internals/src/filters/merge.rs
index b255c854..80d6471c 100644
--- a/rsvg_internals/src/filters/merge.rs
+++ b/rsvg_internals/src/filters/merge.rs
@@ -2,7 +2,7 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::node::{Node, NodeBorrow};
 use crate::parsers::ParseValue;
 use crate::property_bag::PropertyBag;
@@ -34,9 +34,6 @@ impl Default for FeMerge {
 }
 
 impl ElementTrait for FeMerge {
-    impl_node_as_filter_effect!();
-
-    #[inline]
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)
     }
@@ -88,38 +85,33 @@ impl FilterEffect for FeMerge {
     ) -> Result<FilterResult, FilterError> {
         // Compute the filter bounds, taking each child node's input into account.
         let mut bounds = self.base.get_bounds(ctx, node.parent().as_ref())?;
-        for child in node
-            .children()
-            .filter(|c| c.is_element() && c.borrow_element().get_type() == ElementType::FeMergeNode)
-        {
+        for child in node.children().filter(|c| c.is_element()) {
             let elt = child.borrow_element();
 
             if elt.is_in_error() {
                 return Err(FilterError::ChildNodeInError);
             }
 
-            let input = ctx.get_input(
-                acquired_nodes,
-                draw_ctx,
-                elt.get_impl::<FeMergeNode>().in_.as_ref(),
-            )?;
-            bounds = bounds.add_input(&input);
+            if let Element::FeMergeNode(ref merge_node) = *elt {
+                let input = ctx.get_input(acquired_nodes, draw_ctx, merge_node.in_.as_ref())?;
+                bounds = bounds.add_input(&input);
+            }
         }
+
         let bounds = bounds.into_irect(draw_ctx);
 
         // Now merge them all.
         let mut output_surface = None;
-        for child in node
-            .children()
-            .filter(|c| c.is_element() && c.borrow_element().get_type() == ElementType::FeMergeNode)
-        {
-            output_surface = Some(child.borrow_element().get_impl::<FeMergeNode>().render(
-                ctx,
-                acquired_nodes,
-                draw_ctx,
-                bounds,
-                output_surface,
-            )?);
+        for child in node.children().filter(|c| c.is_element()) {
+            if let Element::FeMergeNode(ref merge_node) = *child.borrow_element() {
+                output_surface = Some(merge_node.render(
+                    ctx,
+                    acquired_nodes,
+                    draw_ctx,
+                    bounds,
+                    output_surface,
+                )?);
+            }
         }
 
         let surface = match output_surface {
diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs
index 7ad2da82..c8e52bfb 100644
--- a/rsvg_internals/src/filters/mod.rs
+++ b/rsvg_internals/src/filters/mod.rs
@@ -9,9 +9,8 @@ use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::{ParseError, RenderingError};
-use crate::filter::Filter;
 use crate::length::*;
 use crate::node::{CascadedValues, Node, NodeBorrow};
 use crate::parsers::{CustomIdent, Parse, ParseValue};
@@ -51,14 +50,6 @@ pub trait FilterEffect: ElementTrait {
     fn is_affected_by_color_interpolation_filters(&self) -> bool;
 }
 
-macro_rules! impl_node_as_filter_effect {
-    () => (
-        fn as_filter_effect(&self) -> Option<&dyn FilterEffect> {
-            Some(self)
-        }
-    )
-}
-
 pub mod blend;
 pub mod color_matrix;
 pub mod component_transfer;
@@ -146,12 +137,9 @@ impl Primitive {
         let primitiveunits = parent
             .and_then(|parent| {
                 assert!(parent.is_element());
-                let parent_elt = parent.borrow_element();
-
-                if parent_elt.get_type() == ElementType::Filter {
-                    Some(parent_elt.get_impl::<Filter>().get_primitive_units())
-                } else {
-                    None
+                match *parent.borrow_element() {
+                    Element::Filter(ref f) => Some(f.get_primitive_units()),
+                    _ => None,
                 }
             })
             .unwrap_or(CoordUnits::UserSpaceOnUse);
@@ -287,7 +275,7 @@ pub fn render(
     node_bbox: BoundingBox,
 ) -> Result<SharedImageSurface, RenderingError> {
     let filter_node = &*filter_node;
-    assert_eq!(filter_node.borrow_element().get_type(), ElementType::Filter);
+    assert!(is_element_of_type!(filter_node, Filter));
     assert!(!filter_node.borrow_element().is_in_error());
 
     let mut filter_ctx = FilterContext::new(
diff --git a/rsvg_internals/src/filters/morphology.rs b/rsvg_internals/src/filters/morphology.rs
index 46a0ce32..1ab57342 100644
--- a/rsvg_internals/src/filters/morphology.rs
+++ b/rsvg_internals/src/filters/morphology.rs
@@ -46,8 +46,6 @@ impl Default for FeMorphology {
 }
 
 impl ElementTrait for FeMorphology {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/offset.rs b/rsvg_internals/src/filters/offset.rs
index 73da2261..817955cb 100644
--- a/rsvg_internals/src/filters/offset.rs
+++ b/rsvg_internals/src/filters/offset.rs
@@ -30,8 +30,6 @@ impl Default for FeOffset {
 }
 
 impl ElementTrait for FeOffset {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/filters/tile.rs b/rsvg_internals/src/filters/tile.rs
index 1f92a817..1d2ca239 100644
--- a/rsvg_internals/src/filters/tile.rs
+++ b/rsvg_internals/src/filters/tile.rs
@@ -23,8 +23,6 @@ impl Default for FeTile {
 }
 
 impl ElementTrait for FeTile {
-    impl_node_as_filter_effect!();
-
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)
     }
diff --git a/rsvg_internals/src/filters/turbulence.rs b/rsvg_internals/src/filters/turbulence.rs
index 43b4f6ef..b5e14a49 100644
--- a/rsvg_internals/src/filters/turbulence.rs
+++ b/rsvg_internals/src/filters/turbulence.rs
@@ -57,9 +57,6 @@ impl Default for FeTurbulence {
 }
 
 impl ElementTrait for FeTurbulence {
-    impl_node_as_filter_effect!();
-
-    #[inline]
     fn set_atts(&mut self, pbag: &PropertyBag<'_>) -> ElementResult {
         self.base.set_atts(pbag)?;
 
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index 42f3728f..184c5834 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -4,6 +4,7 @@ use cssparser::Parser;
 use markup5ever::{
     expanded_name, local_name, namespace_url, ns, ExpandedName, LocalName, Namespace,
 };
+use matches::matches;
 use std::cell::RefCell;
 
 use crate::allowed_url::Fragment;
@@ -11,7 +12,7 @@ use crate::bbox::*;
 use crate::coord_units::CoordUnits;
 use crate::document::{AcquiredNodes, NodeStack};
 use crate::drawing_ctx::{DrawingCtx, ViewParams};
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::*;
 use crate::length::*;
 use crate::node::{CascadedValues, Node, NodeBorrow};
@@ -462,33 +463,26 @@ impl UnresolvedGradient {
     /// Looks for <stop> children inside a linearGradient or radialGradient node,
     /// and adds their info to the UnresolvedGradient &self.
     fn add_color_stops_from_node(&mut self, node: &Node) {
-        let element_type = node.borrow_element().get_type();
-
         assert!(
-            element_type == ElementType::LinearGradient
-                || element_type == ElementType::RadialGradient
+            matches!(*node.borrow_element(), Element::LinearGradient(_) | Element::RadialGradient(_))
         );
 
-        for child_node in node.children().filter(|c| c.is_element()) {
-            let child = child_node.borrow_element();
-
-            if child.get_type() != ElementType::Stop {
-                continue;
-            }
-
-            let stop = child.get_impl::<Stop>();
-
-            if child.is_in_error() {
-                rsvg_log!("(not using gradient stop {} because it is in error)", child);
-            } else {
-                let cascaded = CascadedValues::new_from_node(&child_node);
-                let values = cascaded.get();
-                let rgba = match values.stop_color() {
-                    StopColor(cssparser::Color::CurrentColor) => values.color().0,
-                    StopColor(cssparser::Color::RGBA(ref rgba)) => *rgba,
-                };
-
-                self.add_color_stop(stop.offset, rgba, values.stop_opacity().0);
+        for child in node.children().filter(|c| c.is_element()) {
+            let elt = child.borrow_element();
+
+            if let Element::Stop(ref stop) = *elt {
+                if elt.is_in_error() {
+                    rsvg_log!("(not using gradient stop {} because it is in error)", child);
+                } else {
+                    let cascaded = CascadedValues::new_from_node(&child);
+                    let values = cascaded.get();
+                    let rgba = match values.stop_color() {
+                        StopColor(cssparser::Color::CurrentColor) => values.color().0,
+                        StopColor(cssparser::Color::RGBA(ref rgba)) => *rgba,
+                    };
+
+                    self.add_color_stop(stop.offset, rgba, values.stop_opacity().0);
+                }
             }
         }
     }
@@ -694,14 +688,9 @@ macro_rules! impl_paint_source {
                             return Err(AcquireError::CircularReference(acquired_node.clone()));
                         }
 
-                        let elt = acquired_node.borrow_element();
-                        let unresolved = match acquired_node.borrow_element().get_type() {
-                            ElementType::$gradient_type => elt
-                                .get_impl::<$gradient_type>()
-                                .get_unresolved(&acquired_node),
-                            ElementType::$other_type => {
-                                elt.get_impl::<$other_type>().get_unresolved(&acquired_node)
-                            }
+                        let unresolved = match *acquired_node.borrow_element() {
+                            Element::$gradient_type(ref g) => g.get_unresolved(&acquired_node),
+                            Element::$other_type(ref g) => g.get_unresolved(&acquired_node),
                             _ => return Err(AcquireError::InvalidLinkType(fragment.clone())),
                         };
 
@@ -841,10 +830,8 @@ mod tests {
             &bag,
         ));
 
-        let borrow = node.borrow_element();
-        let g = borrow.get_impl::<LinearGradient>();
-        let Unresolved { gradient, .. } = g.get_unresolved(&node);
-        let gradient = gradient.resolve_from_defaults();
+        let unresolved = borrow_element_as!(node, LinearGradient).get_unresolved(&node);
+        let gradient = unresolved.gradient.resolve_from_defaults();
         assert!(gradient.is_resolved());
 
         let node = Node::new(NodeData::new_element(
@@ -852,10 +839,8 @@ mod tests {
             &bag,
         ));
 
-        let borrow = node.borrow_element();
-        let g = borrow.get_impl::<RadialGradient>();
-        let Unresolved { gradient, .. } = g.get_unresolved(&node);
-        let gradient = gradient.resolve_from_defaults();
+        let unresolved = borrow_element_as!(node, RadialGradient).get_unresolved(&node);
+        let gradient = unresolved.gradient.resolve_from_defaults();
         assert!(gradient.is_resolved());
     }
 }
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index bd0e0f46..abe07805 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -11,10 +11,11 @@ use crate::css::{Origin, Stylesheet};
 use crate::document::{AcquiredNodes, Document};
 use crate::dpi::Dpi;
 use crate::drawing_ctx::DrawingCtx;
+use crate::element::Element;
 use crate::error::{DefsLookupErrorKind, LoadingError, RenderingError};
 use crate::node::{CascadedValues, Node, NodeBorrow};
 use crate::rect::{IRect, Rect};
-use crate::structure::{IntrinsicDimensions, Svg};
+use crate::structure::IntrinsicDimensions;
 use url::Url;
 
 /// Loading options for SVG documents.
@@ -322,14 +323,15 @@ impl Handle {
             let cascaded = CascadedValues::new_from_node(&node);
             let values = cascaded.get();
 
-            if let Some((root_width, root_height)) = node
-                .borrow_element()
-                .get_impl::<Svg>()
-                .get_size(&values, dpi)
-            {
-                let rect = IRect::from_size(root_width, root_height);
+            match *node.borrow_element() {
+                Element::Svg(ref svg) => {
+                    if let Some((w, h)) = svg.get_size(&values, dpi) {
+                        let rect = IRect::from_size(w, h);
 
-                return Ok((cairo::Rectangle::from(rect), cairo::Rectangle::from(rect)));
+                        return Ok((cairo::Rectangle::from(rect), cairo::Rectangle::from(rect)));
+                    }
+                }
+                _ => (),
             }
         }
 
@@ -561,7 +563,7 @@ impl Handle {
     }
 
     pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
-        self.document.get_intrinsic_dimensions()
+        borrow_element_as!(self.document.root(), Svg).get_intrinsic_dimensions()
     }
 
     pub fn set_stylesheet(&mut self, css: &str) -> Result<(), LoadingError> {
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 793526da..66906b64 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -83,6 +83,9 @@ mod coord_units;
 #[macro_use]
 mod float_eq_cairo;
 
+#[macro_use]
+mod node;
+
 #[macro_use]
 mod property_macros;
 
@@ -110,7 +113,6 @@ mod iri;
 mod length;
 mod limits;
 mod marker;
-mod node;
 mod number_list;
 mod paint_server;
 mod path_builder;
diff --git a/rsvg_internals/src/marker.rs b/rsvg_internals/src/marker.rs
index 6d641b3d..24f51387 100644
--- a/rsvg_internals/src/marker.rs
+++ b/rsvg_internals/src/marker.rs
@@ -12,7 +12,7 @@ use crate::aspect_ratio::*;
 use crate::bbox::BoundingBox;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::iri::IRI;
@@ -571,10 +571,9 @@ fn emit_marker_by_name(
 ) -> Result<BoundingBox, RenderingError> {
     if let Ok(acquired) = acquired_nodes.acquire(name) {
         let node = acquired.get();
-        let elt = node.borrow_element();
 
-        match elt.get_type() {
-            ElementType::Marker => elt.get_impl::<Marker>().render(
+        match *node.borrow_element() {
+            Element::Marker(ref m) => m.render(
                 &node,
                 acquired_nodes,
                 draw_ctx,
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index aa84b58b..c086c7e2 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -56,12 +56,6 @@ pub type WeakNode = rctree::WeakNode<NodeData>;
 /// spaces), and a single text node with "`Hello`" in it from the
 /// `<text>` element.
 ///
-/// This enum uses `Box<Element>` instead of embedding the `Element`
-/// struct directly in its variant, in order to make text nodes as
-/// small as possible, i.e. without all the baggage in an `Element`.
-/// With the Box, the `Element` variant is the size of a pointer,
-/// which is smaller than the `Text` variant.
-///
 /// ## Accessing the node's contents
 ///
 /// Code that traverses the DOM tree needs to find out at runtime what
@@ -69,13 +63,13 @@ pub type WeakNode = rctree::WeakNode<NodeData>;
 /// methods from the `NodeBorrow` trait to see if you can then call
 /// `borrow_chars`, `borrow_element`, or `borrow_element_mut`.
 pub enum NodeData {
-    Element(Box<Element>),
+    Element(Element),
     Text(Chars),
 }
 
 impl NodeData {
     pub fn new_element(name: &QualName, pbag: &PropertyBag) -> NodeData {
-        NodeData::Element(Box::new(create_element(name, pbag)))
+        NodeData::Element(create_element(name, pbag))
     }
 
     pub fn new_chars() -> NodeData {
@@ -220,19 +214,39 @@ impl NodeBorrow for Node {
 
     fn borrow_element(&self) -> Ref<Element> {
         Ref::map(self.borrow(), |n| match *n {
-            NodeData::Element(ref e) => e.as_ref(),
+            NodeData::Element(ref e) => e,
             _ => panic!("tried to borrow_element for a non-element node"),
         })
     }
 
     fn borrow_element_mut(&mut self) -> RefMut<Element> {
         RefMut::map(self.borrow_mut(), |n| match *n {
-            NodeData::Element(ref mut e) => e.as_mut(),
+            NodeData::Element(ref mut e) => e,
             _ => panic!("tried to borrow_element_mut for a non-element node"),
         })
     }
 }
 
+#[macro_export]
+macro_rules! is_element_of_type {
+    ($node:expr, $element_type:ident) => {
+        matches::matches!(
+            *$node.borrow_element(),
+            crate::element::Element::$element_type(_)
+        )
+    };
+}
+
+#[macro_export]
+macro_rules! borrow_element_as {
+    ($node:expr, $element_type:ident) => {
+        std::cell::Ref::map($node.borrow_element(), |e| match *e {
+            crate::element::Element::$element_type(ref e) => &*e,
+            _ => panic!("tried to borrow_element_as {}", stringify!($element_type)),
+        })
+    };
+}
+
 /// Helper trait for cascading recursively
 pub trait NodeCascade {
     fn cascade(&mut self, values: &ComputedValues);
diff --git a/rsvg_internals/src/pattern.rs b/rsvg_internals/src/pattern.rs
index 9e878aa6..51251c07 100644
--- a/rsvg_internals/src/pattern.rs
+++ b/rsvg_internals/src/pattern.rs
@@ -10,7 +10,7 @@ use crate::bbox::*;
 use crate::coord_units::CoordUnits;
 use crate::document::{AcquiredNodes, NodeStack};
 use crate::drawing_ctx::{DrawingCtx, ViewParams};
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::length::*;
@@ -185,11 +185,9 @@ impl PaintSource for Pattern {
                             return Err(AcquireError::CircularReference(acquired_node.clone()));
                         }
 
-                        let elt = acquired_node.borrow_element();
-                        match elt.get_type() {
-                            ElementType::Pattern => {
-                                let unresolved =
-                                    elt.get_impl::<Pattern>().get_unresolved(&acquired_node);
+                        match *acquired_node.borrow_element() {
+                            Element::Pattern(ref p) => {
+                                let unresolved = p.get_unresolved(&acquired_node);
                                 pattern = pattern.resolve_from_fallback(&unresolved.pattern);
                                 fallback = unresolved.fallback;
 
@@ -576,10 +574,8 @@ mod tests {
             &bag,
         ));
 
-        let borrow = node.borrow_element();
-        let p = borrow.get_impl::<Pattern>();
-        let Unresolved { pattern, .. } = p.get_unresolved(&node);
-        let pattern = pattern.resolve_from_defaults();
+        let unresolved = borrow_element_as!(node, Pattern).get_unresolved(&node);
+        let pattern = unresolved.pattern.resolve_from_defaults();
         assert!(pattern.is_resolved());
     }
 }
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 5fb9fe4f..9463ba99 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -8,7 +8,7 @@ use crate::allowed_url::Fragment;
 use crate::bbox::BoundingBox;
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
-use crate::element::{ElementResult, ElementTrait, ElementType};
+use crate::element::{Element, ElementResult, ElementTrait};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::font_props::FontWeightSpec;
@@ -437,12 +437,10 @@ fn children_to_chunks(
         } else {
             assert!(child.is_element());
 
-            let elt = child.borrow_element();
-
-            match elt.get_type() {
-                ElementType::TSpan => {
+            match *child.borrow_element() {
+                Element::TSpan(ref tspan) => {
                     let cascaded = CascadedValues::new(cascaded, &child);
-                    elt.get_impl::<TSpan>().to_chunks(
+                    tspan.to_chunks(
                         &child,
                         acquired_nodes,
                         &cascaded,
@@ -452,15 +450,9 @@ fn children_to_chunks(
                     );
                 }
 
-                ElementType::TRef => {
+                Element::TRef(ref tref) => {
                     let cascaded = CascadedValues::new(cascaded, &child);
-                    elt.get_impl::<TRef>().to_chunks(
-                        &child,
-                        acquired_nodes,
-                        &cascaded,
-                        chunks,
-                        depth + 1,
-                    );
+                    tref.to_chunks(&child, acquired_nodes, &cascaded, chunks, depth + 1);
                 }
 
                 _ => (),
diff --git a/rsvg_internals/src/xml.rs b/rsvg_internals/src/xml.rs
index 82a82087..36b23f3f 100644
--- a/rsvg_internals/src/xml.rs
+++ b/rsvg_internals/src/xml.rs
@@ -17,13 +17,12 @@ use xml5ever::tokenizer::{TagKind, Token, TokenSink, XmlTokenizer, XmlTokenizerO
 
 use crate::allowed_url::AllowedUrl;
 use crate::document::{Document, DocumentBuilder};
-use crate::element::ElementType;
 use crate::error::LoadingError;
 use crate::io::{self, get_input_stream_for_loading};
 use crate::limits::MAX_LOADED_ELEMENTS;
 use crate::node::{Node, NodeBorrow};
 use crate::property_bag::PropertyBag;
-use crate::style::{Style, StyleType};
+use crate::style::StyleType;
 use crate::xml2_load::Xml2Parser;
 
 #[derive(Clone)]
@@ -362,11 +361,7 @@ impl XmlState {
         let mut inner = self.inner.borrow_mut();
         let current_node = inner.current_node.as_ref().unwrap();
 
-        assert!(current_node.borrow_element().get_type() == ElementType::Style);
-
-        let style_type = current_node
-            .borrow_element()
-            .get_impl::<Style>()
+        let style_type = borrow_element_as!(current_node, Style)
             .style_type()
             .unwrap_or(StyleType::TextCss);
 


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