Bug 578309 - mask and clipPath applied to foreignObject are translated. r=roc a=blocking
authorRobert Longson <longsonr@gmail.com>
Tue, 27 Jul 2010 08:33:01 +0100
changeset 48241 f7a7153103e2499f1ecd214b4a947b3f817558e1
parent 48240 379c41d8de91af4537ddb95b436bc3945be5b548
child 48242 3b72080b265c94b7e9444da2843eb07fc83f17ef
push idunknown
push userunknown
push dateunknown
reviewersroc, blocking
bugs578309
milestone2.0b3pre
first release with
nightly win64
f7a7153103e2 / 4.0b3pre / 20100727010942 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Bug 578309 - mask and clipPath applied to foreignObject are translated. r=roc a=blocking
content/svg/content/src/nsSVGForeignObjectElement.cpp
content/svg/content/src/nsSVGForeignObjectElement.h
layout/reftests/svg/clipPath-basic-05.svg
layout/reftests/svg/reftest.list
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGForeignObjectFrame.h
--- a/content/svg/content/src/nsSVGForeignObjectElement.cpp
+++ b/content/svg/content/src/nsSVGForeignObjectElement.cpp
@@ -100,31 +100,16 @@ NS_IMETHODIMP nsSVGForeignObjectElement:
 
 /* readonly attribute nsIDOMSVGAnimatedLength height; */
 NS_IMETHODIMP nsSVGForeignObjectElement::GetHeight(nsIDOMSVGAnimatedLength * *aHeight)
 {
   return mLengthAttributes[HEIGHT].ToDOMAnimatedLength(aHeight, this);
 }
 
 //----------------------------------------------------------------------
-// nsSVGElement methods
-
-/* virtual */ gfxMatrix
-nsSVGForeignObjectElement::PrependLocalTransformTo(const gfxMatrix &aMatrix)
-{
-  // 'transform' attribute:
-  gfxMatrix matrix = nsSVGForeignObjectElementBase::PrependLocalTransformTo(aMatrix);
-  
-  // now translate by our 'x' and 'y':
-  float x, y;
-  GetAnimatedLengthValues(&x, &y, nsnull);
-  return gfxMatrix().Translate(gfxPoint(x, y)) * matrix;
-}
-
-//----------------------------------------------------------------------
 // nsIContent methods
 
 NS_IMETHODIMP_(PRBool)
 nsSVGForeignObjectElement::IsAttributeMapped(const nsIAtom* name) const
 {
   static const MappedAttributeEntry* const map[] = {
     sFEFloodMap,
     sFiltersMap,
--- a/content/svg/content/src/nsSVGForeignObjectElement.h
+++ b/content/svg/content/src/nsSVGForeignObjectElement.h
@@ -61,19 +61,16 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMSVGFOREIGNOBJECTELEMENT
 
   // xxx I wish we could use virtual inheritance
   NS_FORWARD_NSIDOMNODE(nsSVGForeignObjectElementBase::)
   NS_FORWARD_NSIDOMELEMENT(nsSVGForeignObjectElementBase::)
   NS_FORWARD_NSIDOMSVGELEMENT(nsSVGForeignObjectElementBase::)
 
-  // nsSVGElement specializations:
-  virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix);
-
   // nsIContent interface
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* name) const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 protected:
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/clipPath-basic-05.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
+
+  <title>Testcase for basic clipPath on foreignObject</title>
+  
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=578309 -->
+
+  <defs>
+    <clipPath id="clip">
+      <rect x="20" y="20" width="100" height="100"/>
+    </clipPath>
+  </defs>
+
+  <rect width="100%" height="100%" fill="lime"/>
+
+  <rect x="20" y="20" width="100" height="100" fill="red"/>
+  <foreignObject x="20" y="20" width="100" height="100" clip-path="url(#clip)">
+    <svg>
+      <rect width="100" height="100" fill="lime"/>
+    </svg>
+  </foreignObject>
+
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -21,16 +21,17 @@ include svg-integration/reftest.list
 == clip-02a.svg clip-02-ref.svg
 == clip-02b.svg clip-02-ref.svg
 == clipPath-advanced-01.svg pass.svg
 == clipPath-and-shape-rendering-01.svg clipPath-and-shape-rendering-01-ref.svg
 == clipPath-basic-01.svg pass.svg
 == clipPath-basic-02.svg pass.svg
 == clipPath-basic-03.svg pass.svg
 == clipPath-basic-04.svg pass.svg
+== clipPath-basic-05.svg pass.svg
 == clipPath-winding-01.svg pass.svg
 == clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
 == conditions-01.svg pass.svg
 == currentColor-01.svg pass.svg
 == currentColor-02.svg pass.svg
 == currentColor-03.svg pass.svg
 == dynamic-attr-removal-1.svg pass.svg
 == dynamic-attr-removal-2.svg pass.svg
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -201,22 +201,21 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGR
 {
   if (IsDisabled())
     return NS_OK;
 
   nsIFrame* kid = GetFirstChild(nsnull);
   if (!kid)
     return NS_OK;
 
-  gfxMatrix matrixForChildren = GetCanvasTMForChildren();
-  gfxMatrix matrix = GetCanvasTM();
+  gfxMatrix matrix = GetCanvasTMWithTranslation();
 
   nsIRenderingContext *ctx = aContext->GetRenderingContext(this);
 
-  if (!ctx || matrixForChildren.IsSingular()) {
+  if (!ctx || matrix.IsSingular()) {
     NS_WARNING("Can't render foreignObject element!");
     return NS_ERROR_FAILURE;
   }
 
   /* Check if we need to draw anything. */
   if (aDirtyRect) {
     PRInt32 appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
     if (!mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect))
@@ -232,17 +231,17 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGR
     static_cast<nsSVGElement*>(mContent)->
       GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
 
     gfxRect clipRect =
       nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height);
     nsSVGUtils::SetClipRect(gfx, matrix, clipRect);
   }
 
-  gfx->Multiply(matrixForChildren);
+  gfx->Multiply(GetScaledMatrixForChildren(matrix));
 
   // Transform the dirty rect into the rectangle containing the
   // transformed dirty rect.
   gfxMatrix invmatrix = matrix.Invert();
   NS_ASSERTION(!invmatrix.IsSingular(),
                "inverse of non-singular matrix should be non-singular");
 
   gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y,
@@ -272,42 +271,42 @@ nsSVGForeignObjectFrame::GetTransformMat
 {
   NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!");
 
   /* Set the ancestor to be the outer frame. */
   *aOutAncestor = nsSVGUtils::GetOuterSVGFrame(this);
   NS_ASSERTION(*aOutAncestor, "How did we end up without an outer frame?");
 
   /* Return the matrix back to the root, factoring in the x and y offsets. */
-  return GetCanvasTMForChildren();
+  return GetScaledMatrixForChildren(GetCanvasTMWithTranslation());
 }
  
 NS_IMETHODIMP_(nsIFrame*)
 nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
 {
   if (IsDisabled() || (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD))
     return nsnull;
 
   nsIFrame* kid = GetFirstChild(nsnull);
   if (!kid)
     return nsnull;
 
-  float x, y, width, height;
-  static_cast<nsSVGElement*>(mContent)->
-    GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
-
-  gfxMatrix tm = GetCanvasTM().Invert();
+  gfxMatrix tm = GetCanvasTMWithTranslation().Invert();
   if (tm.IsSingular())
     return nsnull;
   
   // Convert aPoint from app units in canvas space to user space:
 
   gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel();
   pt = tm.Transform(pt);
 
+  float x, y, width, height;
+  static_cast<nsSVGElement*>(mContent)->
+    GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
+
   if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt))
     return nsnull;
 
   // Convert pt to app units in *local* space:
 
   pt = pt * nsPresContext::AppUnitsPerCSSPixel();
   nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
 
@@ -333,18 +332,18 @@ nsSVGForeignObjectFrame::UpdateCoveredRe
   float x, y, w, h;
   static_cast<nsSVGForeignObjectElement*>(mContent)->
     GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
 
   // If mRect's width or height are negative, reflow blows up! We must clamp!
   if (w < 0.0f) w = 0.0f;
   if (h < 0.0f) h = 0.0f;
 
-  // GetCanvasTM includes the x,y translation
-  mRect = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext());
+  mRect = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTMWithTranslation(),
+                         PresContext());
   
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGForeignObjectFrame::InitialUpdate()
 {
   NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
@@ -448,21 +447,19 @@ nsSVGForeignObjectFrame::GetMatrixPropag
 }
 
 gfxRect
 nsSVGForeignObjectFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace)
 {
   NS_ASSERTION(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
                "Should not be calling this on a non-display child");
 
-  nsSVGForeignObjectElement *content =
-    static_cast<nsSVGForeignObjectElement*>(mContent);
-
   float x, y, w, h;
-  content->GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
+  static_cast<nsSVGForeignObjectElement*>(mContent)->
+    GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
 
   if (w < 0.0f) w = 0.0f;
   if (h < 0.0f) h = 0.0f;
 
   if (aToBBoxUserspace.IsSingular()) {
     // XXX ReportToConsole
     return gfxRect(0.0, 0.0, 0.0, 0.0);
   }
@@ -487,22 +484,32 @@ nsSVGForeignObjectFrame::GetCanvasTM()
   }
   return nsSVGUtils::ConvertSVGMatrixToThebes(mCanvasTM);
 }
 
 //----------------------------------------------------------------------
 // Implementation helpers
 
 gfxMatrix
-nsSVGForeignObjectFrame::GetCanvasTMForChildren()
+nsSVGForeignObjectFrame::GetCanvasTMWithTranslation()
+{
+  float x, y;
+  static_cast<nsSVGElement*>(mContent)->
+    GetAnimatedLengthValues(&x, &y, nsnull);
+
+  return (gfxMatrix().Translate(gfxPoint(x, y)) * GetCanvasTM());
+}
+
+gfxMatrix
+nsSVGForeignObjectFrame::GetScaledMatrixForChildren(gfxMatrix canvasTMWithTranslation) const
 {
   float cssPxPerDevPx = PresContext()->
     AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
 
-  return GetCanvasTM().Scale(cssPxPerDevPx, cssPxPerDevPx);
+  return canvasTMWithTranslation.Scale(cssPxPerDevPx, cssPxPerDevPx);
 }
 
 void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
 {
   if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
     // If we haven't had an InitialUpdate yet, nothing to do.
     return;
 
@@ -625,17 +632,17 @@ nsSVGForeignObjectFrame::InvalidateDirty
     return;
 
   // The areas dirtied by children are in app units, relative to this frame.
   // We need to convert the rect to userspace to use IntersectRect.
 
   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
 
-  nsRect rect = ToCanvasBounds(r, GetCanvasTM(), PresContext());
+  nsRect rect = ToCanvasBounds(r, GetCanvasTMWithTranslation(), PresContext());
 
   // Don't invalidate areas outside our bounds:
   rect.IntersectRect(rect, mRect);
   if (rect.IsEmpty())
     return;
 
   rect = nsSVGUtils::FindFilterInvalidation(this, rect);
   aOuter->InvalidateWithFlags(rect, aFlags);
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.h
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h
@@ -140,19 +140,20 @@ public:
   void MaybeReflowFromOuterSVGFrame();
 
 protected:
   // implementation helpers:
   void DoReflow();
   void RequestReflow(nsIPresShell::IntrinsicDirty aType);
   void UpdateGraphic();
 
-  // Returns GetCanvasTM followed by a scale from CSS px to Dev px. Used for
-  // painting, because children expect to paint to device space, not userspace.
-  gfxMatrix GetCanvasTMForChildren();
+  gfxMatrix GetCanvasTMWithTranslation();
+  // Scale TM from CSS px to Dev px. Used for painting, because children
+  // expect to paint to device space, not userspace.
+  gfxMatrix GetScaledMatrixForChildren(gfxMatrix canvasTMWithTranslation) const;
   void InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter,
                            const nsRect& aRect, PRUint32 aFlags);
   void FlushDirtyRegion();
 
   // If width or height is less than or equal to zero we must disable rendering
   PRBool IsDisabled() const { return mRect.width <= 0 || mRect.height <= 0; }
 
   nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;