[librsvg: 2/3] Split AcquiredNodes from DrawingCtx.



commit e7a16afbb966234d2c958fc960538fb0c2238ac7
Author: Paolo Borelli <pborelli gnome org>
Date:   Sat Feb 1 15:42:30 2020 +0100

    Split AcquiredNodes from DrawingCtx.
    
    Pass acquired_nodes as an additional param to draw(), rather than
    storing in the drawing ctx. This makes the drawing ctx indepent
    from Document, and in the future it will allow to decouple resolving
    references from drawing.

 rsvg_internals/src/document.rs                     | 161 +++++++++-
 rsvg_internals/src/drawing_ctx.rs                  | 335 +++++++--------------
 rsvg_internals/src/filters/blend.rs                |   6 +-
 rsvg_internals/src/filters/color_matrix.rs         |   4 +-
 rsvg_internals/src/filters/component_transfer.rs   |   4 +-
 rsvg_internals/src/filters/composite.rs            |   6 +-
 rsvg_internals/src/filters/context.rs              |  21 +-
 rsvg_internals/src/filters/convolve_matrix.rs      |   4 +-
 rsvg_internals/src/filters/displacement_map.rs     |   6 +-
 rsvg_internals/src/filters/flood.rs                |   2 +
 rsvg_internals/src/filters/gaussian_blur.rs        |   4 +-
 rsvg_internals/src/filters/image.rs                |  26 +-
 rsvg_internals/src/filters/light/lighting.rs       |   7 +-
 rsvg_internals/src/filters/merge.rs                |   7 +-
 rsvg_internals/src/filters/mod.rs                  |   8 +-
 rsvg_internals/src/filters/morphology.rs           |   4 +-
 rsvg_internals/src/filters/offset.rs               |   4 +-
 rsvg_internals/src/filters/tile.rs                 |   4 +-
 rsvg_internals/src/filters/turbulence.rs           |   2 +
 rsvg_internals/src/gradient.rs                     |  14 +-
 rsvg_internals/src/handle.rs                       |  75 ++---
 rsvg_internals/src/image.rs                        |   7 +-
 rsvg_internals/src/marker.rs                       |  22 +-
 rsvg_internals/src/node.rs                         |  11 +-
 rsvg_internals/src/paint_server.rs                 |   9 +-
 rsvg_internals/src/pattern.rs                      |  19 +-
 rsvg_internals/src/shapes.rs                       |  45 ++-
 rsvg_internals/src/structure.rs                    |  35 ++-
 rsvg_internals/src/surface_utils/shared_surface.rs |   5 +-
 rsvg_internals/src/text.rs                         |  43 ++-
 rsvg_internals/src/xml.rs                          |  10 +-
 31 files changed, 561 insertions(+), 349 deletions(-)
---
diff --git a/rsvg_internals/src/document.rs b/rsvg_internals/src/document.rs
index c4b1ee57..5f372930 100644
--- a/rsvg_internals/src/document.rs
+++ b/rsvg_internals/src/document.rs
@@ -10,9 +10,10 @@ use std::rc::Rc;
 use crate::allowed_url::{AllowedUrl, AllowedUrlError, Fragment};
 use crate::create_node::create_node;
 use crate::css::{self, Origin, Stylesheet};
-use crate::error::LoadingError;
+use crate::error::{AcquireError, LoadingError};
 use crate::handle::LoadOptions;
 use crate::io::{self, BinaryData};
+use crate::limits;
 use crate::node::{NodeData, NodeType, RsvgNode};
 use crate::property_bag::PropertyBag;
 use crate::structure::{IntrinsicDimensions, Svg};
@@ -229,6 +230,164 @@ fn load_image(
     Ok(surface)
 }
 
+pub struct AcquiredNode {
+    stack: Option<Rc<RefCell<NodeStack>>>,
+    node: RsvgNode,
+}
+
+impl Drop for AcquiredNode {
+    fn drop(&mut self) {
+        if let Some(ref stack) = self.stack {
+            let mut stack = stack.borrow_mut();
+            let last = stack.pop().unwrap();
+            assert!(last == self.node);
+        }
+    }
+}
+
+impl AcquiredNode {
+    pub fn get(&self) -> &RsvgNode {
+        &self.node
+    }
+}
+
+/// This helper struct is used when looking up urls to other nodes.
+/// Its methods do recursion checking and thereby avoid infinite loops.
+///
+/// Malformed SVGs, for example, may reference a marker by its IRI, but
+/// the object referenced by the IRI is not a marker.
+///
+/// Note that if you acquire a node, you have to release it before trying to
+/// acquire it again.  If you acquire a node "#foo" and don't release it before
+/// trying to acquire "foo" again, you will obtain a None the second time.
+pub struct AcquiredNodes<'i> {
+    document: &'i Document,
+    num_elements_acquired: usize,
+    node_stack: Rc<RefCell<NodeStack>>,
+}
+
+impl<'i> AcquiredNodes<'i> {
+    pub fn new(document: &Document) -> AcquiredNodes {
+        AcquiredNodes {
+            document,
+            num_elements_acquired: 0,
+            node_stack: Rc::new(RefCell::new(NodeStack::new())),
+        }
+    }
+
+    pub fn lookup_node(
+        &self,
+        fragment: &Fragment,
+        node_types: &[NodeType],
+    ) -> Result<RsvgNode, AcquireError> {
+        let node = self.document.lookup(fragment).map_err(|_| {
+            // FIXME: callers shouldn't have to know that get_node() can initiate a file load.
+            // Maybe we should have the following stages:
+            //   - load main SVG XML
+            //
+            //   - load secondary SVG XML and other files like images; all document::Resources and
+            //     document::Images loaded
+            //
+            //   - Now that all files are loaded, resolve URL references
+            AcquireError::LinkNotFound(fragment.clone())
+        })?;
+
+        if node_types.is_empty() {
+            Ok(node)
+        } else {
+            let node_type = node.borrow().get_type();
+            if node_types.iter().find(|&&t| t == node_type).is_some() {
+                Ok(node)
+            } else {
+                Err(AcquireError::InvalidLinkType(fragment.clone()))
+            }
+        }
+    }
+
+    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
+        self.document.lookup_image(href)
+    }
+
+    /// Acquires a node.
+    /// Specify `node_types` when expecting the node to be of a particular type,
+    /// or use an empty slice for `node_types` if you want a node of any type.
+    /// Nodes acquired by this function must be released in reverse acquiring order.
+    pub fn acquire(
+        &mut self,
+        fragment: &Fragment,
+        node_types: &[NodeType],
+    ) -> Result<AcquiredNode, AcquireError> {
+        self.num_elements_acquired += 1;
+
+        // This is a mitigation for SVG files that try to instance a huge number of
+        // elements via <use>, recursive patterns, etc.  See limits.rs for details.
+        if self.num_elements_acquired > limits::MAX_REFERENCED_ELEMENTS {
+            return Err(AcquireError::MaxReferencesExceeded);
+        }
+
+        let node = self.lookup_node(fragment, node_types)?;
+
+        if node_is_accessed_by_reference(&node) {
+            self.acquire_ref(&node)
+        } else {
+            Ok(AcquiredNode {
+                stack: None,
+                node: node.clone(),
+            })
+        }
+    }
+
+    pub fn acquire_ref(&self, node: &RsvgNode) -> Result<AcquiredNode, AcquireError> {
+        if self.node_stack.borrow().contains(&node) {
+            Err(AcquireError::CircularReference(node.clone()))
+        } else {
+            self.node_stack.borrow_mut().push(&node);
+            Ok(AcquiredNode {
+                stack: Some(self.node_stack.clone()),
+                node: node.clone(),
+            })
+        }
+    }
+}
+
+// Returns whether a node of a particular type is only accessed by reference
+// from other nodes' atributes.  The node could in turn cause other nodes
+// to get referenced, potentially causing reference cycles.
+fn node_is_accessed_by_reference(node: &RsvgNode) -> bool {
+    use NodeType::*;
+
+    match node.borrow().get_type() {
+        ClipPath | Filter | LinearGradient | Marker | Mask | Pattern | RadialGradient => true,
+
+        _ => false,
+    }
+}
+
+/// Keeps a stack of nodes and can check if a certain node is contained in the stack
+///
+/// Sometimes parts of the code cannot plainly use the implicit stack of acquired
+/// nodes as maintained by DrawingCtx::acquire_node(), and they must keep their
+/// own stack of nodes to test for reference cycles.  NodeStack can be used to do that.
+pub struct NodeStack(Vec<RsvgNode>);
+
+impl NodeStack {
+    pub fn new() -> NodeStack {
+        NodeStack(Vec::new())
+    }
+
+    pub fn push(&mut self, node: &RsvgNode) {
+        self.0.push(node.clone());
+    }
+
+    pub fn pop(&mut self) -> Option<RsvgNode> {
+        self.0.pop()
+    }
+
+    pub fn contains(&self, node: &RsvgNode) -> bool {
+        self.0.iter().find(|n| **n == *node).is_some()
+    }
+}
+
 pub struct DocumentBuilder {
     load_options: LoadOptions,
     tree: Option<RsvgNode>,
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 1c63c1a1..470b5170 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -12,12 +12,11 @@ use crate::aspect_ratio::AspectRatio;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
 use crate::dasharray::Dasharray;
-use crate::document::Document;
+use crate::document::{AcquiredNode, AcquiredNodes};
 use crate::dpi::Dpi;
 use crate::error::{AcquireError, RenderingError};
 use crate::filters;
 use crate::gradient::{LinearGradient, RadialGradient};
-use crate::limits;
 use crate::marker;
 use crate::node::{CascadedValues, NodeDraw, NodeType, RsvgNode};
 use crate::paint_server::{PaintServer, PaintSource};
@@ -85,9 +84,7 @@ pub enum ClipMode {
     ClipToVbox,
 }
 
-pub struct DrawingCtx<'i> {
-    document: &'i Document,
-
+pub struct DrawingCtx {
     initial_transform: Transform,
 
     rect: Rect,
@@ -100,22 +97,19 @@ pub struct DrawingCtx<'i> {
 
     drawsub_stack: Vec<RsvgNode>,
 
-    acquired_nodes: AcquiredNodes<'i>,
-
     measuring: bool,
     testing: bool,
 }
 
-impl<'i> DrawingCtx<'i> {
+impl DrawingCtx {
     pub fn new(
-        document: &'i Document,
         node: Option<&RsvgNode>,
         cr: &cairo::Context,
         viewport: Rect,
         dpi: Dpi,
         measuring: bool,
         testing: bool,
-    ) -> DrawingCtx<'i> {
+    ) -> DrawingCtx {
         let initial_transform = Transform::from(cr.get_matrix());
 
         // This is more or less a hack to make measuring geometries possible,
@@ -145,10 +139,7 @@ impl<'i> DrawingCtx<'i> {
         let mut view_box_stack = Vec::new();
         view_box_stack.push(vbox);
 
-        let acquired_nodes = AcquiredNodes::new(document);
-
         let mut draw_ctx = DrawingCtx {
-            document,
             initial_transform,
             rect,
             dpi,
@@ -156,7 +147,6 @@ impl<'i> DrawingCtx<'i> {
             cr: cr.clone(),
             view_box_stack: Rc::new(RefCell::new(view_box_stack)),
             drawsub_stack: Vec::new(),
-            acquired_nodes,
             measuring,
             testing,
         };
@@ -321,17 +311,10 @@ impl<'i> DrawingCtx<'i> {
             })
     }
 
-    pub fn acquire_node(
-        &mut self,
-        fragment: &Fragment,
-        node_types: &[NodeType],
-    ) -> Result<AcquiredNode, AcquireError> {
-        self.acquired_nodes.acquire(fragment, node_types)
-    }
-
     fn clip_to_node(
         &mut self,
         clip_node: &Option<RsvgNode>,
+        acquired_nodes: &mut AcquiredNodes,
         bbox: &BoundingBox,
     ) -> Result<(), RenderingError> {
         if let Some(node) = clip_node {
@@ -364,7 +347,7 @@ impl<'i> DrawingCtx<'i> {
                 let cr = dc.get_cairo_context();
 
                 // here we don't push a layer because we are clipping
-                let res = node.draw_children(&cascaded, dc, true);
+                let res = node.draw_children(acquired_nodes, &cascaded, dc, true);
 
                 cr.clip();
 
@@ -386,6 +369,7 @@ impl<'i> DrawingCtx<'i> {
         mask_node: &RsvgNode,
         transform: Transform,
         bbox: &BoundingBox,
+        acquired_nodes: &mut AcquiredNodes,
     ) -> Result<Option<cairo::ImageSurface>, RenderingError> {
         if bbox.rect.is_none() {
             // The node being masked is empty / doesn't have a
@@ -450,9 +434,13 @@ impl<'i> DrawingCtx<'i> {
                 self.get_view_params()
             };
 
-            let res = self.with_discrete_layer(mask_node, values, false, &mut |dc| {
-                mask_node.draw_children(&cascaded, dc, false)
-            });
+            let res = self.with_discrete_layer(
+                mask_node,
+                acquired_nodes,
+                values,
+                false,
+                &mut |an, dc| mask_node.draw_children(an, &cascaded, dc, false),
+            );
 
             self.pop_cairo_context();
 
@@ -471,12 +459,16 @@ impl<'i> DrawingCtx<'i> {
     pub fn with_discrete_layer(
         &mut self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
         clipping: bool,
-        draw_fn: &mut dyn FnMut(&mut DrawingCtx) -> Result<BoundingBox, RenderingError>,
+        draw_fn: &mut dyn FnMut(
+            &mut AcquiredNodes,
+            &mut DrawingCtx,
+        ) -> Result<BoundingBox, RenderingError>,
     ) -> Result<BoundingBox, RenderingError> {
         if clipping {
-            draw_fn(self)
+            draw_fn(acquired_nodes, self)
         } else {
             self.with_saved_cr(&mut |dc| {
                 let clip_uri = values.clip_path.0.get();
@@ -494,10 +486,10 @@ impl<'i> DrawingCtx<'i> {
                 let affine_at_start = dc.get_transform();
 
                 let (clip_in_user_space, clip_in_object_space) =
-                    get_clip_in_user_and_object_space(&mut dc.acquired_nodes, clip_uri);
+                    get_clip_in_user_and_object_space(acquired_nodes, clip_uri);
 
                 // Here we are clipping in user space, so the bbox doesn't matter
-                dc.clip_to_node(&clip_in_user_space, &dc.empty_bbox())?;
+                dc.clip_to_node(&clip_in_user_space, acquired_nodes, &dc.empty_bbox())?;
 
                 let needs_temporary_surface = !(opacity == 1.0
                     && filter.is_none()
@@ -529,7 +521,7 @@ impl<'i> DrawingCtx<'i> {
 
                     // Draw!
 
-                    let mut res = draw_fn(dc);
+                    let mut res = draw_fn(acquired_nodes, dc);
 
                     let bbox = if let Ok(ref bbox) = res {
                         *bbox
@@ -548,7 +540,14 @@ impl<'i> DrawingCtx<'i> {
                         )?;
 
                         let img_surface = dc
-                            .run_filter(filter_uri, node, values, child_surface, bbox)?
+                            .run_filter(
+                                acquired_nodes,
+                                filter_uri,
+                                node,
+                                values,
+                                child_surface,
+                                bbox,
+                            )?
                             .into_image_surface()?;
 
                         // turn ImageSurface into a Surface
@@ -567,12 +566,12 @@ impl<'i> DrawingCtx<'i> {
                     // Clip
 
                     dc.cr.set_matrix(affines.outside_temporary_surface.into());
-                    let _: () = dc.clip_to_node(&clip_in_object_space, &bbox)?;
+                    let _: () = dc.clip_to_node(&clip_in_object_space, acquired_nodes, &bbox)?;
 
                     // Mask
 
                     if let Some(fragment) = mask {
-                        if let Ok(acquired) = dc.acquire_node(fragment, &[NodeType::Mask]) {
+                        if let Ok(acquired) = acquired_nodes.acquire(fragment, &[NodeType::Mask]) {
                             let mask_node = acquired.get();
 
                             res = res.and_then(|bbox| {
@@ -581,6 +580,7 @@ impl<'i> DrawingCtx<'i> {
                                     &mask_node,
                                     affines.for_temporary_surface,
                                     &bbox,
+                                    acquired_nodes,
                                 )
                                 .and_then(|mask_surf| {
                                     if let Some(surf) = mask_surf {
@@ -614,7 +614,7 @@ impl<'i> DrawingCtx<'i> {
 
                     res
                 } else {
-                    draw_fn(dc)
+                    draw_fn(acquired_nodes, dc)
                 }
             })
         }
@@ -664,7 +664,8 @@ impl<'i> DrawingCtx<'i> {
     ) -> Result<BoundingBox, RenderingError> {
         if let Some(rect) = clip {
             self.cr.save();
-            self.cr.rectangle(rect.x0, rect.y0, rect.width(), rect.height());
+            self.cr
+                .rectangle(rect.x0, rect.y0, rect.width(), rect.height());
             self.cr.clip();
         }
 
@@ -710,19 +711,27 @@ impl<'i> DrawingCtx<'i> {
 
     fn run_filter(
         &mut self,
+        acquired_nodes: &mut AcquiredNodes,
         filter_uri: &Fragment,
         node: &RsvgNode,
         values: &ComputedValues,
         child_surface: SharedImageSurface,
         node_bbox: BoundingBox,
     ) -> Result<SharedImageSurface, RenderingError> {
-        match self.acquire_node(filter_uri, &[NodeType::Filter]) {
+        match acquired_nodes.acquire(filter_uri, &[NodeType::Filter]) {
             Ok(acquired) => {
                 let filter_node = acquired.get();
 
                 if !filter_node.borrow().is_in_error() {
                     // FIXME: deal with out of memory here
-                    filters::render(&filter_node, values, child_surface, self, node_bbox)
+                    filters::render(
+                        &filter_node,
+                        values,
+                        child_surface,
+                        acquired_nodes,
+                        self,
+                        node_bbox,
+                    )
                 } else {
                     Ok(child_surface.clone())
                 }
@@ -768,6 +777,7 @@ impl<'i> DrawingCtx<'i> {
 
     pub fn set_source_paint_server(
         &mut self,
+        acquired_nodes: &mut AcquiredNodes,
         ps: &PaintServer,
         opacity: UnitInterval,
         bbox: &BoundingBox,
@@ -780,7 +790,7 @@ impl<'i> DrawingCtx<'i> {
             } => {
                 let mut had_paint_server = false;
 
-                match acquire_paint_server(&mut self.acquired_nodes, iri) {
+                match acquire_paint_server(acquired_nodes, iri) {
                     Ok(acquired) => {
                         let node = acquired.get();
 
@@ -788,15 +798,33 @@ impl<'i> DrawingCtx<'i> {
                             NodeType::LinearGradient => node
                                 .borrow()
                                 .get_impl::<LinearGradient>()
-                                .resolve_fallbacks_and_set_pattern(&node, self, opacity, bbox)?,
+                                .resolve_fallbacks_and_set_pattern(
+                                    &node,
+                                    acquired_nodes,
+                                    self,
+                                    opacity,
+                                    bbox,
+                                )?,
                             NodeType::RadialGradient => node
                                 .borrow()
                                 .get_impl::<RadialGradient>()
-                                .resolve_fallbacks_and_set_pattern(&node, self, opacity, bbox)?,
+                                .resolve_fallbacks_and_set_pattern(
+                                    &node,
+                                    acquired_nodes,
+                                    self,
+                                    opacity,
+                                    bbox,
+                                )?,
                             NodeType::Pattern => node
                                 .borrow()
                                 .get_impl::<Pattern>()
-                                .resolve_fallbacks_and_set_pattern(&node, self, opacity, bbox)?,
+                                .resolve_fallbacks_and_set_pattern(
+                                    &node,
+                                    acquired_nodes,
+                                    self,
+                                    opacity,
+                                    bbox,
+                                )?,
                             _ => unreachable!(),
                         }
                     }
@@ -858,6 +886,7 @@ impl<'i> DrawingCtx<'i> {
     pub fn stroke_and_fill(
         &mut self,
         cr: &cairo::Context,
+        acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
     ) -> Result<BoundingBox, RenderingError> {
         cr.set_antialias(cairo::Antialias::from(values.shape_rendering));
@@ -873,7 +902,13 @@ impl<'i> DrawingCtx<'i> {
         let current_color = values.color.0;
 
         let res = self
-            .set_source_paint_server(&values.fill.0, values.fill_opacity.0, &bbox, current_color)
+            .set_source_paint_server(
+                acquired_nodes,
+                &values.fill.0,
+                values.fill_opacity.0,
+                &bbox,
+                current_color,
+            )
             .and_then(|had_paint_server| {
                 if had_paint_server {
                     if values.stroke.0 == PaintServer::None {
@@ -887,6 +922,7 @@ impl<'i> DrawingCtx<'i> {
             })
             .and_then(|_| {
                 self.set_source_paint_server(
+                    acquired_nodes,
                     &values.stroke.0,
                     values.stroke_opacity.0,
                     &bbox,
@@ -911,27 +947,35 @@ impl<'i> DrawingCtx<'i> {
         &mut self,
         builder: &PathBuilder,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
         markers: Markers,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         if !builder.is_empty() {
-            let bbox = self.with_discrete_layer(node, values, clipping, &mut |dc| {
-                let cr = dc.get_cairo_context();
+            let bbox =
+                self.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
+                    let cr = dc.get_cairo_context();
 
-                builder.to_cairo(&cr)?;
+                    builder.to_cairo(&cr)?;
 
-                if clipping {
-                    cr.set_fill_rule(cairo::FillRule::from(values.clip_rule));
-                    Ok(dc.empty_bbox())
-                } else {
-                    cr.set_fill_rule(cairo::FillRule::from(values.fill_rule));
-                    dc.stroke_and_fill(&cr, values)
-                }
-            })?;
+                    if clipping {
+                        cr.set_fill_rule(cairo::FillRule::from(values.clip_rule));
+                        Ok(dc.empty_bbox())
+                    } else {
+                        cr.set_fill_rule(cairo::FillRule::from(values.fill_rule));
+                        dc.stroke_and_fill(&cr, an, values)
+                    }
+                })?;
 
             if markers == Markers::Yes {
-                marker::render_markers_for_path_builder(builder, self, values, clipping)?;
+                marker::render_markers_for_path_builder(
+                    builder,
+                    self,
+                    acquired_nodes,
+                    values,
+                    clipping,
+                )?;
             }
 
             Ok(bbox)
@@ -981,15 +1025,10 @@ impl<'i> DrawingCtx<'i> {
         surface.share()
     }
 
-    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, RenderingError> {
-        self.document
-            .lookup_image(href)
-            .map_err(|_| RenderingError::InvalidHref)
-    }
-
     pub fn draw_node_to_surface(
         &mut self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         affine: Transform,
         width: i32,
@@ -1008,7 +1047,7 @@ impl<'i> DrawingCtx<'i> {
 
             self.rect = Rect::from_size(f64::from(width), f64::from(height));
 
-            let _ = self.draw_node_from_stack(cascaded, node, false)?;
+            let _ = self.draw_node_from_stack(node, acquired_nodes, cascaded, false)?;
         }
 
         self.cr = save_cr;
@@ -1019,8 +1058,9 @@ impl<'i> DrawingCtx<'i> {
 
     pub fn draw_node_from_stack(
         &mut self,
-        cascaded: &CascadedValues<'_>,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
+        cascaded: &CascadedValues<'_>,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         let stack_top = self.drawsub_stack.pop();
@@ -1033,7 +1073,7 @@ impl<'i> DrawingCtx<'i> {
 
         let values = cascaded.get();
         let res = if draw && values.is_visible() {
-            node.draw(cascaded, self, clipping)
+            node.draw(acquired_nodes, cascaded, self, clipping)
         } else {
             Ok(self.empty_bbox())
         };
@@ -1048,6 +1088,7 @@ impl<'i> DrawingCtx<'i> {
     pub fn draw_from_use_node(
         &mut self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
@@ -1061,7 +1102,7 @@ impl<'i> DrawingCtx<'i> {
         // another <use> which references the first one, etc.).  So,
         // we acquire the <use> element itself so that circular
         // references can be caught.
-        let _self_acquired = self.acquired_nodes.acquire_ref(node).map_err(|e| {
+        let _self_acquired = acquired_nodes.acquire_ref(node).map_err(|e| {
             if let AcquireError::CircularReference(_) = e {
                 rsvg_log!("circular reference in element {}", node);
                 RenderingError::CircularReference
@@ -1075,7 +1116,7 @@ impl<'i> DrawingCtx<'i> {
             return Ok(self.empty_bbox());
         }
 
-        let acquired = match self.acquire_node(link.unwrap(), &[]) {
+        let acquired = match acquired_nodes.acquire(link.unwrap(), &[]) {
             Ok(acquired) => acquired,
 
             Err(AcquireError::CircularReference(node)) => {
@@ -1111,10 +1152,11 @@ impl<'i> DrawingCtx<'i> {
             let cr = self.get_cairo_context();
             cr.translate(use_rect.x0, use_rect.y0);
 
-            self.with_discrete_layer(node, values, clipping, &mut |dc| {
+            self.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
                 dc.draw_node_from_stack(
-                    &CascadedValues::new_from_values(&child, values),
                     &child,
+                    an,
+                    &CascadedValues::new_from_values(&child, values),
                     clipping,
                 )
             })
@@ -1130,7 +1172,7 @@ impl<'i> DrawingCtx<'i> {
                 None
             };
 
-            self.with_discrete_layer(node, values, clipping, &mut |dc| {
+            self.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
                 let _params = dc.push_new_viewport(
                     symbol.get_viewbox(),
                     use_rect,
@@ -1139,6 +1181,7 @@ impl<'i> DrawingCtx<'i> {
                 );
 
                 child.draw_children(
+                    an,
                     &CascadedValues::new_from_values(&child, values),
                     dc,
                     clipping,
@@ -1340,157 +1383,3 @@ impl From<ShapeRendering> for cairo::Antialias {
         }
     }
 }
-
-pub struct AcquiredNode {
-    stack: Option<Rc<RefCell<NodeStack>>>,
-    node: RsvgNode,
-}
-
-impl Drop for AcquiredNode {
-    fn drop(&mut self) {
-        if let Some(ref stack) = self.stack {
-            let mut stack = stack.borrow_mut();
-            let last = stack.pop().unwrap();
-            assert!(last == self.node);
-        }
-    }
-}
-
-impl AcquiredNode {
-    pub fn get(&self) -> &RsvgNode {
-        &self.node
-    }
-}
-
-/// This helper struct is used when looking up urls to other nodes.
-/// Its methods do recursion checking and thereby avoid infinite loops.
-///
-/// Malformed SVGs, for example, may reference a marker by its IRI, but
-/// the object referenced by the IRI is not a marker.
-///
-/// Note that if you acquire a node, you have to release it before trying to
-/// acquire it again.  If you acquire a node "#foo" and don't release it before
-/// trying to acquire "foo" again, you will obtain a None the second time.
-struct AcquiredNodes<'i> {
-    document: &'i Document,
-    num_elements_acquired: usize,
-    node_stack: Rc<RefCell<NodeStack>>,
-}
-
-impl<'i> AcquiredNodes<'i> {
-    fn new(document: &Document) -> AcquiredNodes {
-        AcquiredNodes {
-            document,
-            num_elements_acquired: 0,
-            node_stack: Rc::new(RefCell::new(NodeStack::new())),
-        }
-    }
-
-    fn lookup_node(
-        &self,
-        fragment: &Fragment,
-        node_types: &[NodeType],
-    ) -> Result<RsvgNode, AcquireError> {
-        let node = self.document.lookup(fragment).map_err(|_| {
-            // FIXME: callers shouldn't have to know that get_node() can initiate a file load.
-            // Maybe we should have the following stages:
-            //   - load main SVG XML
-            //
-            //   - load secondary SVG XML and other files like images; all document::Resources and
-            //     document::Images loaded
-            //
-            //   - Now that all files are loaded, resolve URL references
-            AcquireError::LinkNotFound(fragment.clone())
-        })?;
-
-        if node_types.is_empty() {
-            Ok(node)
-        } else {
-            let node_type = node.borrow().get_type();
-            if node_types.iter().find(|&&t| t == node_type).is_some() {
-                Ok(node)
-            } else {
-                Err(AcquireError::InvalidLinkType(fragment.clone()))
-            }
-        }
-    }
-
-    /// Acquires a node.
-    /// Specify `node_types` when expecting the node to be of a particular type,
-    /// or use an empty slice for `node_types` if you want a node of any type.
-    /// Nodes acquired by this function must be released in reverse acquiring order.
-    fn acquire(
-        &mut self,
-        fragment: &Fragment,
-        node_types: &[NodeType],
-    ) -> Result<AcquiredNode, AcquireError> {
-        self.num_elements_acquired += 1;
-
-        // This is a mitigation for SVG files that try to instance a huge number of
-        // elements via <use>, recursive patterns, etc.  See limits.rs for details.
-        if self.num_elements_acquired > limits::MAX_REFERENCED_ELEMENTS {
-            return Err(AcquireError::MaxReferencesExceeded);
-        }
-
-        let node = self.lookup_node(fragment, node_types)?;
-
-        if node_is_accessed_by_reference(&node) {
-            self.acquire_ref(&node)
-        } else {
-            Ok(AcquiredNode {
-                stack: None,
-                node: node.clone(),
-            })
-        }
-    }
-
-    fn acquire_ref(&self, node: &RsvgNode) -> Result<AcquiredNode, AcquireError> {
-        if self.node_stack.borrow().contains(&node) {
-            Err(AcquireError::CircularReference(node.clone()))
-        } else {
-            self.node_stack.borrow_mut().push(&node);
-            Ok(AcquiredNode {
-                stack: Some(self.node_stack.clone()),
-                node: node.clone(),
-            })
-        }
-    }
-}
-
-// Returns whether a node of a particular type is only accessed by reference
-// from other nodes' atributes.  The node could in turn cause other nodes
-// to get referenced, potentially causing reference cycles.
-fn node_is_accessed_by_reference(node: &RsvgNode) -> bool {
-    use NodeType::*;
-
-    match node.borrow().get_type() {
-        ClipPath | Filter | LinearGradient | Marker | Mask | Pattern | RadialGradient => true,
-
-        _ => false,
-    }
-}
-
-/// Keeps a stack of nodes and can check if a certain node is contained in the stack
-///
-/// Sometimes parts of the code cannot plainly use the implicit stack of acquired
-/// nodes as maintained by DrawingCtx::acquire_node(), and they must keep their
-/// own stack of nodes to test for reference cycles.  NodeStack can be used to do that.
-pub struct NodeStack(Vec<RsvgNode>);
-
-impl NodeStack {
-    pub fn new() -> NodeStack {
-        NodeStack(Vec::new())
-    }
-
-    pub fn push(&mut self, node: &RsvgNode) {
-        self.0.push(node.clone());
-    }
-
-    pub fn pop(&mut self) -> Option<RsvgNode> {
-        self.0.pop()
-    }
-
-    pub fn contains(&self, node: &RsvgNode) -> bool {
-        self.0.iter().find(|n| **n == *node).is_some()
-    }
-}
diff --git a/rsvg_internals/src/filters/blend.rs b/rsvg_internals/src/filters/blend.rs
index 8bef9b96..9c75522f 100755
--- a/rsvg_internals/src/filters/blend.rs
+++ b/rsvg_internals/src/filters/blend.rs
@@ -1,6 +1,7 @@
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -76,10 +77,11 @@ impl FilterEffect for FeBlend {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
-        let input_2 = ctx.get_input(draw_ctx, self.in2.as_ref())?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
+        let input_2 = ctx.get_input(acquired_nodes, draw_ctx, self.in2.as_ref())?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/color_matrix.rs b/rsvg_internals/src/filters/color_matrix.rs
index 4db2d24c..2eaf2506 100644
--- a/rsvg_internals/src/filters/color_matrix.rs
+++ b/rsvg_internals/src/filters/color_matrix.rs
@@ -2,6 +2,7 @@ use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -157,9 +158,10 @@ impl FilterEffect for FeColorMatrix {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/component_transfer.rs 
b/rsvg_internals/src/filters/component_transfer.rs
index c5790ecb..91af86f1 100644
--- a/rsvg_internals/src/filters/component_transfer.rs
+++ b/rsvg_internals/src/filters/component_transfer.rs
@@ -3,6 +3,7 @@ use std::cmp::min;
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
@@ -269,9 +270,10 @@ impl FilterEffect for FeComponentTransfer {
         &self,
         node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs
index aec3fd9f..277bf3aa 100644
--- a/rsvg_internals/src/filters/composite.rs
+++ b/rsvg_internals/src/filters/composite.rs
@@ -1,6 +1,7 @@
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -76,10 +77,11 @@ impl FilterEffect for FeComposite {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
-        let input_2 = ctx.get_input(draw_ctx, self.in2.as_ref())?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
+        let input_2 = ctx.get_input(acquired_nodes, draw_ctx, self.in2.as_ref())?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/context.rs b/rsvg_internals/src/filters/context.rs
index e4d31ced..9a1556e0 100644
--- a/rsvg_internals/src/filters/context.rs
+++ b/rsvg_internals/src/filters/context.rs
@@ -4,6 +4,7 @@ use std::f64;
 
 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::RsvgNode;
@@ -270,6 +271,7 @@ impl FilterContext {
     fn get_paint_server_surface(
         &self,
         draw_ctx: &mut DrawingCtx,
+        acquired_nodes: &mut AcquiredNodes,
         paint_server: &PaintServer,
         opacity: UnitInterval,
     ) -> Result<SharedImageSurface, cairo::Status> {
@@ -286,6 +288,7 @@ impl FilterContext {
             // FIXME: we are ignoring the following error; propagate it upstream
             let _ = draw_ctx
                 .set_source_paint_server(
+                    acquired_nodes,
                     paint_server,
                     opacity,
                     &self.node_bbox,
@@ -311,6 +314,7 @@ impl FilterContext {
     /// Does not take `processing_linear_rgb` into account.
     fn get_input_raw(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         in_: Option<&Input>,
     ) -> Result<FilterInput, FilterError> {
@@ -350,12 +354,22 @@ impl FilterContext {
                 .map(FilterInput::StandardInput),
 
             Input::FillPaint => self
-                .get_paint_server_surface(draw_ctx, &values.fill.0, values.fill_opacity.0)
+                .get_paint_server_surface(
+                    draw_ctx,
+                    acquired_nodes,
+                    &values.fill.0,
+                    values.fill_opacity.0,
+                )
                 .map_err(FilterError::CairoError)
                 .map(FilterInput::StandardInput),
 
             Input::StrokePaint => self
-                .get_paint_server_surface(draw_ctx, &values.stroke.0, values.stroke_opacity.0)
+                .get_paint_server_surface(
+                    draw_ctx,
+                    acquired_nodes,
+                    &values.stroke.0,
+                    values.stroke_opacity.0,
+                )
                 .map_err(FilterError::CairoError)
                 .map(FilterInput::StandardInput),
 
@@ -371,10 +385,11 @@ impl FilterContext {
     /// Retrieves the filter input surface according to the SVG rules.
     pub fn get_input(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         in_: Option<&Input>,
     ) -> Result<FilterInput, FilterError> {
-        let raw = self.get_input_raw(draw_ctx, in_)?;
+        let raw = self.get_input_raw(acquired_nodes, draw_ctx, in_)?;
 
         // Convert the input surface to the desired format.
         let (surface, bounds) = match raw {
diff --git a/rsvg_internals/src/filters/convolve_matrix.rs b/rsvg_internals/src/filters/convolve_matrix.rs
index de6c1ca8..4ae5a45e 100644
--- a/rsvg_internals/src/filters/convolve_matrix.rs
+++ b/rsvg_internals/src/filters/convolve_matrix.rs
@@ -2,6 +2,7 @@ use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
 use nalgebra::{DMatrix, Dynamic, VecStorage};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -198,9 +199,10 @@ impl FilterEffect for FeConvolveMatrix {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
         let mut bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/displacement_map.rs b/rsvg_internals/src/filters/displacement_map.rs
index 978073e2..86776bc7 100644
--- a/rsvg_internals/src/filters/displacement_map.rs
+++ b/rsvg_internals/src/filters/displacement_map.rs
@@ -1,6 +1,7 @@
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -74,10 +75,11 @@ impl FilterEffect for FeDisplacementMap {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
-        let displacement_input = ctx.get_input(draw_ctx, self.in2.as_ref())?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
+        let displacement_input = ctx.get_input(acquired_nodes, draw_ctx, self.in2.as_ref())?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/flood.rs b/rsvg_internals/src/filters/flood.rs
index a3e58bdd..9886a308 100644
--- a/rsvg_internals/src/filters/flood.rs
+++ b/rsvg_internals/src/filters/flood.rs
@@ -1,3 +1,4 @@
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
 use crate::property_bag::PropertyBag;
@@ -34,6 +35,7 @@ impl FilterEffect for FeFlood {
         &self,
         node: &RsvgNode,
         ctx: &FilterContext,
+        _acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
         let bounds = self.base.get_bounds(ctx).into_irect(draw_ctx);
diff --git a/rsvg_internals/src/filters/gaussian_blur.rs b/rsvg_internals/src/filters/gaussian_blur.rs
index d0b68811..813c6bd9 100644
--- a/rsvg_internals/src/filters/gaussian_blur.rs
+++ b/rsvg_internals/src/filters/gaussian_blur.rs
@@ -4,6 +4,7 @@ use std::f64;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 use nalgebra::{DMatrix, Dynamic, VecStorage};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -202,9 +203,10 @@ impl FilterEffect for FeGaussianBlur {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs
index ca21b6b6..bf9a7844 100644
--- a/rsvg_internals/src/filters/image.rs
+++ b/rsvg_internals/src/filters/image.rs
@@ -2,6 +2,7 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::allowed_url::{Fragment, Href};
 use crate::aspect_ratio::AspectRatio;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
@@ -37,12 +38,13 @@ impl FeImage {
     fn render_node(
         &self,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         bounds: Rect,
         fragment: &Fragment,
     ) -> Result<FilterResult, FilterError> {
-        let acquired_drawable = draw_ctx
-            .acquire_node(fragment, &[])
+        let acquired_drawable = acquired_nodes
+            .acquire(fragment, &[])
             .map_err(|_| FilterError::InvalidInput)?;
         let drawable = acquired_drawable.get();
 
@@ -51,6 +53,7 @@ impl FeImage {
 
         let image = draw_ctx.draw_node_to_surface(
             &drawable,
+            acquired_nodes,
             &cascaded,
             ctx.paffine(),
             ctx.source_graphic().width(),
@@ -72,13 +75,14 @@ impl FeImage {
     fn render_external_image(
         &self,
         ctx: &FilterContext,
-        draw_ctx: &DrawingCtx,
+        acquired_nodes: &mut AcquiredNodes,
+        _draw_ctx: &DrawingCtx,
         bounds: Rect,
         unclipped_bounds: &Rect,
         url: &str,
     ) -> Result<FilterResult, FilterError> {
         // FIXME: translate the error better here
-        let image = draw_ctx
+        let image = acquired_nodes
             .lookup_image(url)
             .map_err(|_| FilterError::InvalidInput)?;
 
@@ -137,6 +141,7 @@ impl FilterEffect for FeImage {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
         let bounds_builder = self.base.get_bounds(ctx);
@@ -145,9 +150,18 @@ impl FilterEffect for FeImage {
         match self.href.as_ref() {
             Some(Href::PlainUrl(url)) => {
                 let unclipped_bounds = bounds_builder.into_rect_without_clipping(draw_ctx);
-                self.render_external_image(ctx, draw_ctx, bounds, &unclipped_bounds, url)
+                self.render_external_image(
+                    ctx,
+                    acquired_nodes,
+                    draw_ctx,
+                    bounds,
+                    &unclipped_bounds,
+                    url,
+                )
+            }
+            Some(Href::WithFragment(ref frag)) => {
+                self.render_node(ctx, acquired_nodes, draw_ctx, bounds, frag)
             }
-            Some(Href::WithFragment(ref frag)) => self.render_node(ctx, draw_ctx, bounds, frag),
             _ => Err(FilterError::InvalidInput),
         }
     }
diff --git a/rsvg_internals/src/filters/light/lighting.rs b/rsvg_internals/src/filters/light/lighting.rs
index 44bcb659..020411cd 100644
--- a/rsvg_internals/src/filters/light/lighting.rs
+++ b/rsvg_internals/src/filters/light/lighting.rs
@@ -5,6 +5,7 @@ use nalgebra::Vector3;
 use num_traits::identities::Zero;
 use rayon::prelude::*;
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::filters::{
@@ -249,9 +250,13 @@ macro_rules! impl_lighting_filter {
                 &self,
                 node: &RsvgNode,
                 ctx: &FilterContext,
+                acquired_nodes: &mut AcquiredNodes,
                 draw_ctx: &mut DrawingCtx,
             ) -> Result<FilterResult, FilterError> {
-                let input = self.common().base.get_input(ctx, draw_ctx)?;
+                let input = self
+                    .common()
+                    .base
+                    .get_input(ctx, acquired_nodes, draw_ctx)?;
                 let mut bounds = self
                     .common()
                     .base
diff --git a/rsvg_internals/src/filters/merge.rs b/rsvg_internals/src/filters/merge.rs
index b8b95cde..c70975f2 100644
--- a/rsvg_internals/src/filters/merge.rs
+++ b/rsvg_internals/src/filters/merge.rs
@@ -1,5 +1,6 @@
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::node::{NodeResult, NodeTrait, NodeType, RsvgNode};
 use crate::parsers::ParseValue;
@@ -59,11 +60,12 @@ impl FeMergeNode {
     fn render(
         &self,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         bounds: IRect,
         output_surface: Option<SharedImageSurface>,
     ) -> Result<SharedImageSurface, FilterError> {
-        let input = ctx.get_input(draw_ctx, self.in_.as_ref())?;
+        let input = ctx.get_input(acquired_nodes, draw_ctx, self.in_.as_ref())?;
 
         if output_surface.is_none() {
             return Ok(input.surface().clone());
@@ -81,6 +83,7 @@ impl FilterEffect for FeMerge {
         &self,
         node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
         // Compute the filter bounds, taking each child node's input into account.
@@ -94,6 +97,7 @@ impl FilterEffect for FeMerge {
             }
 
             let input = ctx.get_input(
+                acquired_nodes,
                 draw_ctx,
                 child.borrow().get_impl::<FeMergeNode>().in_.as_ref(),
             )?;
@@ -109,6 +113,7 @@ impl FilterEffect for FeMerge {
         {
             output_surface = Some(child.borrow().get_impl::<FeMergeNode>().render(
                 ctx,
+                acquired_nodes,
                 draw_ctx,
                 bounds,
                 output_surface,
diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs
index 07e4c5bd..79f61572 100644
--- a/rsvg_internals/src/filters/mod.rs
+++ b/rsvg_internals/src/filters/mod.rs
@@ -7,6 +7,7 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::{RenderingError, ValueErrorKind};
 use crate::filter::Filter;
@@ -40,6 +41,7 @@ pub trait FilterEffect: NodeTrait {
         &self,
         node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError>;
 
@@ -205,9 +207,10 @@ impl PrimitiveWithInput {
     fn get_input(
         &self,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterInput, FilterError> {
-        ctx.get_input(draw_ctx, self.in_.as_ref())
+        ctx.get_input(acquired_nodes, draw_ctx, self.in_.as_ref())
     }
 }
 
@@ -240,6 +243,7 @@ pub fn render(
     filter_node: &RsvgNode,
     computed_from_node_being_filtered: &ComputedValues,
     source_surface: SharedImageSurface,
+    acquired_nodes: &mut AcquiredNodes,
     draw_ctx: &mut DrawingCtx,
     node_bbox: BoundingBox,
 ) -> Result<SharedImageSurface, RenderingError> {
@@ -293,7 +297,7 @@ pub fn render(
 
         let mut render = |filter_ctx: &mut FilterContext| {
             if let Err(err) = filter
-                .render(&c, filter_ctx, draw_ctx)
+                .render(&c, filter_ctx, acquired_nodes, draw_ctx)
                 .and_then(|result| filter_ctx.store_result(result))
             {
                 rsvg_log!("(filter primitive {} returned an error: {})", c, err);
diff --git a/rsvg_internals/src/filters/morphology.rs b/rsvg_internals/src/filters/morphology.rs
index 0fed43b7..719bbc1d 100644
--- a/rsvg_internals/src/filters/morphology.rs
+++ b/rsvg_internals/src/filters/morphology.rs
@@ -3,6 +3,7 @@ use std::cmp::{max, min};
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
@@ -77,9 +78,10 @@ impl FilterEffect for FeMorphology {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/offset.rs b/rsvg_internals/src/filters/offset.rs
index 297b9027..0604d9ae 100644
--- a/rsvg_internals/src/filters/offset.rs
+++ b/rsvg_internals/src/filters/offset.rs
@@ -1,5 +1,6 @@
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
 use crate::parsers::ParseValue;
@@ -50,9 +51,10 @@ impl FilterEffect for FeOffset {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
         let bounds = self
             .base
             .get_bounds(ctx)
diff --git a/rsvg_internals/src/filters/tile.rs b/rsvg_internals/src/filters/tile.rs
index 06239ce2..c8dbfdc4 100644
--- a/rsvg_internals/src/filters/tile.rs
+++ b/rsvg_internals/src/filters/tile.rs
@@ -1,3 +1,4 @@
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::node::{NodeResult, NodeTrait, RsvgNode};
 use crate::property_bag::PropertyBag;
@@ -33,9 +34,10 @@ impl FilterEffect for FeTile {
         &self,
         _node: &RsvgNode,
         ctx: &FilterContext,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input = self.base.get_input(ctx, draw_ctx)?;
+        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
 
         // feTile doesn't consider its inputs in the filter primitive subregion calculation.
         let bounds = self.base.get_bounds(ctx).into_irect(draw_ctx);
diff --git a/rsvg_internals/src/filters/turbulence.rs b/rsvg_internals/src/filters/turbulence.rs
index b3828c5c..d7fa55eb 100644
--- a/rsvg_internals/src/filters/turbulence.rs
+++ b/rsvg_internals/src/filters/turbulence.rs
@@ -1,6 +1,7 @@
 use cssparser::Parser;
 use markup5ever::{expanded_name, local_name, namespace_url, ns};
 
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
@@ -335,6 +336,7 @@ impl FilterEffect for FeTurbulence {
         &self,
         node: &RsvgNode,
         ctx: &FilterContext,
+        _acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
         let bounds = self.base.get_bounds(ctx).into_irect(draw_ctx);
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index 782c7c5e..c6741659 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -7,7 +7,8 @@ use std::cell::RefCell;
 use crate::allowed_url::Fragment;
 use crate::bbox::*;
 use crate::coord_units::CoordUnits;
-use crate::drawing_ctx::{AcquiredNode, DrawingCtx, NodeStack, ViewParams};
+use crate::document::{AcquiredNode, AcquiredNodes, NodeStack};
+use crate::drawing_ctx::{DrawingCtx, ViewParams};
 use crate::error::*;
 use crate::length::*;
 use crate::node::{CascadedValues, NodeResult, NodeTrait, NodeType, RsvgNode};
@@ -637,7 +638,7 @@ macro_rules! impl_paint_source {
             fn resolve(
                 &self,
                 node: &RsvgNode,
-                draw_ctx: &mut DrawingCtx,
+                acquired_nodes: &mut AcquiredNodes,
             ) -> Result<Self::Resolved, AcquireError> {
                 let mut resolved = self.common.resolved.borrow_mut();
                 if let Some(ref gradient) = *resolved {
@@ -653,7 +654,7 @@ macro_rules! impl_paint_source {
 
                 while !gradient.is_resolved() {
                     if let Some(fragment) = fallback {
-                        let acquired = acquire_gradient(draw_ctx, &fragment)?;
+                        let acquired = acquire_gradient(acquired_nodes, &fragment)?;
                         let acquired_node = acquired.get();
 
                         if stack.contains(acquired_node) {
@@ -712,6 +713,7 @@ impl_paint_source!(
 impl AsPaintSource for Gradient {
     fn set_as_paint_source(
         self,
+        _acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
         draw_ctx: &mut DrawingCtx,
         opacity: UnitInterval,
@@ -794,11 +796,11 @@ impl Gradient {
 }
 
 /// Acquires a node of linearGradient or radialGradient type
-fn acquire_gradient<'a>(
-    draw_ctx: &'a mut DrawingCtx,
+fn acquire_gradient(
+    acquired_nodes: &mut AcquiredNodes,
     fragment: &Fragment,
 ) -> Result<AcquiredNode, AcquireError> {
-    draw_ctx.acquire_node(
+    acquired_nodes.acquire(
         fragment,
         &[NodeType::LinearGradient, NodeType::RadialGradient],
     )
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index f887f0db..a615654d 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -10,7 +10,7 @@ use locale_config::{LanguageRange, Locale};
 use crate::allowed_url::{AllowedUrl, Href};
 use crate::bbox::BoundingBox;
 use crate::css::{Origin, Stylesheet};
-use crate::document::Document;
+use crate::document::{AcquiredNodes, Document};
 use crate::dpi::Dpi;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::{DefsLookupErrorKind, LoadingError, RenderingError};
@@ -203,11 +203,7 @@ impl Handle {
         cancellable: Option<&gio::Cancellable>,
     ) -> Result<Handle, LoadingError> {
         Ok(Handle {
-            document: Document::load_from_stream(
-                load_options,
-                stream,
-                cancellable,
-            )?,
+            document: Document::load_from_stream(load_options, stream, cancellable)?,
         })
     }
 
@@ -300,21 +296,18 @@ impl Handle {
         dpi: Dpi,
         is_testing: bool,
     ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> {
+        let root = self.document.root();
+
         let target = cairo::ImageSurface::create(cairo::Format::Rgb24, 1, 1)?;
         let cr = cairo::Context::new(&target);
-        let mut draw_ctx = DrawingCtx::new(
-            &self.document,
-            Some(node),
-            &cr,
-            viewport,
-            dpi,
-            true,
-            is_testing,
-        );
-        let root = self.document.root();
+        let mut draw_ctx = DrawingCtx::new(Some(node), &cr, viewport, dpi, true, is_testing);
 
-        let bbox =
-            draw_ctx.draw_node_from_stack(&CascadedValues::new_from_node(&root), &root, false)?;
+        let bbox = draw_ctx.draw_node_from_stack(
+            &root,
+            &mut AcquiredNodes::new(&self.document),
+            &CascadedValues::new_from_node(&root),
+            false,
+        )?;
 
         let ink_rect = bbox.ink_rect.unwrap_or_default();
         let logical_rect = bbox.rect.unwrap_or_default();
@@ -462,7 +455,6 @@ impl Handle {
 
         cr.save();
         let mut draw_ctx = DrawingCtx::new(
-            &self.document,
             node.as_ref(),
             cr,
             Rect::from(*viewport),
@@ -470,10 +462,16 @@ impl Handle {
             false,
             is_testing,
         );
-        let cascaded = CascadedValues::new_from_node(&root);
+
         let res = draw_ctx
-            .draw_node_from_stack(&cascaded, &root, false)
+            .draw_node_from_stack(
+                &root,
+                &mut AcquiredNodes::new(&self.document),
+                &CascadedValues::new_from_node(&root),
+                false,
+            )
             .map(|_bbox| ());
+
         cr.restore();
 
         res
@@ -487,18 +485,14 @@ impl Handle {
     ) -> Result<BoundingBox, RenderingError> {
         let target = cairo::ImageSurface::create(cairo::Format::Rgb24, 1, 1)?;
         let cr = cairo::Context::new(&target);
+        let mut draw_ctx = DrawingCtx::new(None, &cr, unit_rectangle(), dpi, true, is_testing);
 
-        let mut draw_ctx = DrawingCtx::new(
-            &self.document,
-            None,
-            &cr,
-            unit_rectangle(),
-            dpi,
-            true,
-            is_testing,
-        );
-
-        draw_ctx.draw_node_from_stack(&CascadedValues::new_from_node(node), node, false)
+        draw_ctx.draw_node_from_stack(
+            node,
+            &mut AcquiredNodes::new(&self.document),
+            &CascadedValues::new_from_node(node),
+            false,
+        )
     }
 
     /// Returns (ink_rect, logical_rect)
@@ -560,18 +554,15 @@ impl Handle {
         cr.scale(factor, factor);
         cr.translate(-ink_r.x0, -ink_r.y0);
 
-        let mut draw_ctx = DrawingCtx::new(
-            &self.document,
-            None,
-            &cr,
-            unit_rectangle(),
-            dpi,
-            false,
-            is_testing,
-        );
+        let mut draw_ctx = DrawingCtx::new(None, &cr, unit_rectangle(), dpi, false, is_testing);
 
         let res = draw_ctx
-            .draw_node_from_stack(&CascadedValues::new_from_node(&node), &node, false)
+            .draw_node_from_stack(
+                &node,
+                &mut AcquiredNodes::new(&self.document),
+                &CascadedValues::new_from_node(&node),
+                false,
+            )
             .map(|_bbox| ());
 
         cr.restore();
diff --git a/rsvg_internals/src/image.rs b/rsvg_internals/src/image.rs
index 94b6d1e6..6e282108 100644
--- a/rsvg_internals/src/image.rs
+++ b/rsvg_internals/src/image.rs
@@ -5,6 +5,7 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 use crate::allowed_url::Href;
 use crate::aspect_ratio::AspectRatio;
 use crate::bbox::BoundingBox;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
 use crate::error::*;
 use crate::length::*;
@@ -62,6 +63,7 @@ impl NodeTrait for Image {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -75,9 +77,10 @@ impl NodeTrait for Image {
             return Ok(draw_ctx.empty_bbox());
         }
 
-        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
+        draw_ctx.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
             let surface = if let Some(Href::PlainUrl(ref url)) = self.href {
-                dc.lookup_image(&url)?
+                an.lookup_image(&url)
+                    .map_err(|_| RenderingError::InvalidHref)?
             } else {
                 return Ok(dc.empty_bbox());
             };
diff --git a/rsvg_internals/src/marker.rs b/rsvg_internals/src/marker.rs
index 001336fe..bac0efc3 100644
--- a/rsvg_internals/src/marker.rs
+++ b/rsvg_internals/src/marker.rs
@@ -10,6 +10,7 @@ use crate::allowed_url::Fragment;
 use crate::angle::Angle;
 use crate::aspect_ratio::*;
 use crate::bbox::BoundingBox;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
@@ -99,6 +100,7 @@ impl Marker {
     fn render(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         xpos: f64,
         ypos: f64,
@@ -164,8 +166,8 @@ impl Marker {
 
         draw_ctx.with_saved_transform(Some(transform), &mut |dc| {
             dc.with_clip_rect(clip, &mut |dc| {
-                dc.with_discrete_layer(node, values, clipping, &mut |dc| {
-                    node.draw_children(&cascaded, dc, clipping)
+                dc.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
+                    node.draw_children(an, &cascaded, dc, clipping)
                 })
             })
         })
@@ -560,6 +562,7 @@ enum MarkerType {
 
 fn emit_marker_by_name(
     draw_ctx: &mut DrawingCtx,
+    acquired_nodes: &mut AcquiredNodes,
     name: &Fragment,
     xpos: f64,
     ypos: f64,
@@ -567,11 +570,12 @@ fn emit_marker_by_name(
     line_width: f64,
     clipping: bool,
 ) -> Result<BoundingBox, RenderingError> {
-    if let Ok(acquired) = draw_ctx.acquire_node(name, &[NodeType::Marker]) {
+    if let Ok(acquired) = acquired_nodes.acquire(name, &[NodeType::Marker]) {
         let node = acquired.get();
 
         node.borrow().get_impl::<Marker>().render(
             &node,
+            acquired_nodes,
             draw_ctx,
             xpos,
             ypos,
@@ -616,6 +620,7 @@ where
 pub fn render_markers_for_path_builder(
     builder: &PathBuilder,
     draw_ctx: &mut DrawingCtx,
+    acquired_nodes: &mut AcquiredNodes,
     values: &ComputedValues,
     clipping: bool,
 ) -> Result<BoundingBox, RenderingError> {
@@ -645,7 +650,16 @@ pub fn render_markers_for_path_builder(
                 MarkerType::Middle => &values.marker_mid.0,
                 MarkerType::End => &values.marker_end.0,
             } {
-                emit_marker_by_name(draw_ctx, marker, x, y, computed_angle, line_width, clipping)
+                emit_marker_by_name(
+                    draw_ctx,
+                    acquired_nodes,
+                    marker,
+                    x,
+                    y,
+                    computed_angle,
+                    line_width,
+                    clipping,
+                )
             } else {
                 Ok(draw_ctx.empty_bbox())
             }
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index 07f932e2..67bee167 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -22,6 +22,7 @@ use std::fmt;
 use crate::bbox::BoundingBox;
 use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
 use crate::css::Declaration;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::filters::FilterEffect;
@@ -360,6 +361,7 @@ pub trait NodeTrait: Downcast {
     fn draw(
         &self,
         _node: &RsvgNode,
+        _acquired_nodes: &mut AcquiredNodes,
         _cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         _clipping: bool,
@@ -482,6 +484,7 @@ impl NodeCascade for RsvgNode {
 pub trait NodeDraw {
     fn draw(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -489,6 +492,7 @@ pub trait NodeDraw {
 
     fn draw_children(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -498,6 +502,7 @@ pub trait NodeDraw {
 impl NodeDraw for RsvgNode {
     fn draw(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -507,7 +512,7 @@ impl NodeDraw for RsvgNode {
             draw_ctx.with_saved_transform(Some(transform), &mut |dc| {
                 self.borrow()
                     .get_node_trait()
-                    .draw(self, cascaded, dc, clipping)
+                    .draw(self, acquired_nodes, cascaded, dc, clipping)
             })
         } else {
             rsvg_log!("(not rendering element {} because it is in error)", self);
@@ -519,6 +524,7 @@ impl NodeDraw for RsvgNode {
 
     fn draw_children(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -527,8 +533,9 @@ impl NodeDraw for RsvgNode {
 
         for child in self.children() {
             let child_bbox = draw_ctx.draw_node_from_stack(
-                &CascadedValues::new(cascaded, &child),
                 &child,
+                acquired_nodes,
+                &CascadedValues::new(cascaded, &child),
                 clipping,
             )?;
             bbox.insert(&child_bbox);
diff --git a/rsvg_internals/src/paint_server.rs b/rsvg_internals/src/paint_server.rs
index 61f17d98..0c97f0c3 100644
--- a/rsvg_internals/src/paint_server.rs
+++ b/rsvg_internals/src/paint_server.rs
@@ -4,6 +4,7 @@ use cssparser::Parser;
 
 use crate::allowed_url::Fragment;
 use crate::bbox::BoundingBox;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::node::{CascadedValues, RsvgNode};
@@ -62,21 +63,22 @@ pub trait PaintSource {
     fn resolve(
         &self,
         node: &RsvgNode,
-        draw_ctx: &mut DrawingCtx,
+        acquired_nodes: &mut AcquiredNodes,
     ) -> Result<Self::Resolved, AcquireError>;
 
     fn resolve_fallbacks_and_set_pattern(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         opacity: UnitInterval,
         bbox: &BoundingBox,
     ) -> Result<bool, RenderingError> {
-        match self.resolve(&node, draw_ctx) {
+        match self.resolve(&node, acquired_nodes) {
             Ok(resolved) => {
                 let cascaded = CascadedValues::new_from_node(node);
                 let values = cascaded.get();
-                resolved.set_as_paint_source(values, draw_ctx, opacity, bbox)
+                resolved.set_as_paint_source(acquired_nodes, values, draw_ctx, opacity, bbox)
             }
 
             Err(AcquireError::CircularReference(node)) => {
@@ -102,6 +104,7 @@ pub trait PaintSource {
 pub trait AsPaintSource {
     fn set_as_paint_source(
         self,
+        acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
         draw_ctx: &mut DrawingCtx,
         opacity: UnitInterval,
diff --git a/rsvg_internals/src/pattern.rs b/rsvg_internals/src/pattern.rs
index 8337a073..38aeb765 100644
--- a/rsvg_internals/src/pattern.rs
+++ b/rsvg_internals/src/pattern.rs
@@ -8,7 +8,8 @@ use crate::allowed_url::Fragment;
 use crate::aspect_ratio::*;
 use crate::bbox::*;
 use crate::coord_units::CoordUnits;
-use crate::drawing_ctx::{DrawingCtx, NodeStack, ViewParams};
+use crate::document::{AcquiredNodes, NodeStack};
+use crate::drawing_ctx::{DrawingCtx, ViewParams};
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::length::*;
@@ -163,7 +164,7 @@ impl PaintSource for Pattern {
     fn resolve(
         &self,
         node: &RsvgNode,
-        draw_ctx: &mut DrawingCtx,
+        acquired_nodes: &mut AcquiredNodes,
     ) -> Result<Self::Resolved, AcquireError> {
         let mut resolved = self.resolved.borrow_mut();
         if let Some(ref pattern) = *resolved {
@@ -179,7 +180,7 @@ impl PaintSource for Pattern {
 
         while !pattern.is_resolved() {
             if let Some(ref fragment) = fallback {
-                match draw_ctx.acquire_node(&fragment, &[NodeType::Pattern]) {
+                match acquired_nodes.acquire(&fragment, &[NodeType::Pattern]) {
                     Ok(acquired) => {
                         let acquired_node = acquired.get();
 
@@ -224,6 +225,7 @@ impl PaintSource for Pattern {
 impl AsPaintSource for ResolvedPattern {
     fn set_as_paint_source(
         self,
+        acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
         draw_ctx: &mut DrawingCtx,
         opacity: UnitInterval,
@@ -356,10 +358,13 @@ impl AsPaintSource for ResolvedPattern {
             cr_pattern.push_group();
         }
 
-        let res =
-            draw_ctx.with_discrete_layer(&node_with_children, pattern_values, false, &mut |dc| {
-                node_with_children.draw_children(&pattern_cascaded, dc, false)
-            });
+        let res = draw_ctx.with_discrete_layer(
+            &node_with_children,
+            acquired_nodes,
+            pattern_values,
+            false,
+            &mut |an, dc| node_with_children.draw_children(an, &pattern_cascaded, dc, false),
+        );
 
         if o < 1.0 {
             cr_pattern.pop_group_to_source();
diff --git a/rsvg_internals/src/shapes.rs b/rsvg_internals/src/shapes.rs
index 5d5429bc..ea0c5888 100644
--- a/rsvg_internals/src/shapes.rs
+++ b/rsvg_internals/src/shapes.rs
@@ -6,6 +6,7 @@ use std::ops::Deref;
 use std::rc::Rc;
 
 use crate::bbox::BoundingBox;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::length::*;
@@ -36,11 +37,19 @@ impl Shape {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
-        draw_ctx.draw_path(&self.builder, node, values, self.markers, clipping)
+        draw_ctx.draw_path(
+            &self.builder,
+            node,
+            acquired_nodes,
+            values,
+            self.markers,
+            clipping,
+        )
     }
 }
 
@@ -126,13 +135,20 @@ impl NodeTrait for Path {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         if let Some(builder) = self.builder.as_ref() {
             let values = cascaded.get();
-            Shape::new(builder.clone(), Markers::Yes).draw(node, values, draw_ctx, clipping)
+            Shape::new(builder.clone(), Markers::Yes).draw(
+                node,
+                acquired_nodes,
+                values,
+                draw_ctx,
+                clipping,
+            )
         } else {
             Ok(draw_ctx.empty_bbox())
         }
@@ -216,13 +232,19 @@ impl NodeTrait for Polygon {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         let values = cascaded.get();
-        Shape::new(Rc::new(make_poly(self.points.as_ref(), true)), Markers::Yes)
-            .draw(node, values, draw_ctx, clipping)
+        Shape::new(Rc::new(make_poly(self.points.as_ref(), true)), Markers::Yes).draw(
+            node,
+            acquired_nodes,
+            values,
+            draw_ctx,
+            clipping,
+        )
     }
 }
 
@@ -245,6 +267,7 @@ impl NodeTrait for Polyline {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -254,7 +277,7 @@ impl NodeTrait for Polyline {
             Rc::new(make_poly(self.points.as_ref(), false)),
             Markers::Yes,
         )
-        .draw(node, values, draw_ctx, clipping)
+        .draw(node, acquired_nodes, values, draw_ctx, clipping)
     }
 }
 
@@ -284,6 +307,7 @@ impl NodeTrait for Line {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -293,7 +317,7 @@ impl NodeTrait for Line {
             Rc::new(self.make_path_builder(values, draw_ctx)),
             Markers::Yes,
         )
-        .draw(node, values, draw_ctx, clipping)
+        .draw(node, acquired_nodes, values, draw_ctx, clipping)
     }
 }
 
@@ -361,6 +385,7 @@ impl NodeTrait for Rect {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -370,7 +395,7 @@ impl NodeTrait for Rect {
             Rc::new(self.make_path_builder(values, draw_ctx)),
             Markers::No,
         )
-        .draw(node, values, draw_ctx, clipping)
+        .draw(node, acquired_nodes, values, draw_ctx, clipping)
     }
 }
 
@@ -573,6 +598,7 @@ impl NodeTrait for Circle {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -582,7 +608,7 @@ impl NodeTrait for Circle {
             Rc::new(self.make_path_builder(values, draw_ctx)),
             Markers::No,
         )
-        .draw(node, values, draw_ctx, clipping)
+        .draw(node, acquired_nodes, values, draw_ctx, clipping)
     }
 }
 
@@ -630,6 +656,7 @@ impl NodeTrait for Ellipse {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -639,7 +666,7 @@ impl NodeTrait for Ellipse {
             Rc::new(self.make_path_builder(values, draw_ctx)),
             Markers::No,
         )
-        .draw(node, values, draw_ctx, clipping)
+        .draw(node, acquired_nodes, values, draw_ctx, clipping)
     }
 }
 
diff --git a/rsvg_internals/src/structure.rs b/rsvg_internals/src/structure.rs
index 765c25d2..a1034f97 100644
--- a/rsvg_internals/src/structure.rs
+++ b/rsvg_internals/src/structure.rs
@@ -6,6 +6,7 @@ use crate::allowed_url::Fragment;
 use crate::aspect_ratio::*;
 use crate::bbox::BoundingBox;
 use crate::coord_units::CoordUnits;
+use crate::document::AcquiredNodes;
 use crate::dpi::Dpi;
 use crate::drawing_ctx::{ClipMode, DrawingCtx, ViewParams};
 use crate::error::*;
@@ -28,14 +29,15 @@ impl NodeTrait for Group {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         let values = cascaded.get();
 
-        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
-            node.draw_children(cascaded, dc, clipping)
+        draw_ctx.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
+            node.draw_children(an, cascaded, dc, clipping)
         })
     }
 }
@@ -64,19 +66,25 @@ impl NodeTrait for Switch {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
         let values = cascaded.get();
 
-        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
+        draw_ctx.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
             if let Some(child) = node
                 .children()
                 .filter(|c| c.borrow().get_type() != NodeType::Chars)
                 .find(|c| c.borrow().get_cond())
             {
-                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
+                dc.draw_node_from_stack(
+                    &child,
+                    an,
+                    &CascadedValues::new(cascaded, &child),
+                    clipping,
+                )
             } else {
                 Ok(dc.empty_bbox())
             }
@@ -211,6 +219,7 @@ impl NodeTrait for Svg {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -253,11 +262,11 @@ impl NodeTrait for Svg {
             )
         };
 
-        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
+        draw_ctx.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
             let _params =
                 dc.push_new_viewport(vbox, viewport, self.preserve_aspect_ratio, clip_mode);
 
-            node.draw_children(cascaded, dc, clipping)
+            node.draw_children(an, cascaded, dc, clipping)
         })
     }
 }
@@ -327,11 +336,12 @@ impl NodeTrait for Use {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
-        draw_ctx.draw_from_use_node(node, cascaded, clipping)
+        draw_ctx.draw_from_use_node(node, acquired_nodes, cascaded, clipping)
     }
 }
 
@@ -488,6 +498,7 @@ impl NodeTrait for Link {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -495,11 +506,13 @@ impl NodeTrait for Link {
         let cascaded = CascadedValues::new(cascaded, node);
         let values = cascaded.get();
 
-        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| match self.link.as_ref() {
-            Some(l) if !l.is_empty() => {
-                dc.with_link_tag(l, &mut |dc| node.draw_children(&cascaded, dc, clipping))
+        draw_ctx.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
+            match self.link.as_ref() {
+                Some(l) if !l.is_empty() => {
+                    dc.with_link_tag(l, &mut |dc| node.draw_children(an, &cascaded, dc, clipping))
+                }
+                _ => node.draw_children(an, &cascaded, dc, clipping),
             }
-            _ => node.draw_children(&cascaded, dc, clipping),
         })
     }
 }
diff --git a/rsvg_internals/src/surface_utils/shared_surface.rs 
b/rsvg_internals/src/surface_utils/shared_surface.rs
index ef7d089e..393c0542 100644
--- a/rsvg_internals/src/surface_utils/shared_surface.rs
+++ b/rsvg_internals/src/surface_utils/shared_surface.rs
@@ -1240,10 +1240,7 @@ impl ImageSurface<Exclusive> {
 
     /// Modify the image data
     #[inline]
-    pub fn modify(
-        &mut self,
-        draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData, usize),
-    ) {
+    pub fn modify(&mut self, draw_fn: &mut dyn FnMut(&mut cairo::ImageSurfaceData, usize)) {
         let stride = self.stride() as usize;
         let mut data = self.get_data();
 
diff --git a/rsvg_internals/src/text.rs b/rsvg_internals/src/text.rs
index 6870cc58..df22b764 100644
--- a/rsvg_internals/src/text.rs
+++ b/rsvg_internals/src/text.rs
@@ -6,6 +6,7 @@ use std::cell::RefCell;
 
 use crate::allowed_url::Fragment;
 use crate::bbox::BoundingBox;
+use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::float_eq_cairo::ApproxEqCairo;
@@ -267,6 +268,7 @@ impl PositionedSpan {
 
     fn draw(
         &self,
+        acquired_nodes: &mut AcquiredNodes,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
@@ -299,6 +301,7 @@ impl PositionedSpan {
 
             let res = if !clipping {
                 dc.set_source_paint_server(
+                    acquired_nodes,
                     &self.values.fill.0,
                     self.values.fill_opacity.0,
                     &bbox,
@@ -320,6 +323,7 @@ impl PositionedSpan {
 
                 let res = if !clipping {
                     dc.set_source_paint_server(
+                        acquired_nodes,
                         &self.values.stroke.0,
                         self.values.stroke_opacity.0,
                         &bbox,
@@ -416,6 +420,7 @@ fn gravity_is_vertical(gravity: pango::Gravity) -> bool {
 fn children_to_chunks(
     chunks: &mut Vec<Chunk>,
     node: &RsvgNode,
+    acquired_nodes: &mut AcquiredNodes,
     cascaded: &CascadedValues<'_>,
     draw_ctx: &mut DrawingCtx,
     dx: Option<Length<Horizontal>>,
@@ -436,6 +441,7 @@ fn children_to_chunks(
                 let cascaded = CascadedValues::new(cascaded, &child);
                 child.borrow().get_impl::<TSpan>().to_chunks(
                     &child,
+                    acquired_nodes,
                     &cascaded,
                     draw_ctx,
                     chunks,
@@ -447,8 +453,8 @@ fn children_to_chunks(
                 let cascaded = CascadedValues::new(cascaded, &child);
                 child.borrow().get_impl::<TRef>().to_chunks(
                     &child,
+                    acquired_nodes,
                     &cascaded,
-                    draw_ctx,
                     chunks,
                     depth + 1,
                 );
@@ -580,12 +586,22 @@ impl Text {
     fn make_chunks(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Vec<Chunk> {
         let mut chunks = Vec::new();
         chunks.push(Chunk::new(cascaded.get(), Some(self.x), Some(self.y)));
-        children_to_chunks(&mut chunks, node, cascaded, draw_ctx, self.dx, self.dy, 0);
+        children_to_chunks(
+            &mut chunks,
+            node,
+            acquired_nodes,
+            cascaded,
+            draw_ctx,
+            self.dx,
+            self.dy,
+            0,
+        );
         chunks
     }
 }
@@ -608,6 +624,7 @@ impl NodeTrait for Text {
     fn draw(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         clipping: bool,
@@ -618,7 +635,7 @@ impl NodeTrait for Text {
         let mut x = self.x.normalize(values, &params);
         let mut y = self.y.normalize(values, &params);
 
-        let chunks = self.make_chunks(node, cascaded, draw_ctx);
+        let chunks = self.make_chunks(node, acquired_nodes, cascaded, draw_ctx);
 
         let mut measured_chunks = Vec::new();
         for chunk in &chunks {
@@ -642,12 +659,12 @@ impl NodeTrait for Text {
             positioned_chunks.push(positioned);
         }
 
-        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
+        draw_ctx.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
             let mut bbox = dc.empty_bbox();
 
             for chunk in &positioned_chunks {
                 for span in &chunk.spans {
-                    let span_bbox = span.draw(dc, clipping)?;
+                    let span_bbox = span.draw(an, dc, clipping)?;
                     bbox.insert(&span_bbox);
                 }
             }
@@ -666,8 +683,8 @@ impl TRef {
     fn to_chunks(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
-        draw_ctx: &mut DrawingCtx,
         chunks: &mut Vec<Chunk>,
         depth: usize,
     ) {
@@ -678,7 +695,7 @@ impl TRef {
         let link = self.link.as_ref().unwrap();
         let values = cascaded.get();
 
-        if let Ok(acquired) = draw_ctx.acquire_node(link, &[]) {
+        if let Ok(acquired) = acquired_nodes.acquire(link, &[]) {
             let c = acquired.get();
             extract_chars_children_to_chunks_recursively(chunks, &c, values, depth);
         } else {
@@ -735,6 +752,7 @@ impl TSpan {
     fn to_chunks(
         &self,
         node: &RsvgNode,
+        acquired_nodes: &mut AcquiredNodes,
         cascaded: &CascadedValues<'_>,
         draw_ctx: &mut DrawingCtx,
         chunks: &mut Vec<Chunk>,
@@ -746,7 +764,16 @@ impl TSpan {
             chunks.push(Chunk::new(values, self.x, self.y));
         }
 
-        children_to_chunks(chunks, node, cascaded, draw_ctx, self.dx, self.dy, depth);
+        children_to_chunks(
+            chunks,
+            node,
+            acquired_nodes,
+            cascaded,
+            draw_ctx,
+            self.dx,
+            self.dy,
+            depth,
+        );
     }
 }
 
diff --git a/rsvg_internals/src/xml.rs b/rsvg_internals/src/xml.rs
index 570b895f..ad5456de 100644
--- a/rsvg_internals/src/xml.rs
+++ b/rsvg_internals/src/xml.rs
@@ -401,7 +401,15 @@ impl XmlState {
         for (attr, value) in pbag.iter() {
             match attr.expanded() {
                 expanded_name!("", "href") => href = Some(value),
-                ref v if *v == ExpandedName { ns: &ns!(), local: &ln_parse } => parse = Some(value),
+                ref v
+                    if *v
+                        == ExpandedName {
+                            ns: &ns!(),
+                            local: &ln_parse,
+                        } =>
+                {
+                    parse = Some(value)
+                }
                 expanded_name!("", "encoding") => encoding = Some(value),
                 _ => (),
             }



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