layout/svg/nsSVGUtils.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/svg/nsSVGUtils.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1546 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// Main header first:
    1.10 +// This is also necessary to ensure our definition of M_SQRT1_2 is picked up
    1.11 +#include "nsSVGUtils.h"
    1.12 +#include <algorithm>
    1.13 +
    1.14 +// Keep others in (case-insensitive) order:
    1.15 +#include "gfx2DGlue.h"
    1.16 +#include "gfxContext.h"
    1.17 +#include "gfxMatrix.h"
    1.18 +#include "gfxPlatform.h"
    1.19 +#include "gfxRect.h"
    1.20 +#include "gfxUtils.h"
    1.21 +#include "mozilla/gfx/2D.h"
    1.22 +#include "mozilla/Preferences.h"
    1.23 +#include "nsCSSFrameConstructor.h"
    1.24 +#include "nsDisplayList.h"
    1.25 +#include "nsFilterInstance.h"
    1.26 +#include "nsFrameList.h"
    1.27 +#include "nsGkAtoms.h"
    1.28 +#include "nsIContent.h"
    1.29 +#include "nsIDocument.h"
    1.30 +#include "nsIFrame.h"
    1.31 +#include "nsIPresShell.h"
    1.32 +#include "nsISVGChildFrame.h"
    1.33 +#include "nsPresContext.h"
    1.34 +#include "nsRenderingContext.h"
    1.35 +#include "nsStyleCoord.h"
    1.36 +#include "nsStyleStruct.h"
    1.37 +#include "nsSVGClipPathFrame.h"
    1.38 +#include "nsSVGContainerFrame.h"
    1.39 +#include "nsSVGEffects.h"
    1.40 +#include "nsSVGFilterPaintCallback.h"
    1.41 +#include "nsSVGForeignObjectFrame.h"
    1.42 +#include "gfxSVGGlyphs.h"
    1.43 +#include "nsSVGInnerSVGFrame.h"
    1.44 +#include "nsSVGIntegrationUtils.h"
    1.45 +#include "nsSVGLength2.h"
    1.46 +#include "nsSVGMaskFrame.h"
    1.47 +#include "nsSVGOuterSVGFrame.h"
    1.48 +#include "mozilla/dom/SVGPathElement.h"
    1.49 +#include "nsSVGPathGeometryElement.h"
    1.50 +#include "nsSVGPathGeometryFrame.h"
    1.51 +#include "nsSVGPaintServerFrame.h"
    1.52 +#include "mozilla/dom/SVGSVGElement.h"
    1.53 +#include "nsTextFrame.h"
    1.54 +#include "SVGContentUtils.h"
    1.55 +#include "mozilla/unused.h"
    1.56 +
    1.57 +using namespace mozilla;
    1.58 +using namespace mozilla::dom;
    1.59 +using namespace mozilla::gfx;
    1.60 +
    1.61 +static bool sSVGDisplayListHitTestingEnabled;
    1.62 +static bool sSVGDisplayListPaintingEnabled;
    1.63 +
    1.64 +bool
    1.65 +NS_SVGDisplayListHitTestingEnabled()
    1.66 +{
    1.67 +  return sSVGDisplayListHitTestingEnabled;
    1.68 +}
    1.69 +
    1.70 +bool
    1.71 +NS_SVGDisplayListPaintingEnabled()
    1.72 +{
    1.73 +  return sSVGDisplayListPaintingEnabled;
    1.74 +}
    1.75 +
    1.76 +// we only take the address of this:
    1.77 +static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
    1.78 +
    1.79 +SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
    1.80 +                                       RenderMode aMode
    1.81 +                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
    1.82 +  : mContext(aContext)
    1.83 +  , mOriginalRenderState(nullptr)
    1.84 +  , mMode(aMode)
    1.85 +  , mPaintingToWindow(false)
    1.86 +{
    1.87 +  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    1.88 +  mOriginalRenderState = aContext->RemoveUserData(&sSVGAutoRenderStateKey);
    1.89 +  // We always remove ourselves from aContext before it dies, so
    1.90 +  // passing nullptr as the destroy function is okay.
    1.91 +  aContext->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
    1.92 +}
    1.93 +
    1.94 +SVGAutoRenderState::~SVGAutoRenderState()
    1.95 +{
    1.96 +  mContext->RemoveUserData(&sSVGAutoRenderStateKey);
    1.97 +  if (mOriginalRenderState) {
    1.98 +    mContext->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState, nullptr);
    1.99 +  }
   1.100 +}
   1.101 +
   1.102 +void
   1.103 +SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
   1.104 +{
   1.105 +  mPaintingToWindow = aPaintingToWindow;
   1.106 +}
   1.107 +
   1.108 +/* static */ SVGAutoRenderState::RenderMode
   1.109 +SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
   1.110 +{
   1.111 +  void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
   1.112 +  if (state) {
   1.113 +    return static_cast<SVGAutoRenderState*>(state)->mMode;
   1.114 +  }
   1.115 +  return NORMAL;
   1.116 +}
   1.117 +
   1.118 +/* static */ bool
   1.119 +SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
   1.120 +{
   1.121 +  void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
   1.122 +  if (state) {
   1.123 +    return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
   1.124 +  }
   1.125 +  return false;
   1.126 +}
   1.127 +
   1.128 +void
   1.129 +nsSVGUtils::Init()
   1.130 +{
   1.131 +  Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
   1.132 +                               "svg.display-lists.hit-testing.enabled");
   1.133 +
   1.134 +  Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
   1.135 +                               "svg.display-lists.painting.enabled");
   1.136 +}
   1.137 +
   1.138 +nsSVGDisplayContainerFrame*
   1.139 +nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
   1.140 +{
   1.141 +  NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
   1.142 +  if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
   1.143 +    return nullptr;
   1.144 +  }
   1.145 +  while ((aFrame = aFrame->GetParent())) {
   1.146 +    NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
   1.147 +    if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame ||
   1.148 +        aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
   1.149 +      return do_QueryFrame(aFrame);
   1.150 +    }
   1.151 +  }
   1.152 +  NS_NOTREACHED("This is not reached. It's only needed to compile.");
   1.153 +  return nullptr;
   1.154 +}
   1.155 +
   1.156 +nsRect
   1.157 +nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
   1.158 +                                            const nsRect &aPreFilterRect)
   1.159 +{
   1.160 +  NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
   1.161 +                    "Called on invalid frame type");
   1.162 +
   1.163 +  nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
   1.164 +  if (!property || !property->ReferencesValidResources()) {
   1.165 +    return aPreFilterRect;
   1.166 +  }
   1.167 +
   1.168 +  return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
   1.169 +}
   1.170 +
   1.171 +bool
   1.172 +nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
   1.173 +{
   1.174 +  return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
   1.175 +}
   1.176 +
   1.177 +bool
   1.178 +nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame)
   1.179 +{
   1.180 +  nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
   1.181 +  do {
   1.182 +    if (outer->IsCallingReflowSVG()) {
   1.183 +      return true;
   1.184 +    }
   1.185 +    outer = GetOuterSVGFrame(outer->GetParent());
   1.186 +  } while (outer);
   1.187 +  return false;
   1.188 +}
   1.189 +
   1.190 +void
   1.191 +nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
   1.192 +{
   1.193 +  NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
   1.194 +                    "Passed bad frame!");
   1.195 +
   1.196 +  // If this is triggered, the callers should be fixed to call us before
   1.197 +  // ReflowSVG is called. If we try to mark dirty bits on frames while we're
   1.198 +  // in the process of removing them, things will get messed up.
   1.199 +  NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
   1.200 +               "Do not call under nsISVGChildFrame::ReflowSVG!");
   1.201 +
   1.202 +  // We don't call nsSVGEffects::InvalidateRenderingObservers here because
   1.203 +  // we should only be called under InvalidateAndScheduleReflowSVG (which
   1.204 +  // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
   1.205 +  // (at which point the frame has no observers).
   1.206 +
   1.207 +  if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
   1.208 +    return;
   1.209 +  }
   1.210 +
   1.211 +  if (aFrame->GetStateBits() &
   1.212 +      (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
   1.213 +    // Nothing to do if we're already dirty, or if the outer-<svg>
   1.214 +    // hasn't yet had its initial reflow.
   1.215 +    return;
   1.216 +  }
   1.217 +
   1.218 +  nsSVGOuterSVGFrame *outerSVGFrame = nullptr;
   1.219 +
   1.220 +  // We must not add dirty bits to the nsSVGOuterSVGFrame or else
   1.221 +  // PresShell::FrameNeedsReflow won't work when we pass it in below.
   1.222 +  if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
   1.223 +    outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
   1.224 +  } else {
   1.225 +    aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
   1.226 +
   1.227 +    nsIFrame *f = aFrame->GetParent();
   1.228 +    while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
   1.229 +      if (f->GetStateBits() &
   1.230 +          (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
   1.231 +        return;
   1.232 +      }
   1.233 +      f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   1.234 +      f = f->GetParent();
   1.235 +      NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG),
   1.236 +                        "NS_STATE_IS_OUTER_SVG check above not valid!");
   1.237 +    }
   1.238 +
   1.239 +    outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
   1.240 +
   1.241 +    NS_ABORT_IF_FALSE(outerSVGFrame &&
   1.242 +                      outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame,
   1.243 +                      "Did not find nsSVGOuterSVGFrame!");
   1.244 +  }
   1.245 +
   1.246 +  if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
   1.247 +    // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
   1.248 +    // need to call PresShell::FrameNeedsReflow, since we have an
   1.249 +    // nsSVGOuterSVGFrame::DidReflow call pending.
   1.250 +    return;
   1.251 +  }
   1.252 +
   1.253 +  nsFrameState dirtyBit =
   1.254 +    (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN);
   1.255 +
   1.256 +  aFrame->PresContext()->PresShell()->FrameNeedsReflow(
   1.257 +    outerSVGFrame, nsIPresShell::eResize, dirtyBit);
   1.258 +}
   1.259 +
   1.260 +bool
   1.261 +nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame)
   1.262 +{
   1.263 +  NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
   1.264 +                    "SVG uses bits differently!");
   1.265 +
   1.266 +  // The flags we test here may change, hence why we have this separate
   1.267 +  // function.
   1.268 +  return NS_SUBTREE_DIRTY(aFrame);
   1.269 +}
   1.270 +
   1.271 +void
   1.272 +nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
   1.273 +{
   1.274 +  NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
   1.275 +                    "Not expecting to be called on the outer SVG Frame");
   1.276 +
   1.277 +  aFrame = aFrame->GetParent();
   1.278 +
   1.279 +  while (aFrame) {
   1.280 +    if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
   1.281 +      return;
   1.282 +
   1.283 +    nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
   1.284 +    if (property) {
   1.285 +      property->Invalidate();
   1.286 +    }
   1.287 +    aFrame = aFrame->GetParent();
   1.288 +  }
   1.289 +}
   1.290 +
   1.291 +float
   1.292 +nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
   1.293 +{
   1.294 +  float axis;
   1.295 +
   1.296 +  switch (aLength->GetCtxType()) {
   1.297 +  case SVGContentUtils::X:
   1.298 +    axis = aRect.Width();
   1.299 +    break;
   1.300 +  case SVGContentUtils::Y:
   1.301 +    axis = aRect.Height();
   1.302 +    break;
   1.303 +  case SVGContentUtils::XY:
   1.304 +    axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
   1.305 +                   aRect.Width(), aRect.Height()));
   1.306 +    break;
   1.307 +  default:
   1.308 +    NS_NOTREACHED("unexpected ctx type");
   1.309 +    axis = 0.0f;
   1.310 +    break;
   1.311 +  }
   1.312 +  if (aLength->IsPercentage()) {
   1.313 +    // Multiply first to avoid precision errors:
   1.314 +    return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
   1.315 +  }
   1.316 +  return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis;
   1.317 +}
   1.318 +
   1.319 +float
   1.320 +nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
   1.321 +{
   1.322 +  return aLength->GetAnimValue(aSVGElement);
   1.323 +}
   1.324 +
   1.325 +float
   1.326 +nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
   1.327 +{
   1.328 +  return aLength->GetAnimValue(aNonSVGContext);
   1.329 +}
   1.330 +
   1.331 +nsSVGOuterSVGFrame *
   1.332 +nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
   1.333 +{
   1.334 +  while (aFrame) {
   1.335 +    if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
   1.336 +      return static_cast<nsSVGOuterSVGFrame*>(aFrame);
   1.337 +    }
   1.338 +    aFrame = aFrame->GetParent();
   1.339 +  }
   1.340 +
   1.341 +  return nullptr;
   1.342 +}
   1.343 +
   1.344 +nsIFrame*
   1.345 +nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
   1.346 +{
   1.347 +  nsISVGChildFrame* svg = do_QueryFrame(aFrame);
   1.348 +  if (!svg)
   1.349 +    return nullptr;
   1.350 +  *aRect = (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
   1.351 +             nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
   1.352 +  return GetOuterSVGFrame(aFrame);
   1.353 +}
   1.354 +
   1.355 +gfxMatrix
   1.356 +nsSVGUtils::GetCanvasTM(nsIFrame *aFrame, uint32_t aFor,
   1.357 +                        nsIFrame* aTransformRoot)
   1.358 +{
   1.359 +  // XXX yuck, we really need a common interface for GetCanvasTM
   1.360 +
   1.361 +  if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
   1.362 +    return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
   1.363 +  }
   1.364 +
   1.365 +  if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
   1.366 +      !aTransformRoot) {
   1.367 +    if ((aFor == nsISVGChildFrame::FOR_PAINTING &&
   1.368 +         NS_SVGDisplayListPaintingEnabled()) ||
   1.369 +        (aFor == nsISVGChildFrame::FOR_HIT_TESTING &&
   1.370 +         NS_SVGDisplayListHitTestingEnabled())) {
   1.371 +      return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
   1.372 +    }
   1.373 +  }
   1.374 +
   1.375 +  nsIAtom* type = aFrame->GetType();
   1.376 +  if (type == nsGkAtoms::svgForeignObjectFrame) {
   1.377 +    return static_cast<nsSVGForeignObjectFrame*>(aFrame)->
   1.378 +        GetCanvasTM(aFor, aTransformRoot);
   1.379 +  }
   1.380 +  if (type == nsGkAtoms::svgOuterSVGFrame) {
   1.381 +    return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
   1.382 +  }
   1.383 +
   1.384 +  nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
   1.385 +  if (containerFrame) {
   1.386 +    return containerFrame->GetCanvasTM(aFor, aTransformRoot);
   1.387 +  }
   1.388 +
   1.389 +  return static_cast<nsSVGPathGeometryFrame*>(aFrame)->
   1.390 +      GetCanvasTM(aFor, aTransformRoot);
   1.391 +}
   1.392 +
   1.393 +gfxMatrix
   1.394 +nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame, uint32_t aFor)
   1.395 +{
   1.396 +  NS_ASSERTION(aFor == nsISVGChildFrame::FOR_OUTERSVG_TM,
   1.397 +               "Unexpected aFor?");
   1.398 +
   1.399 +  nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
   1.400 +  NS_ASSERTION(svgFrame, "bad frame");
   1.401 +
   1.402 +  gfxMatrix tm;
   1.403 +  if (svgFrame) {
   1.404 +    nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
   1.405 +    tm = content->PrependLocalTransformsTo(
   1.406 +                    GetCanvasTM(aFrame->GetParent(), aFor),
   1.407 +                    nsSVGElement::eUserSpaceToParent);
   1.408 +  }
   1.409 +  return tm;
   1.410 +}
   1.411 +
   1.412 +void 
   1.413 +nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
   1.414 +{
   1.415 +  nsIFrame *kid = aFrame->GetFirstPrincipalChild();
   1.416 +
   1.417 +  while (kid) {
   1.418 +    nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
   1.419 +    if (SVGFrame) {
   1.420 +      SVGFrame->NotifySVGChanged(aFlags); 
   1.421 +    } else {
   1.422 +      NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) || kid->IsSVGText(),
   1.423 +                   "SVG frame expected");
   1.424 +      // recurse into the children of container frames e.g. <clipPath>, <mask>
   1.425 +      // in case they have child frames with transformation matrices
   1.426 +      if (kid->IsFrameOfType(nsIFrame::eSVG)) {
   1.427 +        NotifyChildrenOfSVGChange(kid, aFlags);
   1.428 +      }
   1.429 +    }
   1.430 +    kid = kid->GetNextSibling();
   1.431 +  }
   1.432 +}
   1.433 +
   1.434 +// ************************************************************
   1.435 +
   1.436 +class SVGPaintCallback : public nsSVGFilterPaintCallback
   1.437 +{
   1.438 +public:
   1.439 +  virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
   1.440 +                     const nsIntRect* aDirtyRect,
   1.441 +                     nsIFrame* aTransformRoot) MOZ_OVERRIDE
   1.442 +  {
   1.443 +    nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
   1.444 +    NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
   1.445 +
   1.446 +    nsIntRect* dirtyRect = nullptr;
   1.447 +    nsIntRect tmpDirtyRect;
   1.448 +
   1.449 +    // aDirtyRect is in user-space pixels, we need to convert to
   1.450 +    // outer-SVG-frame-relative device pixels.
   1.451 +    if (aDirtyRect) {
   1.452 +      gfxMatrix userToDeviceSpace =
   1.453 +        nsSVGUtils::GetCanvasTM(aTarget, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
   1.454 +      if (userToDeviceSpace.IsSingular()) {
   1.455 +        return;
   1.456 +      }
   1.457 +      gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
   1.458 +        gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
   1.459 +      dirtyBounds.RoundOut();
   1.460 +      if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
   1.461 +        dirtyRect = &tmpDirtyRect;
   1.462 +      }
   1.463 +    }
   1.464 +
   1.465 +    svgChildFrame->PaintSVG(aContext, dirtyRect, aTransformRoot);
   1.466 +  }
   1.467 +};
   1.468 +
   1.469 +void
   1.470 +nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
   1.471 +                                  const nsIntRect *aDirtyRect,
   1.472 +                                  nsIFrame *aFrame,
   1.473 +                                  nsIFrame *aTransformRoot)
   1.474 +{
   1.475 +  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
   1.476 +               (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
   1.477 +               aFrame->PresContext()->IsGlyph(),
   1.478 +               "If display lists are enabled, only painting of non-display "
   1.479 +               "SVG should take this code path");
   1.480 +
   1.481 +  nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
   1.482 +  if (!svgChildFrame)
   1.483 +    return;
   1.484 +
   1.485 +  float opacity = aFrame->StyleDisplay()->mOpacity;
   1.486 +  if (opacity == 0.0f)
   1.487 +    return;
   1.488 +
   1.489 +  const nsIContent* content = aFrame->GetContent();
   1.490 +  if (content->IsSVG() &&
   1.491 +      !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   1.492 +    return;
   1.493 +  }
   1.494 +
   1.495 +  /* Properties are added lazily and may have been removed by a restyle,
   1.496 +     so make sure all applicable ones are set again. */
   1.497 +
   1.498 +  nsSVGEffects::EffectProperties effectProperties =
   1.499 +    nsSVGEffects::GetEffectProperties(aFrame);
   1.500 +
   1.501 +  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
   1.502 +
   1.503 +  if (aDirtyRect &&
   1.504 +      !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
   1.505 +    // Here we convert aFrame's paint bounds to outer-<svg> device space,
   1.506 +    // compare it to aDirtyRect, and return early if they don't intersect.
   1.507 +    // We don't do this optimization for nondisplay SVG since nondisplay
   1.508 +    // SVG doesn't maintain bounds/overflow rects.
   1.509 +    nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
   1.510 +    if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
   1.511 +        aFrame->IsSVGText()) {
   1.512 +      // Unlike containers, leaf frames do not include GetPosition() in
   1.513 +      // GetCanvasTM().
   1.514 +      overflowRect = overflowRect + aFrame->GetPosition();
   1.515 +    }
   1.516 +    int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
   1.517 +    gfxMatrix tm = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
   1.518 +    if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
   1.519 +      gfx::Matrix childrenOnlyTM;
   1.520 +      if (static_cast<nsSVGContainerFrame*>(aFrame)->
   1.521 +            HasChildrenOnlyTransform(&childrenOnlyTM)) {
   1.522 +        // Undo the children-only transform:
   1.523 +        tm = ThebesMatrix(childrenOnlyTM).Invert() * tm;
   1.524 +      }
   1.525 +    }
   1.526 +    nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
   1.527 +                         tm, aFrame->PresContext()).
   1.528 +                           ToOutsidePixels(appUnitsPerDevPx);
   1.529 +    if (!aDirtyRect->Intersects(bounds)) {
   1.530 +      return;
   1.531 +    }
   1.532 +  }
   1.533 +
   1.534 +  /* SVG defines the following rendering model:
   1.535 +   *
   1.536 +   *  1. Render fill
   1.537 +   *  2. Render stroke
   1.538 +   *  3. Render markers
   1.539 +   *  4. Apply filter
   1.540 +   *  5. Apply clipping, masking, group opacity
   1.541 +   *
   1.542 +   * We follow this, but perform a couple of optimizations:
   1.543 +   *
   1.544 +   * + Use cairo's clipPath when representable natively (single object
   1.545 +   *   clip region).
   1.546 +   *
   1.547 +   * + Merge opacity and masking if both used together.
   1.548 +   */
   1.549 +
   1.550 +  if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
   1.551 +    opacity = 1.0f;
   1.552 +
   1.553 +  gfxContext *gfx = aContext->ThebesContext();
   1.554 +  bool complexEffects = false;
   1.555 +
   1.556 +  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
   1.557 +  nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
   1.558 +
   1.559 +  bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
   1.560 +
   1.561 +  if (!isOK) {
   1.562 +    // Some resource is invalid. We shouldn't paint anything.
   1.563 +    return;
   1.564 +  }
   1.565 +  
   1.566 +  gfxMatrix matrix;
   1.567 +  if (clipPathFrame || maskFrame)
   1.568 +    matrix = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
   1.569 +
   1.570 +  /* Check if we need to do additional operations on this child's
   1.571 +   * rendering, which necessitates rendering into another surface. */
   1.572 +  if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
   1.573 +      || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
   1.574 +    complexEffects = true;
   1.575 +    gfx->Save();
   1.576 +    if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
   1.577 +      // aFrame has a valid visual overflow rect, so clip to it before calling
   1.578 +      // PushGroup() to minimize the size of the surfaces we'll composite:
   1.579 +      gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
   1.580 +      gfx->Multiply(GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot));
   1.581 +      nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
   1.582 +      if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
   1.583 +          aFrame->IsSVGText()) {
   1.584 +        // Unlike containers, leaf frames do not include GetPosition() in
   1.585 +        // GetCanvasTM().
   1.586 +        overflowRect = overflowRect + aFrame->GetPosition();
   1.587 +      }
   1.588 +      aContext->IntersectClip(overflowRect);
   1.589 +    }
   1.590 +    gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   1.591 +  }
   1.592 +
   1.593 +  /* If this frame has only a trivial clipPath, set up cairo's clipping now so
   1.594 +   * we can just do normal painting and get it clipped appropriately.
   1.595 +   */
   1.596 +  if (clipPathFrame && isTrivialClip) {
   1.597 +    gfx->Save();
   1.598 +    clipPathFrame->ClipPaint(aContext, aFrame, matrix);
   1.599 +  }
   1.600 +
   1.601 +  /* Paint the child */
   1.602 +  if (effectProperties.HasValidFilter()) {
   1.603 +    nsRegion* dirtyRegion = nullptr;
   1.604 +    nsRegion tmpDirtyRegion;
   1.605 +    if (aDirtyRect) {
   1.606 +      // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
   1.607 +      // it in frame space.
   1.608 +      gfxMatrix userToDeviceSpace =
   1.609 +        GetUserToCanvasTM(aFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
   1.610 +      if (userToDeviceSpace.IsSingular()) {
   1.611 +        return;
   1.612 +      }
   1.613 +      gfxMatrix deviceToUserSpace = userToDeviceSpace;
   1.614 +      deviceToUserSpace.Invert();
   1.615 +      gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
   1.616 +                              gfxRect(aDirtyRect->x, aDirtyRect->y,
   1.617 +                                      aDirtyRect->width, aDirtyRect->height));
   1.618 +      tmpDirtyRegion =
   1.619 +        nsLayoutUtils::RoundGfxRectToAppRect(
   1.620 +          dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
   1.621 +        aFrame->GetPosition();
   1.622 +      dirtyRegion = &tmpDirtyRegion;
   1.623 +    }
   1.624 +    SVGPaintCallback paintCallback;
   1.625 +    nsFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
   1.626 +                                         dirtyRegion, aTransformRoot);
   1.627 +  } else {
   1.628 +    svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
   1.629 +  }
   1.630 +
   1.631 +  if (clipPathFrame && isTrivialClip) {
   1.632 +    gfx->Restore();
   1.633 +  }
   1.634 +
   1.635 +  /* No more effects, we're done. */
   1.636 +  if (!complexEffects)
   1.637 +    return;
   1.638 +
   1.639 +  gfx->PopGroupToSource();
   1.640 +
   1.641 +  nsRefPtr<gfxPattern> maskSurface =
   1.642 +    maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
   1.643 +                                            matrix, opacity) : nullptr;
   1.644 +
   1.645 +  nsRefPtr<gfxPattern> clipMaskSurface;
   1.646 +  if (clipPathFrame && !isTrivialClip) {
   1.647 +    gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   1.648 +
   1.649 +    nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
   1.650 +    clipMaskSurface = gfx->PopGroup();
   1.651 +
   1.652 +    if (NS_SUCCEEDED(rv) && clipMaskSurface) {
   1.653 +      // Still more set after clipping, so clip to another surface
   1.654 +      if (maskSurface || opacity != 1.0f) {
   1.655 +        gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   1.656 +        gfx->Mask(clipMaskSurface);
   1.657 +        gfx->PopGroupToSource();
   1.658 +      } else {
   1.659 +        gfx->Mask(clipMaskSurface);
   1.660 +      }
   1.661 +    }
   1.662 +  }
   1.663 +
   1.664 +  if (maskSurface) {
   1.665 +    gfx->Mask(maskSurface);
   1.666 +  } else if (opacity != 1.0f) {
   1.667 +    gfx->Paint(opacity);
   1.668 +  }
   1.669 +
   1.670 +  gfx->Restore();
   1.671 +}
   1.672 +
   1.673 +bool
   1.674 +nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
   1.675 +{
   1.676 +  nsSVGEffects::EffectProperties props =
   1.677 +    nsSVGEffects::GetEffectProperties(aFrame);
   1.678 +  if (!props.mClipPath)
   1.679 +    return true;
   1.680 +
   1.681 +  bool isOK = true;
   1.682 +  nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
   1.683 +  if (!isOK) {
   1.684 +    // clipPath is not a valid resource, so nothing gets painted, so
   1.685 +    // hit-testing must fail.
   1.686 +    return false;
   1.687 +  }
   1.688 +  if (!clipPathFrame) {
   1.689 +    // clipPath doesn't exist, ignore it.
   1.690 +    return true;
   1.691 +  }
   1.692 +
   1.693 +  return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame,
   1.694 +                                    nsISVGChildFrame::FOR_HIT_TESTING), aPoint);
   1.695 +}
   1.696 +
   1.697 +nsIFrame *
   1.698 +nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
   1.699 +{
   1.700 +  // Traverse the list in reverse order, so that if we get a hit we know that's
   1.701 +  // the topmost frame that intersects the point; then we can just return it.
   1.702 +  nsIFrame* result = nullptr;
   1.703 +  for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
   1.704 +       current;
   1.705 +       current = current->GetPrevSibling()) {
   1.706 +    nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
   1.707 +    if (SVGFrame) {
   1.708 +      const nsIContent* content = current->GetContent();
   1.709 +      if (content->IsSVG() &&
   1.710 +          !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   1.711 +        continue;
   1.712 +      }
   1.713 +      result = SVGFrame->GetFrameForPoint(aPoint);
   1.714 +      if (result)
   1.715 +        break;
   1.716 +    }
   1.717 +  }
   1.718 +
   1.719 +  if (result && !HitTestClip(aFrame, aPoint))
   1.720 +    result = nullptr;
   1.721 +
   1.722 +  return result;
   1.723 +}
   1.724 +
   1.725 +nsRect
   1.726 +nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
   1.727 +{
   1.728 +  nsRect rect;
   1.729 +
   1.730 +  for (nsIFrame* kid = aFrames.FirstChild();
   1.731 +       kid;
   1.732 +       kid = kid->GetNextSibling()) {
   1.733 +    nsISVGChildFrame* child = do_QueryFrame(kid);
   1.734 +    if (child) {
   1.735 +      nsRect childRect = child->GetCoveredRegion();
   1.736 +      rect.UnionRect(rect, childRect);
   1.737 +    }
   1.738 +  }
   1.739 +
   1.740 +  return rect;
   1.741 +}
   1.742 +
   1.743 +nsPoint
   1.744 +nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint,
   1.745 +                                               const gfxMatrix& aFrameToCanvasTM,
   1.746 +                                               nsPresContext* aPresContext)
   1.747 +{
   1.748 +  NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(),
   1.749 +                    "Callers must not pass a singular matrix");
   1.750 +  gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM;
   1.751 +  canvasDevToFrameUserSpace.Invert();
   1.752 +  gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) /
   1.753 +    aPresContext->AppUnitsPerDevPixel();
   1.754 +  gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt);
   1.755 +  gfxPoint appPt = (userPt * aPresContext->AppUnitsPerCSSPixel()).Round();
   1.756 +  userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
   1.757 +  userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
   1.758 +  // now guaranteed to be safe:
   1.759 +  return nsPoint(nscoord(userPt.x), nscoord(userPt.y));
   1.760 +}
   1.761 +
   1.762 +nsRect
   1.763 +nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
   1.764 +                                         const gfxMatrix& aMatrix,
   1.765 +                                         nsPresContext* aPresContext)
   1.766 +{
   1.767 +  gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   1.768 +  r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
   1.769 +  return nsLayoutUtils::RoundGfxRectToAppRect(
   1.770 +    aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
   1.771 +}
   1.772 +
   1.773 +gfxIntSize
   1.774 +nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
   1.775 +                                 bool *aResultOverflows)
   1.776 +{
   1.777 +  gfxIntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
   1.778 +
   1.779 +  *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
   1.780 +    surfaceSize.height != ceil(aSize.height);
   1.781 +
   1.782 +  if (!gfxASurface::CheckSurfaceSize(surfaceSize)) {
   1.783 +    surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
   1.784 +                               surfaceSize.width);
   1.785 +    surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
   1.786 +                                surfaceSize.height);
   1.787 +    *aResultOverflows = true;
   1.788 +  }
   1.789 +
   1.790 +  return surfaceSize;
   1.791 +}
   1.792 +
   1.793 +bool
   1.794 +nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix,
   1.795 +                        float aRX, float aRY, float aRWidth, float aRHeight,
   1.796 +                        float aX, float aY)
   1.797 +{
   1.798 +  gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
   1.799 +  if (rect.IsEmpty() || aMatrix.IsSingular()) {
   1.800 +    return false;
   1.801 +  }
   1.802 +  gfx::Matrix toRectSpace = aMatrix;
   1.803 +  toRectSpace.Invert();
   1.804 +  gfx::Point p = toRectSpace * gfx::Point(aX, aY);
   1.805 +  return rect.x <= p.x && p.x <= rect.XMost() &&
   1.806 +         rect.y <= p.y && p.y <= rect.YMost();
   1.807 +}
   1.808 +
   1.809 +gfxRect
   1.810 +nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
   1.811 +                                float aX, float aY, float aWidth, float aHeight)
   1.812 +{
   1.813 +  const nsStyleDisplay* disp = aFrame->StyleDisplay();
   1.814 +
   1.815 +  if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) {
   1.816 +    NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO,
   1.817 +                 "We don't know about this type of clip.");
   1.818 +    return gfxRect(aX, aY, aWidth, aHeight);
   1.819 +  }
   1.820 +
   1.821 +  if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
   1.822 +      disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
   1.823 +
   1.824 +    nsIntRect clipPxRect =
   1.825 +      disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
   1.826 +    gfxRect clipRect =
   1.827 +      gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
   1.828 +
   1.829 +    if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) {
   1.830 +      clipRect.width = aWidth - clipRect.X();
   1.831 +    }
   1.832 +    if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) {
   1.833 +      clipRect.height = aHeight - clipRect.Y();
   1.834 +    }
   1.835 +
   1.836 +    if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
   1.837 +      clipRect.x = aX;
   1.838 +      clipRect.width = aWidth;
   1.839 +    }
   1.840 +    if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
   1.841 +      clipRect.y = aY;
   1.842 +      clipRect.height = aHeight;
   1.843 +    }
   1.844 +     
   1.845 +    return clipRect;
   1.846 +  }
   1.847 +  return gfxRect(aX, aY, aWidth, aHeight);
   1.848 +}
   1.849 +
   1.850 +void
   1.851 +nsSVGUtils::SetClipRect(gfxContext *aContext,
   1.852 +                        const gfxMatrix &aCTM,
   1.853 +                        const gfxRect &aRect)
   1.854 +{
   1.855 +  if (aCTM.IsSingular())
   1.856 +    return;
   1.857 +
   1.858 +  gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
   1.859 +  aContext->Multiply(aCTM);
   1.860 +  aContext->Clip(aRect);
   1.861 +}
   1.862 +
   1.863 +gfxRect
   1.864 +nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
   1.865 +{
   1.866 +  if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
   1.867 +    aFrame = aFrame->GetParent();
   1.868 +  }
   1.869 +  gfxRect bbox;
   1.870 +  nsISVGChildFrame *svg = do_QueryFrame(aFrame);
   1.871 +  if (svg || aFrame->IsSVGText()) {
   1.872 +    // It is possible to apply a gradient, pattern, clipping path, mask or
   1.873 +    // filter to text. When one of these facilities is applied to text
   1.874 +    // the bounding box is the entire text element in all
   1.875 +    // cases.
   1.876 +    if (aFrame->IsSVGText()) {
   1.877 +      nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
   1.878 +      if (ancestor && ancestor->IsSVGText()) {
   1.879 +        while (ancestor->GetType() != nsGkAtoms::svgTextFrame) {
   1.880 +          ancestor = ancestor->GetParent();
   1.881 +        }
   1.882 +      }
   1.883 +      svg = do_QueryFrame(ancestor);
   1.884 +    }
   1.885 +    nsIContent* content = aFrame->GetContent();
   1.886 +    if (content->IsSVG() &&
   1.887 +        !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   1.888 +      return bbox;
   1.889 +    }
   1.890 +    gfxMatrix matrix;
   1.891 +    if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
   1.892 +      // The spec says getBBox "Returns the tight bounding box in *current user
   1.893 +      // space*". So we should really be doing this for all elements, but that
   1.894 +      // needs investigation to check that we won't break too much content.
   1.895 +      // NOTE: When changing this to apply to other frame types, make sure to
   1.896 +      // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
   1.897 +      NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
   1.898 +      nsSVGElement *element = static_cast<nsSVGElement*>(content);
   1.899 +      matrix = element->PrependLocalTransformsTo(matrix,
   1.900 +                          nsSVGElement::eChildToUserSpace);
   1.901 +    }
   1.902 +    return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
   1.903 +  }
   1.904 +  return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
   1.905 +}
   1.906 +
   1.907 +gfxPoint
   1.908 +nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
   1.909 +{
   1.910 +  if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
   1.911 +    // The user space for non-SVG frames is defined as the bounding box of the
   1.912 +    // frame's border-box rects over all continuations.
   1.913 +    return gfxPoint();
   1.914 +  }
   1.915 +
   1.916 +  // Leaf frames apply their own offset inside their user space.
   1.917 +  if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
   1.918 +      aFrame->IsSVGText()) {
   1.919 +    return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
   1.920 +                                         nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
   1.921 +  }
   1.922 +
   1.923 +  // For foreignObject frames, nsSVGUtils::GetBBox applies their local
   1.924 +  // transform, so we need to do the same here.
   1.925 +  if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
   1.926 +    gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
   1.927 +        PrependLocalTransformsTo(gfxMatrix(),
   1.928 +                                 nsSVGElement::eChildToUserSpace);
   1.929 +    NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
   1.930 +    return transform.GetTranslation();
   1.931 +  }
   1.932 +
   1.933 +  return gfxPoint();
   1.934 +}
   1.935 +
   1.936 +gfxRect
   1.937 +nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
   1.938 +                            const gfxRect &aBBox, nsIFrame *aFrame)
   1.939 +{
   1.940 +  float x, y, width, height;
   1.941 +  if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
   1.942 +    x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]);
   1.943 +    y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]);
   1.944 +    width = ObjectSpace(aBBox, &aXYWH[2]);
   1.945 +    height = ObjectSpace(aBBox, &aXYWH[3]);
   1.946 +  } else {
   1.947 +    x = UserSpace(aFrame, &aXYWH[0]);
   1.948 +    y = UserSpace(aFrame, &aXYWH[1]);
   1.949 +    width = UserSpace(aFrame, &aXYWH[2]);
   1.950 +    height = UserSpace(aFrame, &aXYWH[3]);
   1.951 +  }
   1.952 +  return gfxRect(x, y, width, height);
   1.953 +}
   1.954 +
   1.955 +bool
   1.956 +nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
   1.957 +{
   1.958 +  if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
   1.959 +    return false;
   1.960 +  }
   1.961 +  nsIAtom *type = aFrame->GetType();
   1.962 +  if (type != nsGkAtoms::svgImageFrame &&
   1.963 +      type != nsGkAtoms::svgPathGeometryFrame) {
   1.964 +    return false;
   1.965 +  }
   1.966 +  if (aFrame->StyleSVGReset()->HasFilters()) {
   1.967 +    return false;
   1.968 +  }
   1.969 +  // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
   1.970 +  if (type == nsGkAtoms::svgImageFrame) {
   1.971 +    return true;
   1.972 +  }
   1.973 +  const nsStyleSVG *style = aFrame->StyleSVG();
   1.974 +  if (style->HasMarker()) {
   1.975 +    return false;
   1.976 +  }
   1.977 +  if (!style->HasFill() || !HasStroke(aFrame)) {
   1.978 +    return true;
   1.979 +  }
   1.980 +  return false;
   1.981 +}
   1.982 +
   1.983 +gfxMatrix
   1.984 +nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
   1.985 +                                 nsSVGEnum *aUnits,
   1.986 +                                 nsIFrame *aFrame)
   1.987 +{
   1.988 +  if (aFrame &&
   1.989 +      aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
   1.990 +    gfxRect bbox = GetBBox(aFrame);
   1.991 +    return gfxMatrix().Scale(bbox.Width(), bbox.Height()) *
   1.992 +           gfxMatrix().Translate(gfxPoint(bbox.X(), bbox.Y())) *
   1.993 +           aMatrix;
   1.994 +  }
   1.995 +  return aMatrix;
   1.996 +}
   1.997 +
   1.998 +nsIFrame*
   1.999 +nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
  1.1000 +{
  1.1001 +  for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
  1.1002 +       ancestorFrame = ancestorFrame->GetParent()) {
  1.1003 +    if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
  1.1004 +      return ancestorFrame;
  1.1005 +    }
  1.1006 +  }
  1.1007 +  return nullptr;
  1.1008 +}
  1.1009 +
  1.1010 +gfxMatrix
  1.1011 +nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame)
  1.1012 +{
  1.1013 +  if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
  1.1014 +    aFrame = aFrame->GetParent();
  1.1015 +  }
  1.1016 +
  1.1017 +  if (aFrame->StyleSVGReset()->mVectorEffect ==
  1.1018 +      NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
  1.1019 + 
  1.1020 +    nsIContent *content = aFrame->GetContent();
  1.1021 +    NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
  1.1022 +
  1.1023 +    // a non-scaling stroke is in the screen co-ordinate
  1.1024 +    // space rather so we need to invert the transform
  1.1025 +    // to the screen co-ordinate space to get there.
  1.1026 +    // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
  1.1027 +    gfx::Matrix transform = SVGContentUtils::GetCTM(
  1.1028 +                              static_cast<nsSVGElement*>(content), true);
  1.1029 +    if (!transform.IsSingular()) {
  1.1030 +      transform.Invert();
  1.1031 +      return ThebesMatrix(transform);
  1.1032 +    }
  1.1033 +  }
  1.1034 +  return gfxMatrix();
  1.1035 +}
  1.1036 +
  1.1037 +// The logic here comes from _cairo_stroke_style_max_distance_from_path
  1.1038 +static gfxRect
  1.1039 +PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1.1040 +                              nsIFrame* aFrame,
  1.1041 +                              double aStyleExpansionFactor,
  1.1042 +                              const gfxMatrix& aMatrix)
  1.1043 +{
  1.1044 +  double style_expansion =
  1.1045 +    aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
  1.1046 +
  1.1047 +  gfxMatrix matrix = aMatrix;
  1.1048 +  matrix.Multiply(nsSVGUtils::GetStrokeTransform(aFrame));
  1.1049 +
  1.1050 +  double dx = style_expansion * (fabs(matrix.xx) + fabs(matrix.xy));
  1.1051 +  double dy = style_expansion * (fabs(matrix.yy) + fabs(matrix.yx));
  1.1052 +
  1.1053 +  gfxRect strokeExtents = aPathExtents;
  1.1054 +  strokeExtents.Inflate(dx, dy);
  1.1055 +  return strokeExtents;
  1.1056 +}
  1.1057 +
  1.1058 +/*static*/ gfxRect
  1.1059 +nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1.1060 +                                          nsTextFrame* aFrame,
  1.1061 +                                          const gfxMatrix& aMatrix)
  1.1062 +{
  1.1063 +  NS_ASSERTION(aFrame->IsSVGText(), "expected an nsTextFrame for SVG text");
  1.1064 +  return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
  1.1065 +}
  1.1066 +
  1.1067 +/*static*/ gfxRect
  1.1068 +nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1.1069 +                                          nsSVGPathGeometryFrame* aFrame,
  1.1070 +                                          const gfxMatrix& aMatrix)
  1.1071 +{
  1.1072 +  double styleExpansionFactor = 0.5;
  1.1073 +
  1.1074 +  if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
  1.1075 +    const nsStyleSVG* style = aFrame->StyleSVG();
  1.1076 +
  1.1077 +    if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
  1.1078 +      styleExpansionFactor = M_SQRT1_2;
  1.1079 +    }
  1.1080 +
  1.1081 +    if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
  1.1082 +        styleExpansionFactor < style->mStrokeMiterlimit &&
  1.1083 +        aFrame->GetContent()->Tag() != nsGkAtoms::line) {
  1.1084 +      styleExpansionFactor = style->mStrokeMiterlimit;
  1.1085 +    }
  1.1086 +  }
  1.1087 +
  1.1088 +  return ::PathExtentsToMaxStrokeExtents(aPathExtents,
  1.1089 +                                         aFrame,
  1.1090 +                                         styleExpansionFactor,
  1.1091 +                                         aMatrix);
  1.1092 +}
  1.1093 +
  1.1094 +// ----------------------------------------------------------------------
  1.1095 +
  1.1096 +/* static */ nscolor
  1.1097 +nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
  1.1098 +                                    nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
  1.1099 +{
  1.1100 +  const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
  1.1101 +  nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
  1.1102 +  bool isServer = paint.mType == eStyleSVGPaintType_Server ||
  1.1103 +                  paint.mType == eStyleSVGPaintType_ContextFill ||
  1.1104 +                  paint.mType == eStyleSVGPaintType_ContextStroke;
  1.1105 +  nscolor color = isServer ? paint.mFallbackColor : paint.mPaint.mColor;
  1.1106 +  if (styleIfVisited) {
  1.1107 +    const nsStyleSVGPaint &paintIfVisited =
  1.1108 +      styleIfVisited->StyleSVG()->*aFillOrStroke;
  1.1109 +    // To prevent Web content from detecting if a user has visited a URL
  1.1110 +    // (via URL loading triggered by paint servers or performance
  1.1111 +    // differences between paint servers or between a paint server and a
  1.1112 +    // color), we do not allow whether links are visited to change which
  1.1113 +    // paint server is used or switch between paint servers and simple
  1.1114 +    // colors.  A :visited style may only override a simple color with
  1.1115 +    // another simple color.
  1.1116 +    if (paintIfVisited.mType == eStyleSVGPaintType_Color &&
  1.1117 +        paint.mType == eStyleSVGPaintType_Color) {
  1.1118 +      nscolor colors[2] = { color, paintIfVisited.mPaint.mColor };
  1.1119 +      return nsStyleContext::CombineVisitedColors(
  1.1120 +               colors, aStyleContext->RelevantLinkVisited());
  1.1121 +    }
  1.1122 +  }
  1.1123 +  return color;
  1.1124 +}
  1.1125 +
  1.1126 +static void
  1.1127 +SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
  1.1128 +                          nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
  1.1129 +                          float aOpacity)
  1.1130 +{
  1.1131 +  nscolor color = nsSVGUtils::GetFallbackOrPaintColor(
  1.1132 +    aContext, aStyleContext, aFillOrStroke);
  1.1133 +
  1.1134 +  aContext->SetColor(gfxRGBA(NS_GET_R(color)/255.0,
  1.1135 +                             NS_GET_G(color)/255.0,
  1.1136 +                             NS_GET_B(color)/255.0,
  1.1137 +                             NS_GET_A(color)/255.0 * aOpacity));
  1.1138 +}
  1.1139 +
  1.1140 +static float
  1.1141 +MaybeOptimizeOpacity(nsIFrame *aFrame, float aFillOrStrokeOpacity)
  1.1142 +{
  1.1143 +  float opacity = aFrame->StyleDisplay()->mOpacity;
  1.1144 +  if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
  1.1145 +    return aFillOrStrokeOpacity * opacity;
  1.1146 +  }
  1.1147 +  return aFillOrStrokeOpacity;
  1.1148 +}
  1.1149 +
  1.1150 +/* static */ bool
  1.1151 +nsSVGUtils::SetupContextPaint(gfxContext *aContext,
  1.1152 +                              gfxTextContextPaint *aContextPaint,
  1.1153 +                              const nsStyleSVGPaint &aPaint,
  1.1154 +                              float aOpacity)
  1.1155 +{
  1.1156 +  nsRefPtr<gfxPattern> pattern;
  1.1157 +
  1.1158 +  if (!aContextPaint) {
  1.1159 +    return false;
  1.1160 +  }
  1.1161 +
  1.1162 +  switch (aPaint.mType) {
  1.1163 +    case eStyleSVGPaintType_ContextFill:
  1.1164 +      pattern = aContextPaint->GetFillPattern(aOpacity, aContext->CurrentMatrix());
  1.1165 +      break;
  1.1166 +    case eStyleSVGPaintType_ContextStroke:
  1.1167 +      pattern = aContextPaint->GetStrokePattern(aOpacity, aContext->CurrentMatrix());
  1.1168 +      break;
  1.1169 +    default:
  1.1170 +      return false;
  1.1171 +  }
  1.1172 +
  1.1173 +  if (!pattern) {
  1.1174 +    return false;
  1.1175 +  }
  1.1176 +
  1.1177 +  aContext->SetPattern(pattern);
  1.1178 +
  1.1179 +  return true;
  1.1180 +}
  1.1181 +
  1.1182 +bool
  1.1183 +nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext,
  1.1184 +                                gfxTextContextPaint *aContextPaint)
  1.1185 +{
  1.1186 +  const nsStyleSVG* style = aFrame->StyleSVG();
  1.1187 +  if (style->mFill.mType == eStyleSVGPaintType_None)
  1.1188 +    return false;
  1.1189 +
  1.1190 +  if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
  1.1191 +    aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
  1.1192 +  else
  1.1193 +    aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
  1.1194 +
  1.1195 +  float opacity = MaybeOptimizeOpacity(aFrame,
  1.1196 +                                       GetOpacity(style->mFillOpacitySource,
  1.1197 +                                                  style->mFillOpacity,
  1.1198 +                                                  aContextPaint));
  1.1199 +  nsSVGPaintServerFrame *ps =
  1.1200 +    nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty());
  1.1201 +  if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity))
  1.1202 +    return true;
  1.1203 +
  1.1204 +  if (SetupContextPaint(aContext, aContextPaint, style->mFill, opacity)) {
  1.1205 +    return true;
  1.1206 +  }
  1.1207 +
  1.1208 +  // On failure, use the fallback colour in case we have an
  1.1209 +  // objectBoundingBox where the width or height of the object is zero.
  1.1210 +  // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
  1.1211 +  SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
  1.1212 +                            &nsStyleSVG::mFill, opacity);
  1.1213 +
  1.1214 +  return true;
  1.1215 +}
  1.1216 +
  1.1217 +bool
  1.1218 +nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext,
  1.1219 +                                  gfxTextContextPaint *aContextPaint)
  1.1220 +{
  1.1221 +  const nsStyleSVG* style = aFrame->StyleSVG();
  1.1222 +  if (style->mStroke.mType == eStyleSVGPaintType_None)
  1.1223 +    return false;
  1.1224 +
  1.1225 +  float opacity = MaybeOptimizeOpacity(aFrame,
  1.1226 +                                       GetOpacity(style->mStrokeOpacitySource,
  1.1227 +                                                  style->mStrokeOpacity,
  1.1228 +                                                  aContextPaint));
  1.1229 +
  1.1230 +  nsSVGPaintServerFrame *ps =
  1.1231 +    nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty());
  1.1232 +  if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity))
  1.1233 +    return true;
  1.1234 +
  1.1235 +  if (SetupContextPaint(aContext, aContextPaint, style->mStroke, opacity)) {
  1.1236 +    return true;
  1.1237 +  }
  1.1238 +
  1.1239 +  // On failure, use the fallback colour in case we have an
  1.1240 +  // objectBoundingBox where the width or height of the object is zero.
  1.1241 +  // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
  1.1242 +  SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
  1.1243 +                            &nsStyleSVG::mStroke, opacity);
  1.1244 +
  1.1245 +  return true;
  1.1246 +}
  1.1247 +
  1.1248 +/* static */ float
  1.1249 +nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
  1.1250 +                       const float& aOpacity,
  1.1251 +                       gfxTextContextPaint *aOuterContextPaint)
  1.1252 +{
  1.1253 +  float opacity = 1.0f;
  1.1254 +  switch (aOpacityType) {
  1.1255 +  case eStyleSVGOpacitySource_Normal:
  1.1256 +    opacity = aOpacity;
  1.1257 +    break;
  1.1258 +  case eStyleSVGOpacitySource_ContextFillOpacity:
  1.1259 +    if (aOuterContextPaint) {
  1.1260 +      opacity = aOuterContextPaint->GetFillOpacity();
  1.1261 +    } else {
  1.1262 +      NS_WARNING("context-fill-opacity used outside of an SVG glyph");
  1.1263 +    }
  1.1264 +    break;
  1.1265 +  case eStyleSVGOpacitySource_ContextStrokeOpacity:
  1.1266 +    if (aOuterContextPaint) {
  1.1267 +      opacity = aOuterContextPaint->GetStrokeOpacity();
  1.1268 +    } else {
  1.1269 +      NS_WARNING("context-stroke-opacity used outside of an SVG glyph");
  1.1270 +    }
  1.1271 +    break;
  1.1272 +  default:
  1.1273 +    NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
  1.1274 +  }
  1.1275 +  return opacity;
  1.1276 +}
  1.1277 +
  1.1278 +bool
  1.1279 +nsSVGUtils::HasStroke(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
  1.1280 +{
  1.1281 +  const nsStyleSVG *style = aFrame->StyleSVG();
  1.1282 +  return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
  1.1283 +}
  1.1284 +
  1.1285 +float
  1.1286 +nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
  1.1287 +{
  1.1288 +  const nsStyleSVG *style = aFrame->StyleSVG();
  1.1289 +  if (aContextPaint && style->mStrokeWidthFromObject) {
  1.1290 +    return aContextPaint->GetStrokeWidth();
  1.1291 +  }
  1.1292 +
  1.1293 +  nsIContent* content = aFrame->GetContent();
  1.1294 +  if (content->IsNodeOfType(nsINode::eTEXT)) {
  1.1295 +    content = content->GetParent();
  1.1296 +  }
  1.1297 +
  1.1298 +  nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
  1.1299 +
  1.1300 +  return SVGContentUtils::CoordToFloat(aFrame->PresContext(), ctx,
  1.1301 +                                       style->mStrokeWidth);
  1.1302 +}
  1.1303 +
  1.1304 +void
  1.1305 +nsSVGUtils::SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame,
  1.1306 +                                         gfxContext *aContext,
  1.1307 +                                         gfxTextContextPaint *aContextPaint)
  1.1308 +{
  1.1309 +  float width = GetStrokeWidth(aFrame, aContextPaint);
  1.1310 +  if (width <= 0)
  1.1311 +    return;
  1.1312 +  aContext->SetLineWidth(width);
  1.1313 +
  1.1314 +  // Apply any stroke-specific transform
  1.1315 +  gfxMatrix strokeTransform = GetStrokeTransform(aFrame);
  1.1316 +  if (!strokeTransform.IsIdentity()) {
  1.1317 +    aContext->Multiply(strokeTransform);
  1.1318 +  }
  1.1319 +
  1.1320 +  const nsStyleSVG* style = aFrame->StyleSVG();
  1.1321 +  
  1.1322 +  switch (style->mStrokeLinecap) {
  1.1323 +  case NS_STYLE_STROKE_LINECAP_BUTT:
  1.1324 +    aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
  1.1325 +    break;
  1.1326 +  case NS_STYLE_STROKE_LINECAP_ROUND:
  1.1327 +    aContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
  1.1328 +    break;
  1.1329 +  case NS_STYLE_STROKE_LINECAP_SQUARE:
  1.1330 +    aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
  1.1331 +    break;
  1.1332 +  }
  1.1333 +
  1.1334 +  aContext->SetMiterLimit(style->mStrokeMiterlimit);
  1.1335 +
  1.1336 +  switch (style->mStrokeLinejoin) {
  1.1337 +  case NS_STYLE_STROKE_LINEJOIN_MITER:
  1.1338 +    aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
  1.1339 +    break;
  1.1340 +  case NS_STYLE_STROKE_LINEJOIN_ROUND:
  1.1341 +    aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND);
  1.1342 +    break;
  1.1343 +  case NS_STYLE_STROKE_LINEJOIN_BEVEL:
  1.1344 +    aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
  1.1345 +    break;
  1.1346 +  }
  1.1347 +}
  1.1348 +
  1.1349 +static bool
  1.1350 +GetStrokeDashData(nsIFrame* aFrame,
  1.1351 +                  FallibleTArray<gfxFloat>& aDashes,
  1.1352 +                  gfxFloat* aDashOffset,
  1.1353 +                  gfxTextContextPaint *aContextPaint)
  1.1354 +{
  1.1355 +  const nsStyleSVG* style = aFrame->StyleSVG();
  1.1356 +  nsPresContext *presContext = aFrame->PresContext();
  1.1357 +  nsIContent *content = aFrame->GetContent();
  1.1358 +  nsSVGElement *ctx = static_cast<nsSVGElement*>
  1.1359 +    (content->IsNodeOfType(nsINode::eTEXT) ?
  1.1360 +     content->GetParent() : content);
  1.1361 +
  1.1362 +  gfxFloat totalLength = 0.0;
  1.1363 +  if (aContextPaint && style->mStrokeDasharrayFromObject) {
  1.1364 +    aDashes = aContextPaint->GetStrokeDashArray();
  1.1365 +
  1.1366 +    for (uint32_t i = 0; i < aDashes.Length(); i++) {
  1.1367 +      if (aDashes[i] < 0.0) {
  1.1368 +        return false;
  1.1369 +      }
  1.1370 +      totalLength += aDashes[i];
  1.1371 +    }
  1.1372 +
  1.1373 +  } else {
  1.1374 +    uint32_t count = style->mStrokeDasharrayLength;
  1.1375 +    if (!count || !aDashes.SetLength(count)) {
  1.1376 +      return false;
  1.1377 +    }
  1.1378 +
  1.1379 +    gfxFloat pathScale = 1.0;
  1.1380 +
  1.1381 +    if (content->Tag() == nsGkAtoms::path) {
  1.1382 +      pathScale = static_cast<SVGPathElement*>(content)->
  1.1383 +        GetPathLengthScale(SVGPathElement::eForStroking);
  1.1384 +      if (pathScale <= 0) {
  1.1385 +        return false;
  1.1386 +      }
  1.1387 +    }
  1.1388 +
  1.1389 +    const nsStyleCoord *dasharray = style->mStrokeDasharray;
  1.1390 +
  1.1391 +    for (uint32_t i = 0; i < count; i++) {
  1.1392 +      aDashes[i] = SVGContentUtils::CoordToFloat(presContext,
  1.1393 +                                                 ctx,
  1.1394 +                                                 dasharray[i]) * pathScale;
  1.1395 +      if (aDashes[i] < 0.0) {
  1.1396 +        return false;
  1.1397 +      }
  1.1398 +      totalLength += aDashes[i];
  1.1399 +    }
  1.1400 +  }
  1.1401 +
  1.1402 +  if (aContextPaint && style->mStrokeDashoffsetFromObject) {
  1.1403 +    *aDashOffset = aContextPaint->GetStrokeDashOffset();
  1.1404 +  } else {
  1.1405 +    *aDashOffset = SVGContentUtils::CoordToFloat(presContext,
  1.1406 +                                                 ctx,
  1.1407 +                                                 style->mStrokeDashoffset);
  1.1408 +  }
  1.1409 +  
  1.1410 +  return (totalLength > 0.0);
  1.1411 +}
  1.1412 +
  1.1413 +void
  1.1414 +nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
  1.1415 +                                     gfxTextContextPaint *aContextPaint)
  1.1416 +{
  1.1417 +  SetupCairoStrokeBBoxGeometry(aFrame, aContext, aContextPaint);
  1.1418 +
  1.1419 +  AutoFallibleTArray<gfxFloat, 10> dashes;
  1.1420 +  gfxFloat dashOffset;
  1.1421 +  if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) {
  1.1422 +    aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
  1.1423 +  }
  1.1424 +}
  1.1425 +
  1.1426 +uint16_t
  1.1427 +nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
  1.1428 +{
  1.1429 +  uint16_t flags = 0;
  1.1430 +
  1.1431 +  switch(aFrame->StyleVisibility()->mPointerEvents) {
  1.1432 +  case NS_STYLE_POINTER_EVENTS_NONE:
  1.1433 +    break;
  1.1434 +  case NS_STYLE_POINTER_EVENTS_AUTO:
  1.1435 +  case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
  1.1436 +    if (aFrame->StyleVisibility()->IsVisible()) {
  1.1437 +      if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
  1.1438 +        flags |= SVG_HIT_TEST_FILL;
  1.1439 +      if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
  1.1440 +        flags |= SVG_HIT_TEST_STROKE;
  1.1441 +      if (aFrame->StyleSVG()->mStrokeOpacity > 0)
  1.1442 +        flags |= SVG_HIT_TEST_CHECK_MRECT;
  1.1443 +    }
  1.1444 +    break;
  1.1445 +  case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
  1.1446 +    if (aFrame->StyleVisibility()->IsVisible()) {
  1.1447 +      flags |= SVG_HIT_TEST_FILL;
  1.1448 +    }
  1.1449 +    break;
  1.1450 +  case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
  1.1451 +    if (aFrame->StyleVisibility()->IsVisible()) {
  1.1452 +      flags |= SVG_HIT_TEST_STROKE;
  1.1453 +    }
  1.1454 +    break;
  1.1455 +  case NS_STYLE_POINTER_EVENTS_VISIBLE:
  1.1456 +    if (aFrame->StyleVisibility()->IsVisible()) {
  1.1457 +      flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
  1.1458 +    }
  1.1459 +    break;
  1.1460 +  case NS_STYLE_POINTER_EVENTS_PAINTED:
  1.1461 +    if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
  1.1462 +      flags |= SVG_HIT_TEST_FILL;
  1.1463 +    if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
  1.1464 +      flags |= SVG_HIT_TEST_STROKE;
  1.1465 +    if (aFrame->StyleSVG()->mStrokeOpacity)
  1.1466 +      flags |= SVG_HIT_TEST_CHECK_MRECT;
  1.1467 +    break;
  1.1468 +  case NS_STYLE_POINTER_EVENTS_FILL:
  1.1469 +    flags |= SVG_HIT_TEST_FILL;
  1.1470 +    break;
  1.1471 +  case NS_STYLE_POINTER_EVENTS_STROKE:
  1.1472 +    flags |= SVG_HIT_TEST_STROKE;
  1.1473 +    break;
  1.1474 +  case NS_STYLE_POINTER_EVENTS_ALL:
  1.1475 +    flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
  1.1476 +    break;
  1.1477 +  default:
  1.1478 +    NS_ERROR("not reached");
  1.1479 +    break;
  1.1480 +  }
  1.1481 +
  1.1482 +  return flags;
  1.1483 +}
  1.1484 +
  1.1485 +bool
  1.1486 +nsSVGUtils::SetupCairoStroke(nsIFrame* aFrame, gfxContext* aContext,
  1.1487 +                             gfxTextContextPaint *aContextPaint)
  1.1488 +{
  1.1489 +  if (!HasStroke(aFrame, aContextPaint)) {
  1.1490 +    return false;
  1.1491 +  }
  1.1492 +  SetupCairoStrokeGeometry(aFrame, aContext, aContextPaint);
  1.1493 +
  1.1494 +  return SetupCairoStrokePaint(aFrame, aContext, aContextPaint);
  1.1495 +}
  1.1496 +
  1.1497 +bool
  1.1498 +nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext,
  1.1499 +                          DrawMode aDrawMode,
  1.1500 +                          gfxTextContextPaint* aContextPaint)
  1.1501 +{
  1.1502 +  nsIFrame* frame = aElement->GetPrimaryFrame();
  1.1503 +  nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
  1.1504 +  if (!svgFrame) {
  1.1505 +    return false;
  1.1506 +  }
  1.1507 +  nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
  1.1508 +  context->Init(frame->PresContext()->DeviceContext(), aContext);
  1.1509 +  context->AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint,
  1.1510 +                       nullptr);
  1.1511 +  svgFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
  1.1512 +  nsresult rv = svgFrame->PaintSVG(context, nullptr, frame);
  1.1513 +  return NS_SUCCEEDED(rv);
  1.1514 +}
  1.1515 +
  1.1516 +bool
  1.1517 +nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
  1.1518 +                               const gfxMatrix& aSVGToAppSpace,
  1.1519 +                               gfxRect* aResult)
  1.1520 +{
  1.1521 +  nsIFrame* frame = aElement->GetPrimaryFrame();
  1.1522 +  nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
  1.1523 +  if (!svgFrame) {
  1.1524 +    return false;
  1.1525 +  }
  1.1526 +
  1.1527 +  gfxMatrix transform(aSVGToAppSpace);
  1.1528 +  nsIContent* content = frame->GetContent();
  1.1529 +  if (content->IsSVG()) {
  1.1530 +    transform = static_cast<nsSVGElement*>(content)->
  1.1531 +                  PrependLocalTransformsTo(aSVGToAppSpace);
  1.1532 +  }
  1.1533 +
  1.1534 +  *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform),
  1.1535 +    nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
  1.1536 +    nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
  1.1537 +    nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect();
  1.1538 +  return true;
  1.1539 +}
  1.1540 +
  1.1541 +nsRect
  1.1542 +nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect,
  1.1543 +                           const gfxMatrix &aToCanvas,
  1.1544 +                           const nsPresContext *presContext)
  1.1545 +{
  1.1546 +  return nsLayoutUtils::RoundGfxRectToAppRect(
  1.1547 +                          aToCanvas.TransformBounds(aUserspaceRect),
  1.1548 +                          presContext->AppUnitsPerDevPixel());
  1.1549 +}

mercurial