Bug 1063073 - Make sure embedding elements that rely on an embedded SVG's intrinsic dimensions are resized if the SVG is late in loading. r=dholbert, a=lmandel
authorJonathan Watt <jwatt@jwatt.org>
Thu, 04 Sep 2014 20:09:51 +0100
changeset 223106 42be358fe96fb9693ee5f06149f2f46067cb3cac
parent 223105 a5196df8db6c91c35eba73e1001bc8775affacaa
child 223107 b3d8b5ea20fde275a04a987cda071fdbfac12a7b
push id4
push usergszorc@mozilla.com
push dateWed, 29 Oct 2014 02:48:29 +0000
reviewersdholbert, lmandel
bugs1063073
milestone34.0a2
Bug 1063073 - Make sure embedding elements that rely on an embedded SVG's intrinsic dimensions are resized if the SVG is late in loading. r=dholbert, a=lmandel
content/svg/content/test/mochitest.ini
content/svg/content/test/object-delayed-intrinsic-size.sjs
content/svg/content/test/test_object-delayed-intrinsic-size.html
layout/svg/nsSVGOuterSVGFrame.cpp
--- a/content/svg/content/test/mochitest.ini
+++ b/content/svg/content/test/mochitest.ini
@@ -11,16 +11,17 @@ support-files =
   bounds-helper.svg
   getBBox-method-helper.svg
   dataTypes-helper.svg
   fragments-helper.svg
   getCTM-helper.svg
   getSubStringLength-helper.svg
   matrixUtils.js
   MutationEventChecker.js
+  object-delayed-intrinsic-size.sjs
   pointer-events.js
   scientific-helper.svg
   selectSubString-helper.svg
   switch-helper.svg
   text-helper-scaled.svg
   text-helper-selection.svg
   text-helper.svg
   viewport-helper.svg
@@ -45,16 +46,17 @@ support-files =
 [test_hasFeature.xhtml]
 [test_lang.xhtml]
 skip-if = true # disabled-for-intermittent-failures--bug-701060
 [test_length.xhtml]
 skip-if = true
 [test_lengthParsing.html]
 [test_nonAnimStrings.xhtml]
 [test_non-scaling-stroke.html]
+[test_object-delayed-intrinsic-size.html]
 [test_onerror.xhtml]
 [test_pathAnimInterpolation.xhtml]
 [test_pathLength.html]
 [test_pathSeg.xhtml]
 [test_pointAtLength.xhtml]
 [test_pointer-events-1a.xhtml]
 [test_pointer-events-1b.xhtml]
 [test_pointer-events-2.xhtml]
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/object-delayed-intrinsic-size.sjs
@@ -0,0 +1,24 @@
+
+var timer = null;
+
+function handleRequest(request, response)
+{
+  response.processAsync();
+
+  response.setStatusLine(null, 200, "OK");
+  response.setHeader("Content-Type", "image/svg+xml", false);
+
+  // We need some body output or else gecko will not do an initial reflow
+  // while waiting for the rest of the document to load:
+  response.bodyOutputStream.write("\n", 1);
+
+  timer = Components.classes["@mozilla.org/timer;1"]
+                    .createInstance(Components.interfaces.nsITimer);
+  timer.initWithCallback(function()
+  {
+    var body = "<svg xmlns='http://www.w3.org/2000/svg' width='70' height='0'></svg>";
+    response.bodyOutputStream.write(body, body.length);
+    response.finish();
+  }, 1000 /* milliseconds */, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/svg/content/test/test_object-delayed-intrinsic-size.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1063073
+-->
+<html>
+  <head>
+    <title>Test that &lt;object&gt; embedding SVG and using its intrinsic
+           size will resize if the &lt;object&gt; gets a reflow before the
+           root-&lt;svg&gt; gets its nsSVGOuterSVGFrame
+    </title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script>
+
+// This test checks for a race condition. If it fails intermittently then it
+// may actually be a full failure.
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest()
+{
+  var object = document.querySelector("object");
+  var cs = document.defaultView.getComputedStyle(object, "");
+  var width = cs.getPropertyValue("width");
+  is(width, "70px", "Check that the &lt;object&gt; size updated");
+  SimpleTest.finish();
+}
+
+    </script>
+  </head>
+  <body onload="runTest();">
+    <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1063073">Mozilla Bug 1063073</a>
+    <p id="display"></p>
+    <div id="content">
+      <object style="border:1px solid black" type="image/svg+xml"
+              data="object-delayed-intrinsic-size.sjs"></object>
+    </div>
+  </body>
+</html>
+
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -68,16 +68,31 @@ nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(n
     , mFullZoom(aContext->PresContext()->GetFullZoom())
     , mViewportInitialized(false)
     , mIsRootContent(false)
 {
   // Outer-<svg> has CSS layout, so remove this bit:
   RemoveStateBits(NS_FRAME_SVG_LAYOUT);
 }
 
+// helper
+static inline bool
+DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame)
+{
+  const nsStylePosition *pos = aEmbeddingFrame->StylePosition();
+  const nsStyleCoord &width = pos->mWidth;
+  const nsStyleCoord &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();
+}
+
 void
 nsSVGOuterSVGFrame::Init(nsIContent*       aContent,
                          nsContainerFrame* aParent,
                          nsIFrame*         aPrevInFlow)
 {
   NS_ASSERTION(aContent->IsSVG(nsGkAtoms::svg),
                "Content is not an SVG 'svg' element!");
 
@@ -100,16 +115,29 @@ nsSVGOuterSVGFrame::Init(nsIContent*    
 
   nsSVGOuterSVGFrameBase::Init(aContent, aParent, aPrevInFlow);
 
   nsIDocument* doc = mContent->GetCurrentDoc();
   if (doc) {
     // we only care about our content's zoom and pan values if it's the root element
     if (doc->GetRootElement() == mContent) {
       mIsRootContent = true;
+
+      nsIFrame* embeddingFrame;
+      if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
+        if (MOZ_UNLIKELY(!embeddingFrame->HasAllStateBits(NS_FRAME_IS_DIRTY)) &&
+            DependsOnIntrinsicSize(embeddingFrame)) {
+          // Looks like this document is loading after the embedding element
+          // has had its first reflow, and that its size depends on our
+          // intrinsic size.  We need it to resize itself to use our (now
+          // available) intrinsic size:
+          embeddingFrame->PresContext()->PresShell()->
+            FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
+        }
+      }
     }
   }
 }
 
 //----------------------------------------------------------------------
 // nsQueryFrame methods
 
 NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame)
@@ -618,31 +646,16 @@ nsDisplayOuterSVG::ComputeInvalidationRe
   nsRegion result = frame->GetInvalidRegion();
   result.MoveBy(ToReferenceFrame());
   frame->ClearInvalidRegion();
 
   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
   aInvalidRegion->Or(*aInvalidRegion, result);
 }
 
-// helper
-static inline bool
-DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame)
-{
-  const nsStylePosition *pos = aEmbeddingFrame->StylePosition();
-  const nsStyleCoord &width = pos->mWidth;
-  const nsStyleCoord &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();
-}
-
 nsresult
 nsSVGOuterSVGFrame::AttributeChanged(int32_t  aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      int32_t  aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       !(GetStateBits() & (NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_NONDISPLAY))) {
     if (aAttribute == nsGkAtoms::viewBox ||