Title: [275944] trunk
Revision
275944
Author
[email protected]
Date
2021-04-14 08:13:36 -0700 (Wed, 14 Apr 2021)

Log Message

Crash in RenderBlock::addOverflowFromChildren
https://bugs.webkit.org/show_bug.cgi?id=223539

Patch by Frédéric Wang <[email protected]> on 2021-04-14
Reviewed by Ryosuke Niwa.

Source/WebCore:

For an <svg> element that is a direct child of a <foreignObject>, a RenderSVGRoot is
generally created. However, a RenderSVGViewportContainer is currently created instead if
the element is inside a shadow tree. This is leading to bad state during the layout of
the foreign object, causing a debug assertion and a nullptr crash. This patch fixes this
issue by always treating direct <svg> child of <foreignObject> as an outermost SVG element.

Tests: svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html
       svg/outermost-svg-root.html

* svg/SVGElement.cpp:
(WebCore::SVGElement::isOutermostSVGSVGElement const): Lower down the priority of the rule
"is in shadow tree".

LayoutTests:

Add regressions tests for the crash and for isOutermostSVGSVGElement().

* svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash-expected.txt: Added.
* svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html: Added.
* svg/outermost-svg-root-expected.txt: Added.
* svg/outermost-svg-root.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (275943 => 275944)


--- trunk/LayoutTests/ChangeLog	2021-04-14 13:57:14 UTC (rev 275943)
+++ trunk/LayoutTests/ChangeLog	2021-04-14 15:13:36 UTC (rev 275944)
@@ -1,3 +1,17 @@
+2021-04-14  Frédéric Wang  <[email protected]>
+
+        Crash in RenderBlock::addOverflowFromChildren
+        https://bugs.webkit.org/show_bug.cgi?id=223539
+
+        Reviewed by Ryosuke Niwa.
+
+        Add regressions tests for the crash and for isOutermostSVGSVGElement().
+
+        * svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash-expected.txt: Added.
+        * svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html: Added.
+        * svg/outermost-svg-root-expected.txt: Added.
+        * svg/outermost-svg-root.html: Added.
+
 2021-04-13  Jer Noble  <[email protected]>
 
         [GPUP] MediaSource.addSourceBuffer() will throw if given the same parameter as a previous call to HTMLMediaElement.canPlayType()

Added: trunk/LayoutTests/svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash-expected.txt (0 => 275944)


--- trunk/LayoutTests/svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash-expected.txt	2021-04-14 15:13:36 UTC (rev 275944)
@@ -0,0 +1,3 @@
+This test passes if it does not crash.
+
+

Added: trunk/LayoutTests/svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html (0 => 275944)


--- trunk/LayoutTests/svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html	                        (rev 0)
+++ trunk/LayoutTests/svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html	2021-04-14 15:13:36 UTC (rev 275944)
@@ -0,0 +1,14 @@
+<p>This test passes if it does not crash.</p>
+<div id="container"></div>
+<script>
+  if (window.testRunner)
+      testRunner.dumpAsText();
+  const svgNS = 'http://www.w3.org/2000/svg';
+  var root = document.getElementById('container').attachShadow({mode: 'open'});
+  var svg1 = document.createElementNS(svgNS, 'svg');
+  root.appendChild(svg1);
+  var fo = document.createElementNS(svgNS, 'foreignObject');
+  svg1.appendChild(fo);
+  var svg2 = document.createElementNS(svgNS, 'svg');
+  fo.appendChild(svg2);
+</script>

Added: trunk/LayoutTests/svg/outermost-svg-root-expected.txt (0 => 275944)


--- trunk/LayoutTests/svg/outermost-svg-root-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/svg/outermost-svg-root-expected.txt	2021-04-14 15:13:36 UTC (rev 275944)
@@ -0,0 +1,11 @@
+PASS extractSVGRootRenderers('svg') is ['RenderSVGRoot {svg}','RenderSVGRoot {svg}']
+PASS extractSVGRootRenderers('mathml') is ['RenderSVGRoot {svg}']
+PASS extractSVGRootRenderers('non_svg') is ['RenderSVGRoot {svg}']
+PASS extractSVGRootRenderers('svg_use') is ['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']
+PASS extractSVGRootRenderers('svg_child_of_use') is ['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']
+PASS extractSVGRootRenderers('svg_in_shadow_tree') is ['RenderSVGRoot {svg}','RenderSVGRoot {svg}']
+PASS extractSVGRootRenderers('mathml_in_shadow_tree') is ['RenderSVGRoot {svg}']
+PASS extractSVGRootRenderers('non_svg_in_shadow_tree') is ['RenderSVGRoot {svg}']
+PASS extractSVGRootRenderers('svg_use_in_shadow_tree') is ['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']
+PASS extractSVGRootRenderers('svg_child_of_use_in_shadow_tree') is ['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']
+

Added: trunk/LayoutTests/svg/outermost-svg-root.html (0 => 275944)


--- trunk/LayoutTests/svg/outermost-svg-root.html	                        (rev 0)
+++ trunk/LayoutTests/svg/outermost-svg-root.html	2021-04-14 15:13:36 UTC (rev 275944)
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      /* relative positioning ensures underlying RenderLayer */
+      .renderingArea {
+          position: relative;
+      }
+    </style>
+  </head>
+  <body>
+    <script src=""
+    <pre id="console"></pre>
+    <div class="renderingArea" id="svg">
+      <svg>
+        <foreignObject>
+          <svg></svg>
+        </foreignObject>
+      </svg>
+    </div>
+    <div class="renderingArea" id="mathml">
+      <math>
+        <semantics>
+          <annotation-xml encoding="image/svg+xml">
+            <svg></svg>
+          </annotation-xml>
+        </semantics>
+      </math>
+    </div>
+    <div class="renderingArea" id="non_svg"></div>
+    <div class="renderingArea" id="svg_use">
+      <svg>
+        <defs>
+          <foreignObject>
+            <svg id="mySVG"></svg>
+          </foreignObject>
+        </defs>
+        <use href=""
+      </svg>
+    </div>
+    <div class="renderingArea" id="svg_child_of_use">
+      <svg>
+        <use href=""
+          <svg></svg>
+        </use>
+      </svg>
+    </div>
+    <div class="renderingArea" id="svg_in_shadow_tree"></div>
+    <div class="renderingArea" id="mathml_in_shadow_tree"></div>
+    <div class="renderingArea" id="non_svg_in_shadow_tree"></div>
+    <div class="renderingArea" id="svg_use_in_shadow_tree"></div>
+    <div class="renderingArea" id="svg_child_of_use_in_shadow_tree"></div>
+    <script>
+      const svgNS = 'http://www.w3.org/2000/svg';
+
+      (function() {
+          let nonSVG = document.createElementNS('http://example.org/', "nonSVG");
+          let svg = document.createElementNS(svgNS, 'svg');
+          nonSVG.appendChild(svg);
+          document.getElementById("non_svg").appendChild(nonSVG);
+      })();
+
+      ["svg", "mathml", "non_svg", "svg_use", "svg_child_of_use"].forEach(id => {
+          let shadowHost = document.createElement("div");
+          let shadowRoot = shadowHost.attachShadow({mode: 'open'});
+          let svg = document.getElementById(id).firstElementChild.cloneNode(true);
+          ["href", "id"].forEach(attribute => {
+              // Fix duplicate ids.
+              Array.from(svg.querySelectorAll(`[${attribute}]`)).forEach(element => {
+                  element.setAttribute(attribute,
+                                       `${element.getAttribute(attribute)}_copy`);
+              });
+          });
+          shadowRoot.appendChild(svg);
+          document.getElementById(`${id}_in_shadow_tree`).appendChild(shadowHost);
+
+      });
+
+      function extractSVGRootRenderers(name) {
+          let element = document.getElementById(name);
+          const regexp = /\w+ \{svg\}/g;
+          return window.internals.elementRenderTreeAsText(element).match(regexp);
+      }
+
+      if (window.testRunner)
+          testRunner.dumpAsText();
+
+      shouldBe("extractSVGRootRenderers('svg')",
+               "['RenderSVGRoot {svg}','RenderSVGRoot {svg}']");
+
+      shouldBe("extractSVGRootRenderers('mathml')",
+               "['RenderSVGRoot {svg}']");
+
+      shouldBe("extractSVGRootRenderers('non_svg')",
+               "['RenderSVGRoot {svg}']");
+
+      shouldBe("extractSVGRootRenderers('svg_use')",
+               "['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']");
+
+      shouldBe("extractSVGRootRenderers('svg_child_of_use')",
+               "['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']");
+
+      shouldBe("extractSVGRootRenderers('svg_in_shadow_tree')",
+               "['RenderSVGRoot {svg}','RenderSVGRoot {svg}']");
+
+      shouldBe("extractSVGRootRenderers('mathml_in_shadow_tree')",
+               "['RenderSVGRoot {svg}']");
+
+      shouldBe("extractSVGRootRenderers('non_svg_in_shadow_tree')",
+               "['RenderSVGRoot {svg}']");
+
+      shouldBe("extractSVGRootRenderers('svg_use_in_shadow_tree')",
+               "['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']");
+
+      shouldBe("extractSVGRootRenderers('svg_child_of_use_in_shadow_tree')",
+               "['RenderSVGRoot {svg}','RenderSVGViewportContainer {svg}']");
+
+    </script>
+    <script src=""
+  </body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (275943 => 275944)


--- trunk/Source/WebCore/ChangeLog	2021-04-14 13:57:14 UTC (rev 275943)
+++ trunk/Source/WebCore/ChangeLog	2021-04-14 15:13:36 UTC (rev 275944)
@@ -1,3 +1,23 @@
+2021-04-14  Frédéric Wang  <[email protected]>
+
+        Crash in RenderBlock::addOverflowFromChildren
+        https://bugs.webkit.org/show_bug.cgi?id=223539
+
+        Reviewed by Ryosuke Niwa.
+
+        For an <svg> element that is a direct child of a <foreignObject>, a RenderSVGRoot is
+        generally created. However, a RenderSVGViewportContainer is currently created instead if
+        the element is inside a shadow tree. This is leading to bad state during the layout of
+        the foreign object, causing a debug assertion and a nullptr crash. This patch fixes this
+        issue by always treating direct <svg> child of <foreignObject> as an outermost SVG element.
+
+        Tests: svg/foreignObject/svg-child-of-foreign-object-in-shadow-tree-crash.html
+               svg/outermost-svg-root.html
+
+        * svg/SVGElement.cpp:
+        (WebCore::SVGElement::isOutermostSVGSVGElement const): Lower down the priority of the rule
+        "is in shadow tree".
+
 2021-04-14  Zalan Bujtas  <[email protected]>
 
         InlineBox should not hold a reference to RenderObject

Modified: trunk/Source/WebCore/svg/SVGElement.cpp (275943 => 275944)


--- trunk/Source/WebCore/svg/SVGElement.cpp	2021-04-14 13:57:14 UTC (rev 275943)
+++ trunk/Source/WebCore/svg/SVGElement.cpp	2021-04-14 15:13:36 UTC (rev 275944)
@@ -200,12 +200,6 @@
     if (!is<SVGSVGElement>(*this))
         return false;
 
-    // If we're living in a shadow tree, we're a <svg> element that got created as replacement
-    // for a <symbol> element or a cloned <svg> element in the referenced tree. In that case
-    // we're always an inner <svg> element.
-    if (isInShadowTree() && is<SVGElement>(parentOrShadowHostElement()))
-        return false;
-
     // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
     if (!parentNode())
         return true;
@@ -214,6 +208,10 @@
     if (is<SVGForeignObjectElement>(*parentNode()))
         return true;
 
+    // If we're inside the shadow tree of a <use> element, we're always an inner <svg> element.
+    if (isInShadowTree() && is<SVGUseElement>(shadowHost()))
+        return false;
+
     // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
     return !is<SVGElement>(*parentNode());
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to