Bug 762411 - getScreenCTM scaling broken on inline outer svg elements. r=jwatt
authorRobert Longson <longsonr@gmail.com>
Wed, 13 Jun 2012 14:32:23 +0100
changeset 101390 88af8c6a5ab9f56bd6c120392a00f1dc3d705201
parent 101389 2b43c6ad74ea645a3ea15f0029f1e04572566d26
child 101391 10ad7878033a8d74273780bdf639eaf718f70246
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-beta@db4b09302ee2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs762411
milestone16.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 762411 - getScreenCTM scaling broken on inline outer svg elements. r=jwatt
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/test/getCTM-helper.svg
content/svg/content/test/test_getCTM.html
layout/svg/base/src/nsSVGUtils.cpp
--- a/content/svg/content/src/nsSVGSVGElement.cpp
+++ b/content/svg/content/src/nsSVGSVGElement.cpp
@@ -676,26 +676,17 @@ nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix 
   NS_IF_ADDREF(*aCTM);
   return NS_OK;
 }
 
 /* nsIDOMSVGMatrix getScreenCTM (); */
 NS_IMETHODIMP
 nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **aCTM)
 {
-  gfxMatrix m;
-  if (IsRoot()) {
-    // Consistency with other elements would have us return only the
-    // eFromUserSpace transforms, but this is what we've been doing for
-    // a while, and it keeps us consistent with WebKit and Opera (if not
-    // really with the ambiguous spec).
-    m = PrependLocalTransformsTo(m);
-  } else {
-    m = nsSVGUtils::GetCTM(this, true);
-  }
+  gfxMatrix m = nsSVGUtils::GetCTM(this, true);
   *aCTM = m.IsSingular() ? nsnull : new DOMSVGMatrix(m);
   NS_IF_ADDREF(*aCTM);
   return NS_OK;
 }
 
 /* nsIDOMSVGMatrix getTransformToElement (in nsIDOMSVGElement element); */
 NS_IMETHODIMP
 nsSVGSVGElement::GetTransformToElement(nsIDOMSVGElement *element,
--- a/content/svg/content/test/getCTM-helper.svg
+++ b/content/svg/content/test/getCTM-helper.svg
@@ -22,16 +22,22 @@
     </svg>
     <foreignObject id="fO" x="30" y="40" width="100" height="100" transform="translate(1, 1)">
       <!-- current layout implementation ignores x="50" and y="60".
            thus, I made getCTM and getScreenCTM do the same. -->
       <svg id="outer" x="50" y="60" width="100" height="100">
         <g id="g2" transform="translate(600, 700)"/>
       </svg>
     </foreignObject>
+    <foreignObject x="30" y="40" width="100" height="100" transform="translate(1, 1)">
+      <html xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%">
+        <svg xmlns="http://www.w3.org/2000/svg" id="outer2" 
+             width="100" height="100" viewBox="100 100 200 200"/>
+      </html>
+    </foreignObject>
     <!-- something invalid -->
     <foreignObject>
       <g id="g3"/>
     </foreignObject>
     <image>
       <g id="g4"/>
     </image>
     <use xlink:href="#sym" id="use"/>
--- a/content/svg/content/test/test_getCTM.html
+++ b/content/svg/content/test/test_getCTM.html
@@ -27,16 +27,17 @@ function runTest()
   var buggy = doc.getElementById("buggy");
   is(buggy.getCTM().e, 30, "buggy.getCTM().e");
   is(buggy.getCTM().f, 40, "buggy.getCTM().f");
 
   var root = doc.documentElement;
   var inner = doc.getElementById("inner");
   var g1 = doc.getElementById("g1");
   var outer = doc.getElementById("outer");
+  var outer2 = doc.getElementById("outer2");
   var g2 = doc.getElementById("g2");
   var g3 = doc.getElementById("g3");
   var g4 = doc.getElementById("g4");
   var g5 = doc.getElementById("g5");
   var sym = doc.getElementById("sym");
   var symbolRect = doc.getElementById("symbolRect");
   var fO = doc.getElementById("fO");
   /* Tests the consistency with nearestViewportElement
@@ -77,16 +78,19 @@ function runTest()
   is((function(){try{return inner.getScreenCTM().e}catch(e){return e}})(), 45, "inner.getScreenCTM().e");
   is((function(){try{return inner.getScreenCTM().f}catch(e){return e}})(), 68, "inner.getScreenCTM().f");
   // g1.farthestViewportElement == root
   is((function(){try{return g1.getScreenCTM().e}catch(e){return e}})(), 45, "g1.getScreenCTM().e");
   is((function(){try{return g1.getScreenCTM().f}catch(e){return e}})(), 68, "g1.getScreenCTM().f");
   // outer.farthestViewportElement == null (but actually == root)
   is((function(){try{return outer.getScreenCTM().e}catch(e){return e}})(), 46, "outer.getScreenCTM().e");
   is((function(){try{return outer.getScreenCTM().f}catch(e){return e}})(), 69, "outer.getScreenCTM().f");
+  // outer.farthestViewportElement == null (but actually == root)
+  is((function(){try{return outer2.getScreenCTM().e}catch(e){return e}})(), -19, "outer2.getScreenCTM().e");
+  is((function(){try{return outer2.getScreenCTM().f}catch(e){return e}})(), -8, "outer2.getScreenCTM().f");
   // g2.farthestViewportElement == outer (but actually == root)
   is((function(){try{return g2.getScreenCTM().e}catch(e){return e}})(), 646, "g2.getScreenCTM().e");
   is((function(){try{return g2.getScreenCTM().f}catch(e){return e}})(), 769, "g2.getScreenCTM().f");
   // g3.farthestViewportElement == null (but actually == null)
   is((function(){try{return g3.getScreenCTM()}catch(e){return e}})(), null, "g3.getScreenCTM()");
   // symbolRect.farthestViewportElement == root
   is((function(){try{return symbolRect.getScreenCTM().e}catch(e){return e}})(), 85, "symbolRect.getScreenCTM().e");
   is((function(){try{return symbolRect.getScreenCTM().f}catch(e){return e}})(), 108, "symbolRect.getScreenCTM().f");
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -480,18 +480,16 @@ nsSVGUtils::GetNearestViewportElement(ns
     element = element->GetFlattenedTreeParent();
   }
   return nsnull;
 }
 
 static gfxMatrix
 GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed)
 {
-  nsIDocument* currentDoc = aElement->GetCurrentDoc();
-
   gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(),
     aHaveRecursed ? nsSVGElement::eAllTransforms : nsSVGElement::eUserSpaceToParent);
   nsSVGElement *element = aElement;
   nsIContent *ancestor = aElement->GetFlattenedTreeParent();
 
   while (ancestor && ancestor->IsSVG() &&
                      ancestor->Tag() != nsGkAtoms::foreignObject) {
     element = static_cast<nsSVGElement*>(ancestor);
@@ -506,28 +504,40 @@ GetCTMInternal(nsSVGElement *aElement, b
       return matrix;
     }
     ancestor = ancestor->GetFlattenedTreeParent();
   }
   if (!aScreenCTM) {
     // didn't find a nearestViewportElement
     return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
   }
+  if (element->Tag() != nsGkAtoms::svg) {
+    // Not a valid SVG fragment
+    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
+  }
+  if (element == aElement && !aHaveRecursed) {
+    // We get here when getScreenCTM() is called on an outer-<svg>.
+    // Consistency with other elements would have us include only the
+    // eFromUserSpace transforms, but we include the eAllTransforms
+    // transforms in this case since that's what we've been doing for
+    // a while, and it keeps us consistent with WebKit and Opera (if not
+    // really with the ambiguous spec).
+    matrix = aElement->PrependLocalTransformsTo(gfxMatrix());
+  }
   if (!ancestor || !ancestor->IsElement()) {
     return matrix;
   }
   if (ancestor->IsSVG()) {
-    if (element->Tag() != nsGkAtoms::svg) {
-      return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
-    }
     return
       matrix * GetCTMInternal(static_cast<nsSVGElement*>(ancestor), true, true);
   }
+
   // XXX this does not take into account CSS transform, or that the non-SVG
   // content that we've hit may itself be inside an SVG foreignObject higher up
+  nsIDocument* currentDoc = aElement->GetCurrentDoc();
   float x = 0.0f, y = 0.0f;
   if (currentDoc && element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
     nsIPresShell *presShell = currentDoc->GetShell();
     if (presShell) {
       nsIFrame* frame = element->GetPrimaryFrame();
       nsIFrame* ancestorFrame = presShell->GetRootFrame();
       if (frame && ancestorFrame) {
         nsPoint point = frame->GetOffsetTo(ancestorFrame);