[librsvg/librsvg-2.44] (#398): Detect circular references in gradients
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/librsvg-2.44] (#398): Detect circular references in gradients
- Date: Mon, 21 Jan 2019 16:57:24 +0000 (UTC)
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]