Bug 490003: Take outer <svg> element's border and padding into account, for invalidation and hit-testing. r=roc
authorRobert Longson <longsonr@gmail.com>
Wed, 12 May 2010 14:41:47 -0700
changeset 42235 fbca3a801c22288c7df56434f5a1c2ec378f6f26
parent 42234 fd883c55f992be5fb3fc147ebabfa168ddd6d331
child 42236 0e11e454db6c4134825bf0629cccbf1ef52bbddb
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs490003
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 490003: Take outer <svg> element's border and padding into account, for invalidation and hit-testing. r=roc
layout/reftests/svg/outer-svg-border-and-padding-01-ref.svg
layout/reftests/svg/outer-svg-border-and-padding-01.svg
layout/reftests/svg/reftest.list
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/svg/base/src/nsSVGUtils.cpp
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/outer-svg-border-and-padding-01-ref.svg
@@ -0,0 +1,9 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     style="border: 10px solid lime; padding: 10px; background: lime;">
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=490003 -->
+  <title>Reference that invalidation takes account of outer-&lt;svg&gt; border/padding</title>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/outer-svg-border-and-padding-01.svg
@@ -0,0 +1,58 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="reftest-wait"
+     style="border: 10px solid lime; padding: 10px; background: lime;">
+  <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=490003 -->
+  <title>Test that invalidation takes account of outer-&lt;svg&gt; border/padding</title>
+  <desc>
+    This test checks that SVG implementations take account of border and
+    padding on outer &lt;svg&gt; when doing invalidation.
+  </desc>
+
+  <filter id="identity">
+    <feColorMatrix type="saturate" in="SourceGraphic"/>
+  </filter>
+
+  <script type="text/javascript">//<![CDATA[
+
+function hide_red_rects()
+{
+  var background = document.getElementById("background");
+  
+  document.getElementById('r').setAttribute('opacity','0');
+  document.getElementById('f').setAttribute('opacity','0');
+  
+  <!-- top left -->
+  document.elementFromPoint(121, 121).setAttribute('opacity','0');
+  <!-- bottom right -->
+  document.elementFromPoint(269, 169).setAttribute('opacity','0');
+
+  <!-- outside top left -->
+  if (document.elementFromPoint(119, 119) != background) {
+    background.setAttribute("fill", "red");
+  }
+  <!-- outside bottom right -->
+  if (document.elementFromPoint(271, 171) != background) {
+    background.setAttribute("fill", "purple");
+  }
+
+  document.documentElement.removeAttribute('class');
+}
+
+document.addEventListener("MozReftestInvalidate", hide_red_rects, false);
+setTimeout(hide_red_rects, 1000)
+
+  //]]>
+  </script>
+
+  <!-- to catch misses-->
+  <rect id="background" width="100%" height="100%" fill="lime"/>
+  
+  <rect id="r" width="50" height="50" fill="red"/>
+  <rect id="f" y="100" width="50" height="50" fill="red"/>
+  
+  <rect x="100" y="100" width="50" height="50" fill="red"/>
+  <rect x="200" y="100" width="50" height="50" fill="red"/>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -110,16 +110,17 @@ fails == inline-in-xul-basic-01.xul pass
 == objectBoundingBox-and-fePointLight-01.svg objectBoundingBox-and-fePointLight-01-ref.svg
 == objectBoundingBox-and-mask.svg pass.svg
 == objectBoundingBox-and-pattern-01a.svg objectBoundingBox-and-pattern-01-ref.svg
 == objectBoundingBox-and-pattern-01b.svg objectBoundingBox-and-pattern-01-ref.svg
 == objectBoundingBox-and-pattern-01c.svg objectBoundingBox-and-pattern-01-ref.svg
 == opacity-and-gradient-01.svg pass.svg
 == opacity-and-gradient-02.svg opacity-and-gradient-02-ref.svg
 == opacity-and-pattern-01.svg pass.svg
+== outer-svg-border-and-padding-01.svg outer-svg-border-and-padding-01-ref.svg 
 == polyline-points-invalid-01.svg pass.svg
 == path-01.svg path-01-ref.svg
 == path-02.svg pass.svg
 == pattern-live-01a.svg pattern-live-01-ref.svg
 == pattern-live-01b.svg pattern-live-01-ref.svg
 == pattern-live-01c.svg pattern-live-01-ref.svg
 == pseudo-classes-01.svg pass.svg
 # This test depends on :visited styles (which are asynchronous), so we run
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -435,26 +435,28 @@ public:
                      nsIRenderingContext* aCtx);
   NS_DISPLAY_DECL_NAME("SVGEventReceiver")
 };
 
 void
 nsDisplaySVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
 {
+  nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
   nsRect rectAtOrigin = aRect - aBuilder->ToReferenceFrame(mFrame);
-  nsRect thisRect(nsPoint(0,0), static_cast<nsSVGOuterSVGFrame*>(mFrame)->GetSize());
+  nsRect thisRect(nsPoint(0,0), outerSVGFrame->GetSize());
   if (!thisRect.Intersects(rectAtOrigin))
     return;
 
   nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2,
                      rectAtOrigin.y + rectAtOrigin.height / 2);
 
-  nsIFrame* frame = nsSVGUtils::HitTestChildren(static_cast<nsSVGOuterSVGFrame*>(mFrame),
-                                                rectCenter);
+  nsIFrame* frame = nsSVGUtils::HitTestChildren(
+    outerSVGFrame, rectCenter + outerSVGFrame->GetPosition() -
+                   outerSVGFrame->GetContentRect().TopLeft());
   if (frame) {
     aOutFrames->AppendElement(frame);
   }
 }
 
 void
 nsDisplaySVG::Paint(nsDisplayListBuilder* aBuilder,
                     nsIRenderingContext* aCtx)
@@ -509,17 +511,18 @@ nsSVGOuterSVGFrame::AttributeChanged(PRI
 nsIFrame*
 nsSVGOuterSVGFrame::GetFrameForPoint(const nsPoint& aPoint)
 {
   nsRect thisRect(nsPoint(0,0), GetSize());
   if (!thisRect.Contains(aPoint)) {
     return nsnull;
   }
 
-  return nsSVGUtils::HitTestChildren(this, aPoint);
+  return nsSVGUtils::HitTestChildren(
+    this, aPoint + GetPosition() - GetContentRect().TopLeft());
 }
 
 //----------------------------------------------------------------------
 // painting
 
 NS_IMETHODIMP
 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                      const nsRect&           aDirtyRect,
@@ -533,21 +536,18 @@ nsSVGOuterSVGFrame::BuildDisplayList(nsD
 
 void
 nsSVGOuterSVGFrame::Paint(nsIRenderingContext& aRenderingContext,
                           const nsRect& aDirtyRect, nsPoint aPt)
 {
   // initialize Mozilla rendering context
   aRenderingContext.PushState();
 
-  nsMargin bp = GetUsedBorderAndPadding();
-  ApplySkipSides(bp);
-
   nsRect viewportRect = GetContentRect();
-  nsPoint viewportOffset = aPt + nsPoint(bp.left, bp.top);
+  nsPoint viewportOffset = aPt + viewportRect.TopLeft() - GetPosition();
   viewportRect.MoveTo(viewportOffset);
 
   nsRect clipRect;
   clipRect.IntersectRect(aDirtyRect, viewportRect);
   aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
   aRenderingContext.Translate(viewportRect.x, viewportRect.y);
   nsRect dirtyRect = clipRect - viewportOffset;
 
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -617,19 +617,17 @@ nsSVGUtils::FindFilterInvalidation(nsIFr
       }
       if (!viewportFrame) {
         viewportFrame = GetOuterSVGFrame(aFrame);
       }
       if (viewportFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
         nsRect r = viewportFrame->GetOverflowRect();
         // GetOverflowRect is relative to our border box, but we need it
         // relative to our content box.
-        nsMargin bp = viewportFrame->GetUsedBorderAndPadding();
-        viewportFrame->ApplySkipSides(bp);
-        r.MoveBy(-bp.left, -bp.top);
+        r.MoveBy(viewportFrame->GetPosition() - viewportFrame->GetContentRect().TopLeft());
         return r;
       }
       NS_ASSERTION(viewportFrame->GetType() == nsGkAtoms::svgInnerSVGFrame,
                    "Wrong frame type");
       nsSVGInnerSVGFrame* innerSvg = do_QueryFrame(static_cast<nsIFrame*>(viewportFrame));
       nsSVGDisplayContainerFrame* innerSvgParent = do_QueryFrame(viewportFrame->GetParent());
       float x, y, width, height;
       static_cast<nsSVGSVGElement*>(innerSvg->GetContent())->
@@ -643,17 +641,23 @@ nsSVGUtils::FindFilterInvalidation(nsIFr
       } else {
         NS_NOTREACHED("Not going to invalidate the correct area");
       }
       aFrame = viewportFrame;
     }
     aFrame = aFrame->GetParent();
   }
 
-  return rect.ToAppUnits(appUnitsPerDevPixel);
+  nsRect r = rect.ToAppUnits(appUnitsPerDevPixel);
+  if (aFrame) {
+    NS_ASSERTION(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG,
+                 "outer SVG frame expected");
+    r.MoveBy(aFrame->GetContentRect().TopLeft() - aFrame->GetPosition());
+  }
+  return r;
 }
 
 void
 nsSVGUtils::InvalidateCoveredRegion(nsIFrame *aFrame)
 {
   if (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
     return;