[librsvg: 14/53] Make <use> with circular references render nothing, instead of exiting early




commit e0ad4b2ebee83bb7493d25cd799009ed92c15c43
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Oct 13 18:09:37 2020 -0500

    Make <use> with circular references render nothing, instead of exiting early
    
    https://www.w3.org/TR/SVG2/struct.html#UseElement says this:
    
      If the referenced element is a (shadow-including) ancestor of the
      ‘use’ element, then this is an invalid circular reference and the
      ‘use’ element is in error.
    
    So, we'll just render nothing instead of exiting early with a
    CircularReference error, as is customary for elements in error.
    
    This moves the 308-*.svg tests to Rust, and removes them from the C tests.

 librsvg_crate/tests/standalone/bugs.rs             | 87 +++++++++++++++++++++-
 rsvg_internals/src/drawing_ctx.rs                  | 16 ++--
 tests/errors.c                                     | 13 ----
 tests/fixtures/errors/308-doubly-recursive-use.svg | 13 ----
 tests/fixtures/errors/308-recursive-use.svg        |  9 ---
 tests/fixtures/errors/308-use-self-ref.svg         |  7 --
 6 files changed, 95 insertions(+), 50 deletions(-)
---
diff --git a/librsvg_crate/tests/standalone/bugs.rs b/librsvg_crate/tests/standalone/bugs.rs
index 3e5a9eec..1debc7e6 100644
--- a/librsvg_crate/tests/standalone/bugs.rs
+++ b/librsvg_crate/tests/standalone/bugs.rs
@@ -1,5 +1,5 @@
 use cairo;
-use librsvg::LoadingError;
+use librsvg::{LoadingError, SvgHandle};
 use matches::matches;
 use rsvg_internals::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
 
@@ -251,3 +251,88 @@ fn recursive_paint_servers_fallback_to_color() {
         "recursive_paint_servers_fallback_to_color",
     );
 }
+
+fn test_renders_as_empty(svg: &SvgHandle, test_name: &str) {
+    let output_surf = render_document(
+        &svg,
+        SurfaceSize(100, 100),
+        |_| (),
+        cairo::Rectangle {
+            x: 0.0,
+            y: 0.0,
+            width: 100.0,
+            height: 100.0,
+        },
+    )
+    .unwrap();
+
+    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 100, 100).unwrap();
+    let reference_surf = SharedImageSurface::wrap(reference_surf, SurfaceType::SRgb).unwrap();
+
+    compare_to_surface(&output_surf, &reference_surf, test_name);
+}
+
+// https://gitlab.gnome.org/GNOME/librsvg/-/issues/308
+#[test]
+fn recursive_use() {
+    let svg = load_svg(
+        br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns:xlink="http://www.w3.org/1999/xlink";>
+  <defs>
+    <g id="one">
+      <use xlink:href="#one"/>
+    </g>
+  </defs>
+
+  <use xlink:href="#one"/>
+</svg>
+"##,
+    )
+    .unwrap();
+
+    test_renders_as_empty(&svg, "308-recursive-use");
+}
+
+// https://gitlab.gnome.org/GNOME/librsvg/-/issues/308
+#[test]
+fn use_self_ref() {
+    let svg = load_svg(
+        br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns:xlink="http://www.w3.org/1999/xlink";>
+  <defs>
+    <use id="one" xlink:href="#one"/>
+  </defs>
+
+  <use xlink:href="#one"/>
+</svg>
+"##,
+    )
+    .unwrap();
+
+    test_renders_as_empty(&svg, "308-use-self-ref");
+}
+
+// https://gitlab.gnome.org/GNOME/librsvg/-/issues/308
+#[test]
+fn doubly_recursive_use() {
+    let svg = load_svg(
+        br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns:xlink="http://www.w3.org/1999/xlink";>
+  <defs>
+    <g id="one">
+      <use xlink:href="#two"/>
+    </g>
+
+    <g id="two">
+      <use xlink:href="#one"/>
+    </g>
+  </defs>
+
+  <use xlink:href="#one"/>
+</svg>
+"##,
+    )
+    .unwrap();
+
+    test_renders_as_empty(&svg, "308-doubly-recursive-use");
+}
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index f4431e4b..c56effb5 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -1645,14 +1645,16 @@ impl DrawingCtx {
         // 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 = acquired_nodes.acquire_ref(node).map_err(|e| {
-            if let AcquireError::CircularReference(_) = e {
+        let _self_acquired = match acquired_nodes.acquire_ref(node) {
+            Ok(n) => n,
+
+            Err(AcquireError::CircularReference(_)) => {
                 rsvg_log!("circular reference in element {}", node);
-                RenderingError::CircularReference
-            } else {
-                unreachable!();
+                return Ok(self.empty_bbox());
             }
-        })?;
+
+            _ => unreachable!(),
+        };
 
         if link.is_none() {
             return Ok(self.empty_bbox());
@@ -1663,7 +1665,7 @@ impl DrawingCtx {
 
             Err(AcquireError::CircularReference(node)) => {
                 rsvg_log!("circular reference in element {}", node);
-                return Err(RenderingError::CircularReference);
+                return Ok(self.empty_bbox());
             }
 
             Err(AcquireError::MaxReferencesExceeded) => {
diff --git a/tests/errors.c b/tests/errors.c
index 9173d833..e476d63f 100644
--- a/tests/errors.c
+++ b/tests/errors.c
@@ -62,18 +62,5 @@ main (int argc, char **argv)
                                test_instancing_limit,
                                NULL);
 
-    g_test_add_data_func_full ("/errors/instancing_limit/308-use-self-ref.svg",
-                               "308-use-self-ref.svg",
-                               test_instancing_limit,
-                               NULL);
-    g_test_add_data_func_full ("/errors/instancing_limit/308-recursive-use.svg",
-                               "308-recursive-use.svg",
-                               test_instancing_limit,
-                               NULL);
-    g_test_add_data_func_full ("/errors/instancing_limit/308-doubly-recursive-use.svg",
-                               "308-doubly-recursive-use.svg",
-                               test_instancing_limit,
-                               NULL);
-
     return g_test_run ();
 }


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