[librsvg/librsvg-2.44] (#398): Detect circular references in gradients



commit a4049ca23160c6e1bc933e67d67188c41173892f
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Jan 2 12:05:22 2019 -0600

    (#398): Detect circular references in gradients
    
    Similar to pattern.rs, we need to detect cycles in gradient
    references.
    
    Fixes https://gitlab.gnome.org/GNOME/librsvg/issues/398

 rsvg_internals/src/gradient.rs                     | 67 +++++++++++++---------
 .../infinite-loop/398-recursive-gradient.svg       |  5 ++
 2 files changed, 44 insertions(+), 28 deletions(-)
---
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index a14d9e0d..04d26f48 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -6,7 +6,7 @@ use std::cell::RefCell;
 use attributes::Attribute;
 use bbox::*;
 use coord_units::CoordUnits;
-use drawing_ctx::{AcquiredNode, DrawingCtx};
+use drawing_ctx::{AcquiredNode, DrawingCtx, NodeStack};
 use error::*;
 use handle::RsvgHandle;
 use length::*;
@@ -425,42 +425,53 @@ impl Gradient {
     }
 }
 
-fn acquire_gradient<'a>(draw_ctx: &'a mut DrawingCtx<'_>, name: &str) -> Option<AcquiredNode> {
-    if let Some(acquired) = draw_ctx.get_acquired_node(name) {
-        let node_type = acquired.get().get_type();
+fn acquire_gradient<'a>(
+    draw_ctx: &'a mut DrawingCtx<'_>,
+    name: Option<&str>,
+) -> Option<AcquiredNode> {
+    name.and_then(move |name| draw_ctx.get_acquired_node(name))
+        .and_then(|acquired| {
+            let node_type = acquired.get().get_type();
 
-        if node_type == NodeType::LinearGradient || node_type == NodeType::RadialGradient {
-            return Some(acquired);
-        }
-    }
-
-    rsvg_log!("element \"{}\" does not exist or is not a gradient", name);
-
-    None
+            if node_type == NodeType::LinearGradient || node_type == NodeType::RadialGradient {
+                Some(acquired)
+            } else {
+                None
+            }
+        })
 }
 
 fn resolve_gradient(gradient: &Gradient, draw_ctx: &mut DrawingCtx<'_>) -> Gradient {
     let mut result = gradient.clone();
 
+    let mut stack = NodeStack::new();
+
     while !result.is_resolved() {
-        result
-            .common
-            .fallback
-            .as_ref()
-            .and_then(|fallback_name| acquire_gradient(draw_ctx, fallback_name))
-            .and_then(|acquired| {
-                let fallback_node = acquired.get();
-
-                fallback_node.with_impl(|i: &NodeGradient| {
-                    let fallback_grad = i.get_gradient_with_color_stops_from_node(&fallback_node);
-                    result.resolve_from_fallback(&fallback_grad)
-                });
-                Some(())
-            })
-            .or_else(|| {
+        if let Some(acquired) = acquire_gradient(
+            draw_ctx,
+            result.common.fallback.as_ref().map(String::as_ref),
+        ) {
+            let a_node = acquired.get();
+
+            if stack.contains(a_node) {
+                rsvg_log!(
+                    "circular reference in gradient {}",
+                    a_node.get_human_readable_name()
+                );
                 result.resolve_from_defaults();
-                Some(())
+                break; // reference cycle; bail out
+            }
+
+            a_node.with_impl(|i: &NodeGradient| {
+                let fallback_grad = i.get_gradient_with_color_stops_from_node(&a_node);
+                result.resolve_from_fallback(&fallback_grad)
             });
+
+            stack.push(a_node);
+            continue;
+        }
+
+        result.resolve_from_defaults();
     }
 
     result
diff --git a/tests/fixtures/infinite-loop/398-recursive-gradient.svg 
b/tests/fixtures/infinite-loop/398-recursive-gradient.svg
new file mode 100644
index 00000000..dbe2e33d
--- /dev/null
+++ b/tests/fixtures/infinite-loop/398-recursive-gradient.svg
@@ -0,0 +1,5 @@
+<svg>
+  <linearGradient id="g1" xlink:href="#g1"/>
+  <linearGradient id="g2" xlink:href="#g1"/>
+  <rect fill="url(#g2)" width="100" height="100"/>
+</svg>


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