Bug 1299876 - Part 1. Correct the algorithm of stroke box computing. draft
authorcku <cku@mozilla.com>
Tue, 04 Oct 2016 00:51:24 +0800
changeset 420226 a9f40311903c8307c776fab58913c19877893709
parent 420017 955840bfd3c20eb24dd5a01be27bdc55c489a285
child 420227 7e54631618314aa26bdd0eb7dbc536f50b700f33
push id31136
push userbmo:cku@mozilla.com
push dateMon, 03 Oct 2016 19:07:10 +0000
bugs1299876
milestone52.0a1
Bug 1299876 - Part 1. Correct the algorithm of stroke box computing. MozReview-Commit-ID: F3hXSuFCosV
layout/svg/nsCSSClipPathInstance.cpp
--- a/layout/svg/nsCSSClipPathInstance.cpp
+++ b/layout/svg/nsCSSClipPathInstance.cpp
@@ -9,16 +9,17 @@
 #include "gfx2DGlue.h"
 #include "gfxPlatform.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "nsCSSRendering.h"
 #include "nsIFrame.h"
 #include "nsRenderingContext.h"
 #include "nsRuleNode.h"
+#include "SVGUseElement.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 /* static*/ void
 nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext,
                                            nsIFrame* aFrame)
 {
@@ -57,31 +58,94 @@ nsCSSClipPathInstance::HitTestBasicShape
   RefPtr<DrawTarget> drawTarget =
     gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
   RefPtr<Path> path = instance.CreateClipPath(drawTarget);
   float pixelRatio = float(nsPresContext::AppUnitsPerCSSPixel()) /
                      aFrame->PresContext()->AppUnitsPerDevPixel();
   return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
 }
 
+static
+gfxRect ComputeStrokeBoundingBox(nsIFrame* aFrame)
+{
+  if (!aFrame) {
+    return gfxRect();
+  }
+
+  nsIContent* content = aFrame->GetContent();
+  gfxRect result = nsSVGUtils::GetBBox(aFrame,
+                                       nsSVGUtils::eBBoxIncludeFill);
+  if (nsSVGUtils::HasStroke(aFrame)) {
+    const nsStyleSVG* style = aFrame->StyleSVG();
+    float delta = nsSVGUtils::GetStrokeWidth(aFrame) / 2.0;
+
+    if (!content->IsAnyOfSVGElements(nsGkAtoms::rect, nsGkAtoms::ellipse,
+                                     nsGkAtoms::circle, nsGkAtoms::image)) {
+      if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER) {
+        float miter = style->mStrokeMiterlimit;
+        delta = (miter < M_SQRT2 &&
+                 style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE)
+                ? delta * M_SQRT2 : miter;
+      } else if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
+        delta *= M_SQRT2;
+      }
+    }
+
+    result.Inflate(delta);
+  }
+
+  return result;
+}
+
 nsRect
 nsCSSClipPathInstance::ComputeSVGReferenceRect()
 {
   MOZ_ASSERT(mTargetFrame->GetContent()->IsSVGElement());
   nsRect r;
 
   // For SVG elements without associated CSS layout box, the used value for
   // content-box, padding-box, border-box and margin-box is fill-box.
   switch (mClipPathStyle.GetReferenceBox()) {
     case StyleClipPathGeometryBox::Stroke: {
-      // XXX Bug 1299876
-      // The size of srtoke-box is not correct if this graphic element has
-      // specific stroke-linejoin or stroke-linecap.
-      gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame,
-                nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeStroke);
+      nsIContent* content = mTargetFrame->GetContent();
+      gfxRect bbox;
+
+      if ((content->IsSVGElement(nsGkAtoms::a) &&
+          nsContentUtils::HasNonEmptyTextContent(content,
+                                  nsContentUtils::eDontRecurseIntoChildren)) ||
+          (content->IsAnyOfSVGElements(nsGkAtoms::circle, nsGkAtoms::ellipse,
+                                       nsGkAtoms::line, nsGkAtoms::path,
+                                       nsGkAtoms::polygon, nsGkAtoms::polyline,
+                                       nsGkAtoms::rect, nsGkAtoms::text))) {
+        // a graphics element without <use> or <image> or an <a> element with
+        // a text content element
+        bbox = ComputeStrokeBoundingBox(mTargetFrame);
+      } else if (content->IsAnyOfSVGElements(nsGkAtoms::a, nsGkAtoms::defs,
+                                             nsGkAtoms::glyphRef, nsGkAtoms::g,
+                                             nsGkAtoms::marker,
+                                             nsGkAtoms::mask,
+                                             nsGkAtoms::missingGlyph,
+                                             nsGkAtoms::pattern,
+                                             nsGkAtoms::svgSwitch,
+                                             nsGkAtoms::symbol)) {
+        // a container element or <use>
+        nsIContent* root = content->IsSVGElement(nsGkAtoms::use)
+                           ? static_cast<SVGUseElement*>(content)->GetAnonymousContent()
+                           : content;
+        for (nsIContent* child = root->GetFirstChild();
+             child && child->GetParentNode() == root;
+             child = child->GetNextSibling()) {
+          bbox =
+            bbox.Union(ComputeStrokeBoundingBox(child->GetPrimaryFrame()));
+        }
+      } else {
+        // an image element.
+        bbox = nsSVGUtils::GetBBox(mTargetFrame, nsSVGUtils::eBBoxIncludeFill);
+      }
+
       r = nsLayoutUtils::RoundGfxRectToAppRect(bbox,
                                          nsPresContext::AppUnitsPerCSSPixel());
       break;
     }
     case StyleClipPathGeometryBox::View: {
       nsIContent* content = mTargetFrame->GetContent();
       nsSVGElement* element = static_cast<nsSVGElement*>(content);
       SVGSVGElement* svgElement = element->GetCtx();