Bug 1548985: Don't let 'contain:size' suppress the intrinsic size of document-root SVG elements. r=TYLin
authorDaniel Holbert <dholbert@cs.stanford.edu>
Wed, 08 May 2019 23:05:04 +0000
changeset 473147 129dafe816e8bd27d75f0eb051f343871df5e744
parent 473146 07774a40d1f6ea04cbe74c17b7bbcb4d4f112e4c
child 473148 c67422ffde6eb701d4ea0dbbf746a7b3ff74a317
push id113065
push useropoprus@mozilla.com
push dateThu, 09 May 2019 03:46:59 +0000
treeherdermozilla-inbound@34a824c75b7b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersTYLin
bugs1548985
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1548985: Don't let 'contain:size' suppress the intrinsic size of document-root SVG elements. r=TYLin Differential Revision: https://phabricator.services.mozilla.com/D30259
layout/svg/crashtests/1548985-1.html
layout/svg/crashtests/1548985-2.svg
layout/svg/crashtests/crashtests.list
layout/svg/nsSVGOuterSVGFrame.cpp
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1548985-1.html
@@ -0,0 +1,16 @@
+<!-- a -->
+<style>
+:root { contain: size }
+</style>
+<script>
+window.requestIdleCallback(window.close)
+function go() {
+  a.appendChild(document.head)
+  let b = d.getRootNode({composed: true})
+  b.replaceChild(c, b.childNodes[1])
+}
+</script>
+<body onload=go()>
+<svg id="c">
+<feTile id="d" />
+<foreignObject id="a">
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1548985-2.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+     style="contain:size">
+  <rect width="100px" height="100px" fill="lime"/>
+</svg>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -222,8 +222,10 @@ load 1502936.html
 load 1504918.svg
 load perspective-invalidation.html
 load invalid_url.html
 load 1535517-1.svg
 load 1504072.html
 load 1072758.html
 load 1536892.html
 load 1539318-1.svg
+load 1548985-1.html
+load 1548985-2.svg
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -84,16 +84,27 @@ static inline bool DependsOnIntrinsicSiz
   const auto& height = pos->mHeight;
 
   // XXX it would be nice to know if the size of aEmbeddingFrame's containing
   // block depends on aEmbeddingFrame, then we'd know if we can return false
   // for eStyleUnit_Percent too.
   return !width.ConvertsToLength() || !height.ConvertsToLength();
 }
 
+// The CSS Containment spec says that size-contained replaced elements must be
+// treated as having an intrinsic width and height of 0.  That's applicable to
+// outer SVG frames, unless they're the outermost element (in which case
+// they're not really "replaced", and there's no outer context to contain sizes
+// from leaking into). Hence, we check for a parent element before we bother
+// testing for 'contain:size'.
+static inline bool IsReplacedAndContainSize(const nsSVGOuterSVGFrame* aFrame) {
+  return aFrame->GetContent()->GetParent() &&
+         aFrame->StyleDisplay()->IsContainSize();
+}
+
 void nsSVGOuterSVGFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                               nsIFrame* aPrevInFlow) {
   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svg),
                "Content is not an SVG 'svg' element!");
 
   AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER |
                NS_FRAME_FONT_INFLATION_FLOW_ROOT);
 
@@ -163,17 +174,17 @@ nscoord nsSVGOuterSVGFrame::GetPrefISize
   DISPLAY_PREF_INLINE_SIZE(this, result);
 
   SVGSVGElement* svg = static_cast<SVGSVGElement*>(GetContent());
   WritingMode wm = GetWritingMode();
   const SVGAnimatedLength& isize =
       wm.IsVertical() ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]
                       : svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
 
-  if (StyleDisplay()->IsContainSize()) {
+  if (IsReplacedAndContainSize(this)) {
     result = nscoord(0);
   } else if (isize.IsPercentage()) {
     // It looks like our containing block's isize may depend on our isize. In
     // that case our behavior is undefined according to CSS 2.1 section 10.3.2.
     // As a last resort, we'll fall back to returning zero.
     result = nscoord(0);
 
     // Returning zero may be unhelpful, however, as it leads to unexpected
@@ -200,17 +211,17 @@ nscoord nsSVGOuterSVGFrame::GetPrefISize
   return result;
 }
 
 /* virtual */
 IntrinsicSize nsSVGOuterSVGFrame::GetIntrinsicSize() {
   // XXXjwatt Note that here we want to return the CSS width/height if they're
   // specified and we're embedded inside an nsIObjectLoadingContent.
 
-  if (StyleDisplay()->IsContainSize()) {
+  if (IsReplacedAndContainSize(this)) {
     // Intrinsic size of 'contain:size' replaced elements is 0,0.
     return IntrinsicSize(0, 0);
   }
 
   SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
   const SVGAnimatedLength& width =
       content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
   const SVGAnimatedLength& height =
@@ -230,17 +241,17 @@ IntrinsicSize nsSVGOuterSVGFrame::GetInt
     intrinsicSize.height.emplace(std::max(val, 0));
   }
 
   return intrinsicSize;
 }
 
 /* virtual */
 AspectRatio nsSVGOuterSVGFrame::GetIntrinsicRatio() {
-  if (StyleDisplay()->IsContainSize()) {
+  if (IsReplacedAndContainSize(this)) {
     return AspectRatio();
   }
 
   // We only have an intrinsic size/ratio if our width and height attributes
   // are both specified and set to non-percentage values, or we have a viewBox
   // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
   // Unfortunately we have to return the ratio as two nscoords whereas what
   // we have are two floats. Using app units allows for some floating point