Bug 530985. Make getBoundingClientRect give more sensible results for <svg> elements. r=jwatt
authorRobert Longson <longsonr@gmail.com>
Mon, 07 Jul 2014 00:55:43 +1200
changeset 213314 99e4b83dd83e5d237a1f2a823f911f4ca4383ccb
parent 213313 ceff7d54080f4b17aa009cf9117294d49be119f6
child 213315 9dc87050ee0a53d281625c84342e6fa6499e5c22
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs530985
milestone33.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 530985. Make getBoundingClientRect give more sensible results for <svg> elements. r=jwatt
content/svg/content/test/bounds-helper.svg
content/svg/content/test/test_bounds.html
dom/tests/mochitest/general/test_offsets.xul
layout/svg/nsSVGInnerSVGFrame.cpp
layout/svg/nsSVGInnerSVGFrame.h
layout/svg/nsSVGOuterSVGFrame.h
layout/svg/nsSVGUtils.cpp
--- a/content/svg/content/test/bounds-helper.svg
+++ b/content/svg/content/test/bounds-helper.svg
@@ -1,16 +1,23 @@
 <?xml version="1.0"?>
 <svg xmlns="http://www.w3.org/2000/svg" width="750"
      xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
   <style type="text/css">
 text { font: 20px monospace; }
   </style>
 
 <g id="g">
+  <svg id="svg1" x="10" y="10" width="25" height="30"/>
+  <svg id="svg2" width="1" height="1" overflow="visible">
+    <rect width="2" height="2" fill="yellow"/>
+  </svg>
+  <svg id="svg3" width="1" height="1" overflow="hidden">
+    <rect width="2" height="2" fill="yellow"/>
+  </svg>
   <text id="text1" x="25" y="25">abc</text>
   <text id="text1a" x="85" y="25" stroke="black" stroke-width="4">abc</text>
   <rect id="rect1" x="50" y="50" width="50" height="50" fill="green"/>
   <rect id="rect1a" x="50" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="yellow"/>
   <text id="text2" x="125" y="25">abc</text>
   <text id="text2a" x="185" y="25" stroke="black" stroke-width="10">abc</text>
   <g transform="rotate(45 175 75)">
     <rect id="rect2" x="150" y="50" width="50" height="50" fill="yellow"/>
--- a/content/svg/content/test/test_bounds.html
+++ b/content/svg/content/test/test_bounds.html
@@ -39,16 +39,34 @@ function isWithAbsTolerance(a, b, tolera
 {
   ok(tolerance >= Math.abs(a - b), message + " - got " + a + ", expected " + b + " ± " + tolerance);
 }
 
 function runTest()
 {
   var doc = $("svg").contentWindow.document;
 
+  var svg1Bounds = doc.getElementById("svg1").getBoundingClientRect();
+  is(svg1Bounds.left, 10, "svg1.getBoundingClientRect().left");
+  is(svg1Bounds.top, 10, "svg1.getBoundingClientRect().top");
+  is(svg1Bounds.width, 25, "svg1.getBoundingClientRect().width");
+  is(svg1Bounds.height, 30, "svg1.getBoundingClientRect().height");
+
+  var svg2Bounds = doc.getElementById("svg2").getBoundingClientRect();
+  is(svg2Bounds.left, 0, "svg2.getBoundingClientRect().left");
+  is(svg2Bounds.top, 0, "svg2.getBoundingClientRect().top");
+  is(svg2Bounds.width, 2, "svg2.getBoundingClientRect().width");
+  is(svg2Bounds.height, 2, "svg2.getBoundingClientRect().height");
+
+  var svg3Bounds = doc.getElementById("svg3").getBoundingClientRect();
+  is(svg3Bounds.left, 0, "svg3.getBoundingClientRect().left");
+  is(svg3Bounds.top, 0, "svg3.getBoundingClientRect().top");
+  is(svg3Bounds.width, 1, "svg3.getBoundingClientRect().width");
+  is(svg3Bounds.height, 1, "svg3.getBoundingClientRect().height");
+
   var text1 = doc.getElementById("text1");
 
   var text1Bounds = text1.getBoundingClientRect();
   var text2Bounds = doc.getElementById("text2").getBoundingClientRect();
   var text3Bounds = doc.getElementById("text3").getBoundingClientRect();
 
   var sin45 = Math.sin(Math.PI / 4);
 
--- a/dom/tests/mochitest/general/test_offsets.xul
+++ b/dom/tests/mochitest/general/test_offsets.xul
@@ -27,19 +27,22 @@
              style="margin: 0; padding: 0; border: 0;"/>
     </vbox>
     <vbox id="scrollbox-test">
        <scrollbar orient="vertical" style="border: 0; padding: 0;"/>
     </vbox>
   </hbox>
 </vbox>
 
+<!-- wrap svg in a div so that it can take its intrinsic width -->
+<div>
 <svg:svg id="svgbase" width="45" height="20" xmlns:svg="http://www.w3.org/2000/svg">
   <svg:rect id="svgrect" x="3" y="5" width="45" height="20" fill="red"/>
 </svg:svg>
+</div>
 
 </vbox>
 
 <button id="outermenu" type="menu" label="Menu">
   <menupopup id="outerpopup"
              style="margin-left: 5px; padding-left: 3px; padding: 0;"
              onpopupshown="this.firstChild.open = true"
              onpopuphidden="if (event.target == this) SimpleTest.finish();">
--- a/layout/svg/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/nsSVGInnerSVGFrame.cpp
@@ -86,16 +86,35 @@ nsSVGInnerSVGFrame::PaintSVG(nsRendering
     gfxRect clipRect =
       nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
     nsSVGUtils::SetClipRect(gfx, clipTransform, clipRect);
   }
 
   return nsSVGInnerSVGFrameBase::PaintSVG(aContext, aDirtyRect);
 }
 
+nsRect
+nsSVGInnerSVGFrame::GetCoveredRegion()
+{
+  float x, y, w, h;
+  static_cast<SVGSVGElement*>(mContent)->
+    GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
+  if (w < 0.0f) w = 0.0f;
+  if (h < 0.0f) h = 0.0f;
+  // GetCanvasTM includes the x,y translation
+  nsRect bounds = nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h),
+                                             GetCanvasTM(FOR_OUTERSVG_TM),
+                                             PresContext());
+
+  if (!StyleDisplay()->IsScrollableOverflow()) {
+    bounds.UnionRect(bounds, nsSVGUtils::GetCoveredRegion(mFrames));
+  }
+  return bounds;
+}
+
 void
 nsSVGInnerSVGFrame::ReflowSVG()
 {
   // mRect must be set before FinishAndStoreOverflow is called in order
   // for our overflow areas to be clipped correctly.
   float x, y, width, height;
   static_cast<SVGSVGElement*>(mContent)->
     GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
--- a/layout/svg/nsSVGInnerSVGFrame.h
+++ b/layout/svg/nsSVGInnerSVGFrame.h
@@ -51,16 +51,17 @@ public:
   virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType) MOZ_OVERRIDE;
 
   // nsISVGChildFrame interface:
   virtual nsresult PaintSVG(nsRenderingContext *aContext,
                             const nsIntRect *aDirtyRect,
                             nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
+  virtual nsRect GetCoveredRegion() MOZ_OVERRIDE;
   virtual void ReflowSVG() MOZ_OVERRIDE;
   virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
   virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM(uint32_t aFor,
                                 nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
 
--- a/layout/svg/nsSVGOuterSVGFrame.h
+++ b/layout/svg/nsSVGOuterSVGFrame.h
@@ -107,17 +107,16 @@ public:
 
   // nsISVGSVGFrame interface:
   virtual void NotifyViewportOrTransformChanged(uint32_t aFlags) MOZ_OVERRIDE;
 
   // nsISVGChildFrame methods:
   virtual nsresult PaintSVG(nsRenderingContext* aContext,
                             const nsIntRect *aDirtyRect,
                             nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
-
   virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags) MOZ_OVERRIDE;
 
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM(uint32_t aFor,
                                 nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
 
   /* Methods to allow descendant nsSVGForeignObjectFrame frames to register and
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -351,19 +351,23 @@ nsSVGUtils::GetOuterSVGFrame(nsIFrame *a
 }
 
 nsIFrame*
 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
 {
   nsISVGChildFrame* svg = do_QueryFrame(aFrame);
   if (!svg)
     return nullptr;
+  nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
+  if (outer == svg) {
+    return nullptr;
+  }
   *aRect = (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
              nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
-  return GetOuterSVGFrame(aFrame);
+  return outer;
 }
 
 gfxMatrix
 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame, uint32_t aFor,
                         nsIFrame* aTransformRoot)
 {
   // XXX yuck, we really need a common interface for GetCanvasTM