- 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());
}