Blame layout/svg/nsSVGUtils.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
// Main header first:
Packit f0b94e
// This is also necessary to ensure our definition of M_SQRT1_2 is picked up
Packit f0b94e
#include "nsSVGUtils.h"
Packit f0b94e
#include <algorithm>
Packit f0b94e
Packit f0b94e
// Keep others in (case-insensitive) order:
Packit f0b94e
#include "gfx2DGlue.h"
Packit f0b94e
#include "gfxContext.h"
Packit f0b94e
#include "gfxMatrix.h"
Packit f0b94e
#include "gfxPlatform.h"
Packit f0b94e
#include "gfxRect.h"
Packit f0b94e
#include "gfxUtils.h"
Packit f0b94e
#include "mozilla/gfx/2D.h"
Packit f0b94e
#include "mozilla/gfx/PatternHelpers.h"
Packit f0b94e
#include "mozilla/Preferences.h"
Packit f0b94e
#include "mozilla/SVGContextPaint.h"
Packit f0b94e
#include "nsCSSClipPathInstance.h"
Packit f0b94e
#include "nsCSSFrameConstructor.h"
Packit f0b94e
#include "nsDisplayList.h"
Packit f0b94e
#include "nsFilterInstance.h"
Packit f0b94e
#include "nsFrameList.h"
Packit f0b94e
#include "nsGkAtoms.h"
Packit f0b94e
#include "nsIContent.h"
Packit f0b94e
#include "nsIDocument.h"
Packit f0b94e
#include "nsIFrame.h"
Packit f0b94e
#include "nsIPresShell.h"
Packit f0b94e
#include "nsSVGDisplayableFrame.h"
Packit f0b94e
#include "nsLayoutUtils.h"
Packit f0b94e
#include "nsPresContext.h"
Packit f0b94e
#include "nsStyleCoord.h"
Packit f0b94e
#include "nsStyleStruct.h"
Packit f0b94e
#include "nsSVGClipPathFrame.h"
Packit f0b94e
#include "nsSVGContainerFrame.h"
Packit f0b94e
#include "SVGObserverUtils.h"
Packit f0b94e
#include "nsSVGFilterPaintCallback.h"
Packit f0b94e
#include "nsSVGForeignObjectFrame.h"
Packit f0b94e
#include "nsSVGInnerSVGFrame.h"
Packit f0b94e
#include "nsSVGIntegrationUtils.h"
Packit f0b94e
#include "nsSVGLength2.h"
Packit f0b94e
#include "nsSVGMaskFrame.h"
Packit f0b94e
#include "nsSVGOuterSVGFrame.h"
Packit f0b94e
#include "mozilla/dom/SVGClipPathElement.h"
Packit f0b94e
#include "mozilla/dom/SVGPathElement.h"
Packit f0b94e
#include "mozilla/dom/SVGUnitTypesBinding.h"
Packit f0b94e
#include "SVGGeometryElement.h"
Packit f0b94e
#include "SVGGeometryFrame.h"
Packit f0b94e
#include "nsSVGPaintServerFrame.h"
Packit f0b94e
#include "mozilla/dom/SVGViewportElement.h"
Packit f0b94e
#include "nsTextFrame.h"
Packit f0b94e
#include "SVGContentUtils.h"
Packit f0b94e
#include "SVGTextFrame.h"
Packit f0b94e
#include "mozilla/Unused.h"
Packit f0b94e
Packit f0b94e
using namespace mozilla;
Packit f0b94e
using namespace mozilla::dom;
Packit f0b94e
using namespace mozilla::dom::SVGUnitTypesBinding;
Packit f0b94e
using namespace mozilla::gfx;
Packit f0b94e
using namespace mozilla::image;
Packit f0b94e
Packit f0b94e
static bool sSVGPathCachingEnabled;
Packit f0b94e
static bool sSVGDisplayListHitTestingEnabled;
Packit f0b94e
static bool sSVGDisplayListPaintingEnabled;
Packit f0b94e
static bool sSVGNewGetBBoxEnabled;
Packit f0b94e
Packit f0b94e
bool NS_SVGPathCachingEnabled() { return sSVGPathCachingEnabled; }
Packit f0b94e
Packit f0b94e
bool NS_SVGDisplayListHitTestingEnabled() {
Packit f0b94e
  return sSVGDisplayListHitTestingEnabled;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool NS_SVGDisplayListPaintingEnabled() {
Packit f0b94e
  return sSVGDisplayListPaintingEnabled;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool NS_SVGNewGetBBoxEnabled() { return sSVGNewGetBBoxEnabled; }
Packit f0b94e
Packit f0b94e
// we only take the address of this:
Packit f0b94e
static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
Packit f0b94e
Packit f0b94e
SVGAutoRenderState::SVGAutoRenderState(
Packit f0b94e
    DrawTarget* aDrawTarget MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
Packit f0b94e
    : mDrawTarget(aDrawTarget),
Packit f0b94e
      mOriginalRenderState(nullptr),
Packit f0b94e
      mPaintingToWindow(false) {
Packit f0b94e
  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
Packit f0b94e
  mOriginalRenderState = aDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
Packit f0b94e
  // We always remove ourselves from aContext before it dies, so
Packit f0b94e
  // passing nullptr as the destroy function is okay.
Packit f0b94e
  aDrawTarget->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
SVGAutoRenderState::~SVGAutoRenderState() {
Packit f0b94e
  mDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
Packit f0b94e
  if (mOriginalRenderState) {
Packit f0b94e
    mDrawTarget->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState,
Packit f0b94e
                             nullptr);
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow) {
Packit f0b94e
  mPaintingToWindow = aPaintingToWindow;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* static */ bool SVGAutoRenderState::IsPaintingToWindow(
Packit f0b94e
    DrawTarget* aDrawTarget) {
Packit f0b94e
  void* state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
Packit f0b94e
  if (state) {
Packit f0b94e
    return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
Packit f0b94e
  }
Packit f0b94e
  return false;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::Init() {
Packit f0b94e
  Preferences::AddBoolVarCache(&sSVGPathCachingEnabled,
Packit f0b94e
                               "svg.path-caching.enabled");
Packit f0b94e
Packit f0b94e
  Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
Packit f0b94e
                               "svg.display-lists.hit-testing.enabled");
Packit f0b94e
Packit f0b94e
  Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
Packit f0b94e
                               "svg.display-lists.painting.enabled");
Packit f0b94e
Packit f0b94e
  Preferences::AddBoolVarCache(&sSVGNewGetBBoxEnabled,
Packit f0b94e
                               "svg.new-getBBox.enabled");
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsRect nsSVGUtils::GetPostFilterVisualOverflowRect(
Packit f0b94e
    nsIFrame* aFrame, const nsRect& aPreFilterRect) {
Packit f0b94e
  MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
Packit f0b94e
             "Called on invalid frame type");
Packit f0b94e
Packit f0b94e
  nsSVGFilterProperty* property = SVGObserverUtils::GetFilterProperty(aFrame);
Packit f0b94e
  if (!property || !property->ReferencesValidResources()) {
Packit f0b94e
    return aPreFilterRect;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr,
Packit f0b94e
                                               &aPreFilterRect);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame* aFrame) {
Packit f0b94e
  return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame) {
Packit f0b94e
  nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
Packit f0b94e
  do {
Packit f0b94e
    if (outer->IsCallingReflowSVG()) {
Packit f0b94e
      return true;
Packit f0b94e
    }
Packit f0b94e
    outer = GetOuterSVGFrame(outer->GetParent());
Packit f0b94e
  } while (outer);
Packit f0b94e
  return false;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::ScheduleReflowSVG(nsIFrame* aFrame) {
Packit f0b94e
  MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG), "Passed bad frame!");
Packit f0b94e
Packit f0b94e
  // If this is triggered, the callers should be fixed to call us before
Packit f0b94e
  // ReflowSVG is called. If we try to mark dirty bits on frames while we're
Packit f0b94e
  // in the process of removing them, things will get messed up.
Packit f0b94e
  NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
Packit f0b94e
               "Do not call under nsSVGDisplayableFrame::ReflowSVG!");
Packit f0b94e
Packit f0b94e
  // We don't call SVGObserverUtils::InvalidateRenderingObservers here because
Packit f0b94e
  // we should only be called under InvalidateAndScheduleReflowSVG (which
Packit f0b94e
  // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
Packit f0b94e
  // (at which point the frame has no observers).
Packit f0b94e
Packit f0b94e
  if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aFrame->GetStateBits() & (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
Packit f0b94e
    // Nothing to do if we're already dirty, or if the outer-<svg>
Packit f0b94e
    // hasn't yet had its initial reflow.
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsSVGOuterSVGFrame* outerSVGFrame = nullptr;
Packit f0b94e
Packit f0b94e
  // We must not add dirty bits to the nsSVGOuterSVGFrame or else
Packit f0b94e
  // PresShell::FrameNeedsReflow won't work when we pass it in below.
Packit f0b94e
  if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
Packit f0b94e
    outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
Packit f0b94e
  } else {
Packit f0b94e
    aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
Packit f0b94e
Packit f0b94e
    nsIFrame* f = aFrame->GetParent();
Packit f0b94e
    while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
Packit f0b94e
      if (f->GetStateBits() &
Packit f0b94e
          (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
      f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
Packit f0b94e
      f = f->GetParent();
Packit f0b94e
      MOZ_ASSERT(f->IsFrameOfType(nsIFrame::eSVG),
Packit f0b94e
                 "NS_STATE_IS_OUTER_SVG check above not valid!");
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
Packit f0b94e
Packit f0b94e
    MOZ_ASSERT(outerSVGFrame && outerSVGFrame->IsSVGOuterSVGFrame(),
Packit f0b94e
               "Did not find nsSVGOuterSVGFrame!");
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
Packit f0b94e
    // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
Packit f0b94e
    // need to call PresShell::FrameNeedsReflow, since we have an
Packit f0b94e
    // nsSVGOuterSVGFrame::DidReflow call pending.
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsFrameState dirtyBit =
Packit f0b94e
      (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY
Packit f0b94e
                               : NS_FRAME_HAS_DIRTY_CHILDREN);
Packit f0b94e
Packit f0b94e
  aFrame->PresShell()->FrameNeedsReflow(outerSVGFrame, nsIPresShell::eResize,
Packit f0b94e
                                        dirtyBit);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::NeedsReflowSVG(nsIFrame* aFrame) {
Packit f0b94e
  MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG),
Packit f0b94e
             "SVG uses bits differently!");
Packit f0b94e
Packit f0b94e
  // The flags we test here may change, hence why we have this separate
Packit f0b94e
  // function.
Packit f0b94e
  return NS_SUBTREE_DIRTY(aFrame);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame* aFrame) {
Packit f0b94e
  MOZ_ASSERT(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
Packit f0b94e
             "Not expecting to be called on the outer SVG Frame");
Packit f0b94e
Packit f0b94e
  aFrame = aFrame->GetParent();
Packit f0b94e
Packit f0b94e
  while (aFrame) {
Packit f0b94e
    if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) return;
Packit f0b94e
Packit f0b94e
    nsSVGFilterProperty* property = SVGObserverUtils::GetFilterProperty(aFrame);
Packit f0b94e
    if (property) {
Packit f0b94e
      property->Invalidate();
Packit f0b94e
    }
Packit f0b94e
    aFrame = aFrame->GetParent();
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
Size nsSVGUtils::GetContextSize(const nsIFrame* aFrame) {
Packit f0b94e
  Size size;
Packit f0b94e
Packit f0b94e
  MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "bad cast");
Packit f0b94e
  const nsSVGElement* element =
Packit f0b94e
      static_cast<nsSVGElement*>(aFrame->GetContent());
Packit f0b94e
Packit f0b94e
  SVGViewportElement* ctx = element->GetCtx();
Packit f0b94e
  if (ctx) {
Packit f0b94e
    size.width = ctx->GetLength(SVGContentUtils::X);
Packit f0b94e
    size.height = ctx->GetLength(SVGContentUtils::Y);
Packit f0b94e
  }
Packit f0b94e
  return size;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
float nsSVGUtils::ObjectSpace(const gfxRect& aRect,
Packit f0b94e
                              const nsSVGLength2* aLength) {
Packit f0b94e
  float axis;
Packit f0b94e
Packit f0b94e
  switch (aLength->GetCtxType()) {
Packit f0b94e
    case SVGContentUtils::X:
Packit f0b94e
      axis = aRect.Width();
Packit f0b94e
      break;
Packit f0b94e
    case SVGContentUtils::Y:
Packit f0b94e
      axis = aRect.Height();
Packit f0b94e
      break;
Packit f0b94e
    case SVGContentUtils::XY:
Packit f0b94e
      axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
Packit f0b94e
          aRect.Width(), aRect.Height()));
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      NS_NOTREACHED("unexpected ctx type");
Packit f0b94e
      axis = 0.0f;
Packit f0b94e
      break;
Packit f0b94e
  }
Packit f0b94e
  if (aLength->IsPercentage()) {
Packit f0b94e
    // Multiply first to avoid precision errors:
Packit f0b94e
    return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
Packit f0b94e
  }
Packit f0b94e
  return aLength->GetAnimValue(static_cast<SVGViewportElement*>(nullptr)) *
Packit f0b94e
         axis;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
float nsSVGUtils::UserSpace(nsSVGElement* aSVGElement,
Packit f0b94e
                            const nsSVGLength2* aLength) {
Packit f0b94e
  return aLength->GetAnimValue(aSVGElement);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
float nsSVGUtils::UserSpace(nsIFrame* aNonSVGContext,
Packit f0b94e
                            const nsSVGLength2* aLength) {
Packit f0b94e
  return aLength->GetAnimValue(aNonSVGContext);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
float nsSVGUtils::UserSpace(const UserSpaceMetrics& aMetrics,
Packit f0b94e
                            const nsSVGLength2* aLength) {
Packit f0b94e
  return aLength->GetAnimValue(aMetrics);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsSVGOuterSVGFrame* nsSVGUtils::GetOuterSVGFrame(nsIFrame* aFrame) {
Packit f0b94e
  while (aFrame) {
Packit f0b94e
    if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
Packit f0b94e
      return static_cast<nsSVGOuterSVGFrame*>(aFrame);
Packit f0b94e
    }
Packit f0b94e
    aFrame = aFrame->GetParent();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return nullptr;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsIFrame* nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame,
Packit f0b94e
                                                       nsRect* aRect) {
Packit f0b94e
  nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
Packit f0b94e
  if (!svg) return nullptr;
Packit f0b94e
  nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
Packit f0b94e
  if (outer == svg) {
Packit f0b94e
    return nullptr;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
Packit f0b94e
    *aRect = nsRect(0, 0, 0, 0);
Packit f0b94e
  } else {
Packit f0b94e
    uint32_t flags =
Packit f0b94e
        nsSVGUtils::eForGetClientRects | nsSVGUtils::eBBoxIncludeFill |
Packit f0b94e
        nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeMarkers;
Packit f0b94e
    gfxMatrix m = nsSVGUtils::GetUserToCanvasTM(aFrame);
Packit f0b94e
    SVGBBox bbox = nsSVGUtils::GetBBox(aFrame, flags, &m);
Packit f0b94e
    nsRect bounds = nsLayoutUtils::RoundGfxRectToAppRect(
Packit f0b94e
        bbox, aFrame->PresContext()->AppUnitsPerDevPixel());
Packit f0b94e
    nsMargin bp = outer->GetUsedBorderAndPadding();
Packit f0b94e
    *aRect = bounds + nsPoint(bp.left, bp.top);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return outer;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxMatrix nsSVGUtils::GetCanvasTM(nsIFrame* aFrame) {
Packit f0b94e
  // XXX yuck, we really need a common interface for GetCanvasTM
Packit f0b94e
Packit f0b94e
  if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
Packit f0b94e
    return GetCSSPxToDevPxMatrix(aFrame);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  LayoutFrameType type = aFrame->Type();
Packit f0b94e
  if (type == LayoutFrameType::SVGForeignObject) {
Packit f0b94e
    return static_cast<nsSVGForeignObjectFrame*>(aFrame)->GetCanvasTM();
Packit f0b94e
  }
Packit f0b94e
  if (type == LayoutFrameType::SVGOuterSVG) {
Packit f0b94e
    return GetCSSPxToDevPxMatrix(aFrame);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsSVGContainerFrame* containerFrame = do_QueryFrame(aFrame);
Packit f0b94e
  if (containerFrame) {
Packit f0b94e
    return containerFrame->GetCanvasTM();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return static_cast<SVGGeometryFrame*>(aFrame)->GetCanvasTM();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxMatrix nsSVGUtils::GetUserToCanvasTM(nsIFrame* aFrame) {
Packit f0b94e
  nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
Packit f0b94e
  NS_ASSERTION(svgFrame, "bad frame");
Packit f0b94e
Packit f0b94e
  gfxMatrix tm;
Packit f0b94e
  if (svgFrame) {
Packit f0b94e
    nsSVGElement* content = static_cast<nsSVGElement*>(aFrame->GetContent());
Packit f0b94e
    tm = content->PrependLocalTransformsTo(GetCanvasTM(aFrame->GetParent()),
Packit f0b94e
                                           eUserSpaceToParent);
Packit f0b94e
  }
Packit f0b94e
  return tm;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame* aFrame, uint32_t aFlags) {
Packit f0b94e
  for (nsIFrame* kid : aFrame->PrincipalChildList()) {
Packit f0b94e
    nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
Packit f0b94e
    if (SVGFrame) {
Packit f0b94e
      SVGFrame->NotifySVGChanged(aFlags);
Packit f0b94e
    } else {
Packit f0b94e
      NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) ||
Packit f0b94e
                       nsSVGUtils::IsInSVGTextSubtree(kid),
Packit f0b94e
                   "SVG frame expected");
Packit f0b94e
      // recurse into the children of container frames e.g. <clipPath>, <mask>
Packit f0b94e
      // in case they have child frames with transformation matrices
Packit f0b94e
      if (kid->IsFrameOfType(nsIFrame::eSVG)) {
Packit f0b94e
        NotifyChildrenOfSVGChange(kid, aFlags);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// ************************************************************
Packit f0b94e
Packit f0b94e
class SVGPaintCallback : public nsSVGFilterPaintCallback {
Packit f0b94e
 public:
Packit f0b94e
  virtual void Paint(gfxContext& aContext, nsIFrame* aTarget,
Packit f0b94e
                     const gfxMatrix& aTransform, const nsIntRect* aDirtyRect,
Packit f0b94e
                     imgDrawingParams& aImgParams) override {
Packit f0b94e
    nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aTarget);
Packit f0b94e
    NS_ASSERTION(svgFrame, "Expected SVG frame here");
Packit f0b94e
Packit f0b94e
    nsIntRect* dirtyRect = nullptr;
Packit f0b94e
    nsIntRect tmpDirtyRect;
Packit f0b94e
Packit f0b94e
    // aDirtyRect is in user-space pixels, we need to convert to
Packit f0b94e
    // outer-SVG-frame-relative device pixels.
Packit f0b94e
    if (aDirtyRect) {
Packit f0b94e
      gfxMatrix userToDeviceSpace = aTransform;
Packit f0b94e
      if (userToDeviceSpace.IsSingular()) {
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
      gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(gfxRect(
Packit f0b94e
          aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
Packit f0b94e
      dirtyBounds.RoundOut();
Packit f0b94e
      if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
Packit f0b94e
        dirtyRect = &tmpDirtyRect;
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    svgFrame->PaintSVG(aContext, nsSVGUtils::GetCSSPxToDevPxMatrix(aTarget),
Packit f0b94e
                       aImgParams, dirtyRect);
Packit f0b94e
  }
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
float nsSVGUtils::ComputeOpacity(nsIFrame* aFrame, bool aHandleOpacity) {
Packit f0b94e
  float opacity = aFrame->StyleEffects()->mOpacity;
Packit f0b94e
Packit f0b94e
  if (opacity != 1.0f &&
Packit f0b94e
      (nsSVGUtils::CanOptimizeOpacity(aFrame) || !aHandleOpacity)) {
Packit f0b94e
    return 1.0f;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return opacity;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,
Packit f0b94e
                                    MaskUsage& aUsage) {
Packit f0b94e
  aUsage.opacity = ComputeOpacity(aFrame, aHandleOpacity);
Packit f0b94e
Packit f0b94e
  nsIFrame* firstFrame =
Packit f0b94e
      nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
Packit f0b94e
Packit f0b94e
  SVGObserverUtils::EffectProperties effectProperties =
Packit f0b94e
      SVGObserverUtils::GetEffectProperties(firstFrame);
Packit f0b94e
  const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
Packit f0b94e
Packit f0b94e
  nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
Packit f0b94e
Packit f0b94e
  aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0);
Packit f0b94e
Packit f0b94e
  nsSVGClipPathFrame* clipPathFrame = effectProperties.GetClipPathFrame();
Packit f0b94e
  MOZ_ASSERT(!clipPathFrame ||
Packit f0b94e
             svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
Packit f0b94e
Packit f0b94e
  switch (svgReset->mClipPath.GetType()) {
Packit f0b94e
    case StyleShapeSourceType::URL:
Packit f0b94e
      if (clipPathFrame) {
Packit f0b94e
        if (clipPathFrame->IsTrivial()) {
Packit f0b94e
          aUsage.shouldApplyClipPath = true;
Packit f0b94e
        } else {
Packit f0b94e
          aUsage.shouldGenerateClipMaskLayer = true;
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    case StyleShapeSourceType::Shape:
Packit f0b94e
    case StyleShapeSourceType::Box:
Packit f0b94e
      aUsage.shouldApplyBasicShape = true;
Packit f0b94e
      break;
Packit f0b94e
    case StyleShapeSourceType::None:
Packit f0b94e
      MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer &&
Packit f0b94e
                 !aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape);
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
Packit f0b94e
      break;
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
class MixModeBlender {
Packit f0b94e
 public:
Packit f0b94e
  typedef mozilla::gfx::Factory Factory;
Packit f0b94e
Packit f0b94e
  MixModeBlender(nsIFrame* aFrame, gfxContext* aContext)
Packit f0b94e
      : mFrame(aFrame), mSourceCtx(aContext) {
Packit f0b94e
    MOZ_ASSERT(mFrame && mSourceCtx);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  bool ShouldCreateDrawTargetForBlend() const {
Packit f0b94e
    return mFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  gfxContext* CreateBlendTarget(const gfxMatrix& aTransform) {
Packit f0b94e
    MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
Packit f0b94e
Packit f0b94e
    // Create a temporary context to draw to so we can blend it back with
Packit f0b94e
    // another operator.
Packit f0b94e
    IntRect drawRect = ComputeClipExtsInDeviceSpace(aTransform);
Packit f0b94e
Packit f0b94e
    RefPtr<DrawTarget> targetDT =
Packit f0b94e
        mSourceCtx->GetDrawTarget()->CreateSimilarDrawTarget(
Packit f0b94e
            drawRect.Size(), SurfaceFormat::B8G8R8A8);
Packit f0b94e
    if (!targetDT || !targetDT->IsValid()) {
Packit f0b94e
      return nullptr;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    MOZ_ASSERT(!mTargetCtx,
Packit f0b94e
               "CreateBlendTarget is designed to be used once only.");
Packit f0b94e
Packit f0b94e
    mTargetCtx = gfxContext::CreateOrNull(targetDT);
Packit f0b94e
    MOZ_ASSERT(mTargetCtx);  // already checked the draw target above
Packit f0b94e
    mTargetCtx->SetMatrix(mSourceCtx->CurrentMatrix() *
Packit f0b94e
                          Matrix::Translation(-drawRect.TopLeft()));
Packit f0b94e
Packit f0b94e
    mTargetOffset = drawRect.TopLeft();
Packit f0b94e
Packit f0b94e
    return mTargetCtx;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  void BlendToTarget() {
Packit f0b94e
    MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
Packit f0b94e
    MOZ_ASSERT(mTargetCtx,
Packit f0b94e
               "BlendToTarget should be used after CreateBlendTarget.");
Packit f0b94e
Packit f0b94e
    RefPtr<SourceSurface> targetSurf = mTargetCtx->GetDrawTarget()->Snapshot();
Packit f0b94e
Packit f0b94e
    gfxContextAutoSaveRestore save(mSourceCtx);
Packit f0b94e
    mSourceCtx->SetMatrix(Matrix());  // This will be restored right after.
Packit f0b94e
    RefPtr<gfxPattern> pattern = new gfxPattern(
Packit f0b94e
        targetSurf, Matrix::Translation(mTargetOffset.x, mTargetOffset.y));
Packit f0b94e
    mSourceCtx->SetPattern(pattern);
Packit f0b94e
    mSourceCtx->Paint();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  MixModeBlender() = delete;
Packit f0b94e
Packit f0b94e
  IntRect ComputeClipExtsInDeviceSpace(const gfxMatrix& aTransform) {
Packit f0b94e
    // These are used if we require a temporary surface for a custom blend
Packit f0b94e
    // mode. Clip the source context first, so that we can generate a smaller
Packit f0b94e
    // temporary surface. (Since we will clip this context in
Packit f0b94e
    // SetupContextMatrix, a pair of save/restore is needed.)
Packit f0b94e
    gfxContextAutoSaveRestore saver(mSourceCtx);
Packit f0b94e
Packit f0b94e
    if (!(mFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
Packit f0b94e
      // aFrame has a valid visual overflow rect, so clip to it before calling
Packit f0b94e
      // PushGroup() to minimize the size of the surfaces we'll composite:
Packit f0b94e
      gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(mSourceCtx);
Packit f0b94e
      mSourceCtx->Multiply(aTransform);
Packit f0b94e
      nsRect overflowRect = mFrame->GetVisualOverflowRectRelativeToSelf();
Packit f0b94e
      if (mFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
Packit f0b94e
          nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
Packit f0b94e
        // Unlike containers, leaf frames do not include GetPosition() in
Packit f0b94e
        // GetCanvasTM().
Packit f0b94e
        overflowRect = overflowRect + mFrame->GetPosition();
Packit f0b94e
      }
Packit f0b94e
      mSourceCtx->Clip(NSRectToSnappedRect(
Packit f0b94e
          overflowRect, mFrame->PresContext()->AppUnitsPerDevPixel(),
Packit f0b94e
          *mSourceCtx->GetDrawTarget()));
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // Get the clip extents in device space.
Packit f0b94e
    gfxRect clippedFrameSurfaceRect =
Packit f0b94e
        mSourceCtx->GetClipExtents(gfxContext::eDeviceSpace);
Packit f0b94e
    clippedFrameSurfaceRect.RoundOut();
Packit f0b94e
Packit f0b94e
    IntRect result;
Packit f0b94e
    ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
Packit f0b94e
Packit f0b94e
    return Factory::CheckSurfaceSize(result.Size()) ? result : IntRect();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsIFrame* mFrame;
Packit f0b94e
  gfxContext* mSourceCtx;
Packit f0b94e
  RefPtr<gfxContext> mTargetCtx;
Packit f0b94e
  IntPoint mTargetOffset;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
void nsSVGUtils::PaintFrameWithEffects(nsIFrame* aFrame, gfxContext& aContext,
Packit f0b94e
                                       const gfxMatrix& aTransform,
Packit f0b94e
                                       imgDrawingParams& aImgParams,
Packit f0b94e
                                       const nsIntRect* aDirtyRect) {
Packit f0b94e
  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
Packit f0b94e
                   (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
Packit f0b94e
                   aFrame->PresContext()->Document()->IsSVGGlyphsDocument(),
Packit f0b94e
               "If display lists are enabled, only painting of non-display "
Packit f0b94e
               "SVG should take this code path");
Packit f0b94e
Packit f0b94e
  nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
Packit f0b94e
  if (!svgFrame) return;
Packit f0b94e
Packit f0b94e
  MaskUsage maskUsage;
Packit f0b94e
  DetermineMaskUsage(aFrame, true, maskUsage);
Packit f0b94e
  if (maskUsage.opacity == 0.0f) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const nsIContent* content = aFrame->GetContent();
Packit f0b94e
  if (content->IsSVGElement() &&
Packit f0b94e
      !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aDirtyRect && !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
Packit f0b94e
    // Here we convert aFrame's paint bounds to outer-<svg> device space,
Packit f0b94e
    // compare it to aDirtyRect, and return early if they don't intersect.
Packit f0b94e
    // We don't do this optimization for nondisplay SVG since nondisplay
Packit f0b94e
    // SVG doesn't maintain bounds/overflow rects.
Packit f0b94e
    nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
Packit f0b94e
    if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
Packit f0b94e
        nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
Packit f0b94e
      // Unlike containers, leaf frames do not include GetPosition() in
Packit f0b94e
      // GetCanvasTM().
Packit f0b94e
      overflowRect = overflowRect + aFrame->GetPosition();
Packit f0b94e
    }
Packit f0b94e
    int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
    gfxMatrix tm = aTransform;
Packit f0b94e
    if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
Packit f0b94e
      gfx::Matrix childrenOnlyTM;
Packit f0b94e
      if (static_cast<nsSVGContainerFrame*>(aFrame)->HasChildrenOnlyTransform(
Packit f0b94e
              &childrenOnlyTM)) {
Packit f0b94e
        // Undo the children-only transform:
Packit f0b94e
        if (!childrenOnlyTM.Invert()) {
Packit f0b94e
          return;
Packit f0b94e
        }
Packit f0b94e
        tm = ThebesMatrix(childrenOnlyTM) * tm;
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
    nsIntRect bounds =
Packit f0b94e
        TransformFrameRectToOuterSVG(overflowRect, tm, aFrame->PresContext())
Packit f0b94e
            .ToOutsidePixels(appUnitsPerDevPx);
Packit f0b94e
    if (!aDirtyRect->Intersects(bounds)) {
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  /* SVG defines the following rendering model:
Packit f0b94e
   *
Packit f0b94e
   *  1. Render fill
Packit f0b94e
   *  2. Render stroke
Packit f0b94e
   *  3. Render markers
Packit f0b94e
   *  4. Apply filter
Packit f0b94e
   *  5. Apply clipping, masking, group opacity
Packit f0b94e
   *
Packit f0b94e
   * We follow this, but perform a couple of optimizations:
Packit f0b94e
   *
Packit f0b94e
   * + Use cairo's clipPath when representable natively (single object
Packit f0b94e
   *   clip region).
Packit f0b94e
   *f
Packit f0b94e
   * + Merge opacity and masking if both used together.
Packit f0b94e
   */
Packit f0b94e
Packit f0b94e
  /* Properties are added lazily and may have been removed by a restyle,
Packit f0b94e
     so make sure all applicable ones are set again. */
Packit f0b94e
  SVGObserverUtils::EffectProperties effectProperties =
Packit f0b94e
      SVGObserverUtils::GetEffectProperties(aFrame);
Packit f0b94e
  if (effectProperties.HasInvalidEffects()) {
Packit f0b94e
    // Some resource is invalid. We shouldn't paint anything.
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsSVGClipPathFrame* clipPathFrame = effectProperties.GetClipPathFrame();
Packit f0b94e
  nsTArray<nsSVGMaskFrame*> masks = effectProperties.GetMaskFrames();
Packit f0b94e
  nsSVGMaskFrame* maskFrame = masks.IsEmpty() ? nullptr : masks[0];
Packit f0b94e
Packit f0b94e
  MixModeBlender blender(aFrame, &aContext);
Packit f0b94e
  gfxContext* target = blender.ShouldCreateDrawTargetForBlend()
Packit f0b94e
                           ? blender.CreateBlendTarget(aTransform)
Packit f0b94e
                           : &aContext;
Packit f0b94e
Packit f0b94e
  if (!target) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  /* Check if we need to do additional operations on this child's
Packit f0b94e
   * rendering, which necessitates rendering into another surface. */
Packit f0b94e
  bool shouldGenerateMask =
Packit f0b94e
      (maskUsage.opacity != 1.0f || maskUsage.shouldGenerateClipMaskLayer ||
Packit f0b94e
       maskUsage.shouldGenerateMaskLayer);
Packit f0b94e
  bool shouldPushMask = false;
Packit f0b94e
Packit f0b94e
  if (shouldGenerateMask) {
Packit f0b94e
    Matrix maskTransform;
Packit f0b94e
    RefPtr<SourceSurface> maskSurface;
Packit f0b94e
Packit f0b94e
    // maskFrame can be nullptr even if maskUsage.shouldGenerateMaskLayer is
Packit f0b94e
    // true. That happens when a user gives an unresolvable mask-id, such as
Packit f0b94e
    //   mask:url()
Packit f0b94e
    //   mask:url(#id-which-does-not-exist)
Packit f0b94e
    // Since we only uses nsSVGUtils with SVG elements, not like mask on an
Packit f0b94e
    // HTML element, we should treat an unresolvable mask as no-mask here.
Packit f0b94e
    if (maskUsage.shouldGenerateMaskLayer && maskFrame) {
Packit f0b94e
      uint8_t maskMode = aFrame->StyleSVGReset()->mMask.mLayers[0].mMaskMode;
Packit f0b94e
      nsSVGMaskFrame::MaskParams params(&aContext, aFrame, aTransform,
Packit f0b94e
                                        maskUsage.opacity, &maskTransform,
Packit f0b94e
                                        maskMode, aImgParams);
Packit f0b94e
      maskSurface = maskFrame->GetMaskForMaskedFrame(params);
Packit f0b94e
Packit f0b94e
      if (!maskSurface) {
Packit f0b94e
        // Either entire surface is clipped out, or gfx buffer allocation
Packit f0b94e
        // failure in nsSVGMaskFrame::GetMaskForMaskedFrame.
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
      shouldPushMask = true;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (maskUsage.shouldGenerateClipMaskLayer) {
Packit f0b94e
      Matrix clippedMaskTransform;
Packit f0b94e
      RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(
Packit f0b94e
          aContext, aFrame, aTransform, &clippedMaskTransform, maskSurface,
Packit f0b94e
          maskTransform);
Packit f0b94e
      if (clipMaskSurface) {
Packit f0b94e
        maskSurface = clipMaskSurface;
Packit f0b94e
        maskTransform = clippedMaskTransform;
Packit f0b94e
      } else {
Packit f0b94e
        // Either entire surface is clipped out, or gfx buffer allocation
Packit f0b94e
        // failure in nsSVGClipPathFrame::GetClipMask.
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
      shouldPushMask = true;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (!maskUsage.shouldGenerateClipMaskLayer &&
Packit f0b94e
        !maskUsage.shouldGenerateMaskLayer) {
Packit f0b94e
      shouldPushMask = true;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    // SVG mask multiply opacity into maskSurface already, so we do not bother
Packit f0b94e
    // to apply opacity again.
Packit f0b94e
    if (shouldPushMask) {
Packit f0b94e
      target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
Packit f0b94e
                                    maskFrame ? 1.0 : maskUsage.opacity,
Packit f0b94e
                                    maskSurface, maskTransform);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  /* If this frame has only a trivial clipPath, set up cairo's clipping now so
Packit f0b94e
   * we can just do normal painting and get it clipped appropriately.
Packit f0b94e
   */
Packit f0b94e
  if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
Packit f0b94e
    if (maskUsage.shouldApplyClipPath) {
Packit f0b94e
      clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
Packit f0b94e
    } else {
Packit f0b94e
      nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  /* Paint the child */
Packit f0b94e
  if (effectProperties.HasValidFilter()) {
Packit f0b94e
    nsRegion* dirtyRegion = nullptr;
Packit f0b94e
    nsRegion tmpDirtyRegion;
Packit f0b94e
    if (aDirtyRect) {
Packit f0b94e
      // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
Packit f0b94e
      // it in frame space.
Packit f0b94e
      gfxMatrix userToDeviceSpace = aTransform;
Packit f0b94e
      if (userToDeviceSpace.IsSingular()) {
Packit f0b94e
        return;
Packit f0b94e
      }
Packit f0b94e
      gfxMatrix deviceToUserSpace = userToDeviceSpace;
Packit f0b94e
      deviceToUserSpace.Invert();
Packit f0b94e
      gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(gfxRect(
Packit f0b94e
          aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
Packit f0b94e
      tmpDirtyRegion =
Packit f0b94e
          nsLayoutUtils::RoundGfxRectToAppRect(
Packit f0b94e
              dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
Packit f0b94e
          aFrame->GetPosition();
Packit f0b94e
      dirtyRegion = &tmpDirtyRegion;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    gfxContextMatrixAutoSaveRestore autoSR(target);
Packit f0b94e
Packit f0b94e
    // 'target' is currently scaled such that its user space units are CSS
Packit f0b94e
    // pixels (SVG user space units). But PaintFilteredFrame expects it to be
Packit f0b94e
    // scaled in such a way that its user space units are device pixels. So we
Packit f0b94e
    // have to adjust the scale.
Packit f0b94e
    gfxMatrix reverseScaleMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aFrame);
Packit f0b94e
    DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
Packit f0b94e
    target->SetMatrixDouble(reverseScaleMatrix * aTransform *
Packit f0b94e
                            target->CurrentMatrixDouble());
Packit f0b94e
Packit f0b94e
    SVGPaintCallback paintCallback;
Packit f0b94e
    nsFilterInstance::PaintFilteredFrame(aFrame, target, &paintCallback,
Packit f0b94e
                                         dirtyRegion, aImgParams);
Packit f0b94e
  } else {
Packit f0b94e
    svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
Packit f0b94e
    aContext.PopClip();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (shouldPushMask) {
Packit f0b94e
    target->PopGroupAndBlend();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (blender.ShouldCreateDrawTargetForBlend()) {
Packit f0b94e
    MOZ_ASSERT(target != &aContext);
Packit f0b94e
    blender.BlendToTarget();
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::HitTestClip(nsIFrame* aFrame, const gfxPoint& aPoint) {
Packit f0b94e
  SVGObserverUtils::EffectProperties props =
Packit f0b94e
      SVGObserverUtils::GetEffectProperties(aFrame);
Packit f0b94e
  if (!props.mClipPath) {
Packit f0b94e
    const nsStyleSVGReset* style = aFrame->StyleSVGReset();
Packit f0b94e
    if (style->HasClipPath()) {
Packit f0b94e
      return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint);
Packit f0b94e
    }
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (props.HasInvalidClipPath()) {
Packit f0b94e
    // clipPath is not a valid resource, so nothing gets painted, so
Packit f0b94e
    // hit-testing must fail.
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
  nsSVGClipPathFrame* clipPathFrame = props.GetClipPathFrame();
Packit f0b94e
Packit f0b94e
  if (!clipPathFrame) {
Packit f0b94e
    // clipPath doesn't exist, ignore it.
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return clipPathFrame->PointIsInsideClipPath(aFrame, aPoint);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsIFrame* nsSVGUtils::HitTestChildren(nsSVGDisplayContainerFrame* aFrame,
Packit f0b94e
                                      const gfxPoint& aPoint) {
Packit f0b94e
  // First we transform aPoint into the coordinate space established by aFrame
Packit f0b94e
  // for its children (e.g. take account of any 'viewBox' attribute):
Packit f0b94e
  gfxPoint point = aPoint;
Packit f0b94e
  if (aFrame->GetContent()->IsSVGElement()) {  // must check before cast
Packit f0b94e
    gfxMatrix m =
Packit f0b94e
        static_cast<const nsSVGElement*>(aFrame->GetContent())
Packit f0b94e
            ->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
Packit f0b94e
    if (!m.IsIdentity()) {
Packit f0b94e
      if (!m.Invert()) {
Packit f0b94e
        return nullptr;
Packit f0b94e
      }
Packit f0b94e
      point = m.TransformPoint(point);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Traverse the list in reverse order, so that if we get a hit we know that's
Packit f0b94e
  // the topmost frame that intersects the point; then we can just return it.
Packit f0b94e
  nsIFrame* result = nullptr;
Packit f0b94e
  for (nsIFrame* current = aFrame->PrincipalChildList().LastChild(); current;
Packit f0b94e
       current = current->GetPrevSibling()) {
Packit f0b94e
    nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(current);
Packit f0b94e
    if (SVGFrame) {
Packit f0b94e
      const nsIContent* content = current->GetContent();
Packit f0b94e
      if (content->IsSVGElement() &&
Packit f0b94e
          !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
Packit f0b94e
        continue;
Packit f0b94e
      }
Packit f0b94e
      // GetFrameForPoint() expects a point in its frame's SVG user space, so
Packit f0b94e
      // we need to convert to that space:
Packit f0b94e
      gfxPoint p = point;
Packit f0b94e
      if (content->IsSVGElement()) {  // must check before cast
Packit f0b94e
        gfxMatrix m =
Packit f0b94e
            static_cast<const nsSVGElement*>(content)->PrependLocalTransformsTo(
Packit f0b94e
                gfxMatrix(), eUserSpaceToParent);
Packit f0b94e
        if (!m.IsIdentity()) {
Packit f0b94e
          if (!m.Invert()) {
Packit f0b94e
            continue;
Packit f0b94e
          }
Packit f0b94e
          p = m.TransformPoint(p);
Packit f0b94e
        }
Packit f0b94e
      }
Packit f0b94e
      result = SVGFrame->GetFrameForPoint(p);
Packit f0b94e
      if (result) break;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (result && !HitTestClip(aFrame, aPoint)) result = nullptr;
Packit f0b94e
Packit f0b94e
  return result;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsRect nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
Packit f0b94e
                                                const gfxMatrix& aMatrix,
Packit f0b94e
                                                nsPresContext* aPresContext) {
Packit f0b94e
  gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
Packit f0b94e
  r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
Packit f0b94e
  return nsLayoutUtils::RoundGfxRectToAppRect(
Packit f0b94e
      aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
IntSize nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
Packit f0b94e
                                         bool* aResultOverflows) {
Packit f0b94e
  IntSize surfaceSize(ClampToInt(ceil(aSize.width)),
Packit f0b94e
                      ClampToInt(ceil(aSize.height)));
Packit f0b94e
Packit f0b94e
  *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
Packit f0b94e
                      surfaceSize.height != ceil(aSize.height);
Packit f0b94e
Packit f0b94e
  if (!Factory::AllowedSurfaceSize(surfaceSize)) {
Packit f0b94e
    surfaceSize.width =
Packit f0b94e
        std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION, surfaceSize.width);
Packit f0b94e
    surfaceSize.height =
Packit f0b94e
        std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION, surfaceSize.height);
Packit f0b94e
    *aResultOverflows = true;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return surfaceSize;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::HitTestRect(const gfx::Matrix& aMatrix, float aRX, float aRY,
Packit f0b94e
                             float aRWidth, float aRHeight, float aX,
Packit f0b94e
                             float aY) {
Packit f0b94e
  gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
Packit f0b94e
  if (rect.IsEmpty() || aMatrix.IsSingular()) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
  gfx::Matrix toRectSpace = aMatrix;
Packit f0b94e
  toRectSpace.Invert();
Packit f0b94e
  gfx::Point p = toRectSpace.TransformPoint(gfx::Point(aX, aY));
Packit f0b94e
  return rect.x <= p.x && p.x <= rect.XMost() && rect.y <= p.y &&
Packit f0b94e
         p.y <= rect.YMost();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxRect nsSVGUtils::GetClipRectForFrame(nsIFrame* aFrame, float aX, float aY,
Packit f0b94e
                                        float aWidth, float aHeight) {
Packit f0b94e
  const nsStyleDisplay* disp = aFrame->StyleDisplay();
Packit f0b94e
  const nsStyleEffects* effects = aFrame->StyleEffects();
Packit f0b94e
Packit f0b94e
  if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
Packit f0b94e
    NS_ASSERTION(effects->mClipFlags == NS_STYLE_CLIP_AUTO,
Packit f0b94e
                 "We don't know about this type of clip.");
Packit f0b94e
    return gfxRect(aX, aY, aWidth, aHeight);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
Packit f0b94e
      disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
Packit f0b94e
    nsIntRect clipPxRect = effects->mClip.ToOutsidePixels(
Packit f0b94e
        aFrame->PresContext()->AppUnitsPerDevPixel());
Packit f0b94e
    gfxRect clipRect = gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width,
Packit f0b94e
                               clipPxRect.height);
Packit f0b94e
Packit f0b94e
    if (NS_STYLE_CLIP_RIGHT_AUTO & effects->mClipFlags) {
Packit f0b94e
      clipRect.width = aWidth - clipRect.X();
Packit f0b94e
    }
Packit f0b94e
    if (NS_STYLE_CLIP_BOTTOM_AUTO & effects->mClipFlags) {
Packit f0b94e
      clipRect.height = aHeight - clipRect.Y();
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
Packit f0b94e
      clipRect.x = aX;
Packit f0b94e
      clipRect.width = aWidth;
Packit f0b94e
    }
Packit f0b94e
    if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
Packit f0b94e
      clipRect.y = aY;
Packit f0b94e
      clipRect.height = aHeight;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    return clipRect;
Packit f0b94e
  }
Packit f0b94e
  return gfxRect(aX, aY, aWidth, aHeight);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::SetClipRect(gfxContext* aContext, const gfxMatrix& aCTM,
Packit f0b94e
                             const gfxRect& aRect) {
Packit f0b94e
  if (aCTM.IsSingular()) return;
Packit f0b94e
Packit f0b94e
  gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
Packit f0b94e
  aContext->Multiply(aCTM);
Packit f0b94e
  aContext->Clip(aRect);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxRect nsSVGUtils::GetBBox(nsIFrame* aFrame, uint32_t aFlags,
Packit f0b94e
                            const gfxMatrix* aToBoundsSpace) {
Packit f0b94e
  if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
Packit f0b94e
    aFrame = aFrame->GetParent();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
Packit f0b94e
    // It is possible to apply a gradient, pattern, clipping path, mask or
Packit f0b94e
    // filter to text. When one of these facilities is applied to text
Packit f0b94e
    // the bounding box is the entire text element in all
Packit f0b94e
    // cases.
Packit f0b94e
    nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
Packit f0b94e
    if (ancestor && nsSVGUtils::IsInSVGTextSubtree(ancestor)) {
Packit f0b94e
      while (!ancestor->IsSVGTextFrame()) {
Packit f0b94e
        ancestor = ancestor->GetParent();
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
    aFrame = ancestor;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
Packit f0b94e
  const bool hasSVGLayout = aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT;
Packit f0b94e
  if (hasSVGLayout && !svg) {
Packit f0b94e
    // An SVG frame, but not one that can be displayed directly (for
Packit f0b94e
    // example, nsGradientFrame). These can't contribute to the bbox.
Packit f0b94e
    return gfxRect();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const bool isOuterSVG = svg && !hasSVGLayout;
Packit f0b94e
  MOZ_ASSERT(!isOuterSVG || aFrame->IsSVGOuterSVGFrame());
Packit f0b94e
  if (!svg || (isOuterSVG && (aFlags & eUseFrameBoundsForOuterSVG))) {
Packit f0b94e
    // An HTML element or an SVG outer frame.
Packit f0b94e
    MOZ_ASSERT(!hasSVGLayout);
Packit f0b94e
    bool onlyCurrentFrame = aFlags & eIncludeOnlyCurrentFrameForNonSVGElement;
Packit f0b94e
    return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(
Packit f0b94e
        aFrame,
Packit f0b94e
        /* aUnionContinuations = */ !onlyCurrentFrame);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  MOZ_ASSERT(svg);
Packit f0b94e
Packit f0b94e
  nsIContent* content = aFrame->GetContent();
Packit f0b94e
  if (content->IsSVGElement() &&
Packit f0b94e
      !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
Packit f0b94e
    return gfxRect();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Clean out flags which have no effects on returning bbox from now, so that
Packit f0b94e
  // we can cache and reuse ObjectBoundingBoxProperty() in the code below.
Packit f0b94e
  aFlags &= ~eIncludeOnlyCurrentFrameForNonSVGElement;
Packit f0b94e
  aFlags &= ~eUseFrameBoundsForOuterSVG;
Packit f0b94e
  if (!aFrame->IsSVGUseFrame()) {
Packit f0b94e
    aFlags &= ~eUseUserSpaceOfUseElement;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aFlags == eBBoxIncludeFillGeometry &&
Packit f0b94e
      // We only cache bbox in element's own user space
Packit f0b94e
      !aToBoundsSpace) {
Packit f0b94e
    gfxRect* prop = aFrame->GetProperty(ObjectBoundingBoxProperty());
Packit f0b94e
    if (prop) {
Packit f0b94e
      return *prop;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  gfxMatrix matrix;
Packit f0b94e
  if (aToBoundsSpace) {
Packit f0b94e
    matrix = *aToBoundsSpace;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aFrame->IsSVGForeignObjectFrame() ||
Packit f0b94e
      aFlags & nsSVGUtils::eUseUserSpaceOfUseElement) {
Packit f0b94e
    // The spec says getBBox "Returns the tight bounding box in *current user
Packit f0b94e
    // space*". So we should really be doing this for all elements, but that
Packit f0b94e
    // needs investigation to check that we won't break too much content.
Packit f0b94e
    // NOTE: When changing this to apply to other frame types, make sure to
Packit f0b94e
    // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
Packit f0b94e
    MOZ_ASSERT(content->IsSVGElement(), "bad cast");
Packit f0b94e
    nsSVGElement* element = static_cast<nsSVGElement*>(content);
Packit f0b94e
    matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace);
Packit f0b94e
  }
Packit f0b94e
  gfxRect bbox =
Packit f0b94e
      svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
Packit f0b94e
  // Account for 'clipped'.
Packit f0b94e
  if (aFlags & nsSVGUtils::eBBoxIncludeClipped) {
Packit f0b94e
    gfxRect clipRect(0, 0, 0, 0);
Packit f0b94e
    float x, y, width, height;
Packit f0b94e
    gfxMatrix tm;
Packit f0b94e
    gfxRect fillBBox =
Packit f0b94e
        svg->GetBBoxContribution(ToMatrix(tm), nsSVGUtils::eBBoxIncludeFill)
Packit f0b94e
            .ToThebesRect();
Packit f0b94e
    x = fillBBox.x;
Packit f0b94e
    y = fillBBox.y;
Packit f0b94e
    width = fillBBox.width;
Packit f0b94e
    height = fillBBox.height;
Packit f0b94e
    bool hasClip = aFrame->StyleDisplay()->IsScrollableOverflow();
Packit f0b94e
    if (hasClip) {
Packit f0b94e
      clipRect = nsSVGUtils::GetClipRectForFrame(aFrame, x, y, width, height);
Packit f0b94e
      if (aFrame->IsSVGForeignObjectFrame() || aFrame->IsSVGUseFrame()) {
Packit f0b94e
        clipRect = matrix.TransformBounds(clipRect);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
    SVGObserverUtils::EffectProperties effectProperties =
Packit f0b94e
        SVGObserverUtils::GetEffectProperties(aFrame);
Packit f0b94e
    if (effectProperties.HasInvalidClipPath()) {
Packit f0b94e
      bbox = gfxRect(0, 0, 0, 0);
Packit f0b94e
    } else {
Packit f0b94e
      nsSVGClipPathFrame* clipPathFrame = effectProperties.GetClipPathFrame();
Packit f0b94e
      if (clipPathFrame) {
Packit f0b94e
        SVGClipPathElement* clipContent =
Packit f0b94e
            static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
Packit f0b94e
        if (clipContent->IsUnitsObjectBoundingBox()) {
Packit f0b94e
          matrix.PreTranslate(gfxPoint(x, y));
Packit f0b94e
          matrix.PreScale(width, height);
Packit f0b94e
        } else if (aFrame->IsSVGForeignObjectFrame()) {
Packit f0b94e
          matrix = gfxMatrix();
Packit f0b94e
        }
Packit f0b94e
        matrix =
Packit f0b94e
            clipContent->PrependLocalTransformsTo(matrix, eUserSpaceToParent);
Packit f0b94e
        bbox =
Packit f0b94e
            clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix).ToThebesRect();
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      if (hasClip) {
Packit f0b94e
        bbox = bbox.Intersect(clipRect);
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      if (bbox.IsEmpty()) {
Packit f0b94e
        bbox = gfxRect(0, 0, 0, 0);
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aFlags == eBBoxIncludeFillGeometry &&
Packit f0b94e
      // We only cache bbox in element's own user space
Packit f0b94e
      !aToBoundsSpace) {
Packit f0b94e
    // Obtaining the bbox for objectBoundingBox calculations is common so we
Packit f0b94e
    // cache the result for future calls, since calculation can be expensive:
Packit f0b94e
    aFrame->SetProperty(ObjectBoundingBoxProperty(), new gfxRect(bbox));
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return bbox;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxPoint nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame* aFrame) {
Packit f0b94e
  if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
Packit f0b94e
    // The user space for non-SVG frames is defined as the bounding box of the
Packit f0b94e
    // frame's border-box rects over all continuations.
Packit f0b94e
    return gfxPoint();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Leaf frames apply their own offset inside their user space.
Packit f0b94e
  if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
Packit f0b94e
      nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
Packit f0b94e
    return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
Packit f0b94e
                                        nsPresContext::AppUnitsPerCSSPixel())
Packit f0b94e
        .TopLeft();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // For foreignObject frames, nsSVGUtils::GetBBox applies their local
Packit f0b94e
  // transform, so we need to do the same here.
Packit f0b94e
  if (aFrame->IsSVGForeignObjectFrame()) {
Packit f0b94e
    gfxMatrix transform =
Packit f0b94e
        static_cast<nsSVGElement*>(aFrame->GetContent())
Packit f0b94e
            ->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
Packit f0b94e
    NS_ASSERTION(!transform.HasNonTranslation(),
Packit f0b94e
                 "we're relying on this being an offset-only transform");
Packit f0b94e
    return transform.GetTranslation();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return gfxPoint();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
static gfxRect GetBoundingBoxRelativeRect(const nsSVGLength2* aXYWH,
Packit f0b94e
                                          const gfxRect& aBBox) {
Packit f0b94e
  return gfxRect(aBBox.x + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[0]),
Packit f0b94e
                 aBBox.y + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[1]),
Packit f0b94e
                 nsSVGUtils::ObjectSpace(aBBox, &aXYWH[2]),
Packit f0b94e
                 nsSVGUtils::ObjectSpace(aBBox, &aXYWH[3]));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxRect nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2* aXYWH,
Packit f0b94e
                                    const gfxRect& aBBox,
Packit f0b94e
                                    const UserSpaceMetrics& aMetrics) {
Packit f0b94e
  if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
Packit f0b94e
    return GetBoundingBoxRelativeRect(aXYWH, aBBox);
Packit f0b94e
  }
Packit f0b94e
  return gfxRect(UserSpace(aMetrics, &aXYWH[0]), UserSpace(aMetrics, &aXYWH[1]),
Packit f0b94e
                 UserSpace(aMetrics, &aXYWH[2]),
Packit f0b94e
                 UserSpace(aMetrics, &aXYWH[3]));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxRect nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2* aXYWH,
Packit f0b94e
                                    const gfxRect& aBBox, nsIFrame* aFrame) {
Packit f0b94e
  if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
Packit f0b94e
    return GetBoundingBoxRelativeRect(aXYWH, aBBox);
Packit f0b94e
  }
Packit f0b94e
  nsIContent* content = aFrame->GetContent();
Packit f0b94e
  if (content->IsSVGElement()) {
Packit f0b94e
    nsSVGElement* svgElement = static_cast<nsSVGElement*>(content);
Packit f0b94e
    return GetRelativeRect(aUnits, aXYWH, aBBox, SVGElementMetrics(svgElement));
Packit f0b94e
  }
Packit f0b94e
  return GetRelativeRect(aUnits, aXYWH, aBBox,
Packit f0b94e
                         NonSVGFrameUserSpaceMetrics(aFrame));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::CanOptimizeOpacity(nsIFrame* aFrame) {
Packit f0b94e
  if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
  LayoutFrameType type = aFrame->Type();
Packit f0b94e
  if (type != LayoutFrameType::SVGImage &&
Packit f0b94e
      type != LayoutFrameType::SVGGeometry) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
  if (aFrame->StyleEffects()->HasFilters()) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
  // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
Packit f0b94e
  if (type == LayoutFrameType::SVGImage) {
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
  const nsStyleSVG* style = aFrame->StyleSVG();
Packit f0b94e
  if (style->HasMarker()) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (nsLayoutUtils::HasAnimationOfProperty(aFrame, eCSSProperty_opacity)) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!style->HasFill() || !HasStroke(aFrame)) {
Packit f0b94e
    return true;
Packit f0b94e
  }
Packit f0b94e
  return false;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxMatrix nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix& aMatrix,
Packit f0b94e
                                           nsSVGEnum* aUnits, nsIFrame* aFrame,
Packit f0b94e
                                           uint32_t aFlags) {
Packit f0b94e
  if (aFrame && aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
Packit f0b94e
    gfxRect bbox = GetBBox(aFrame, aFlags);
Packit f0b94e
    gfxMatrix tm = aMatrix;
Packit f0b94e
    tm.PreTranslate(gfxPoint(bbox.X(), bbox.Y()));
Packit f0b94e
    tm.PreScale(bbox.Width(), bbox.Height());
Packit f0b94e
    return tm;
Packit f0b94e
  }
Packit f0b94e
  return aMatrix;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsIFrame* nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame) {
Packit f0b94e
  for (nsIFrame* ancestorFrame = aStartFrame; ancestorFrame;
Packit f0b94e
       ancestorFrame = ancestorFrame->GetParent()) {
Packit f0b94e
    if (!ancestorFrame->IsSVGAFrame()) {
Packit f0b94e
      return ancestorFrame;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  return nullptr;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame* aFrame,
Packit f0b94e
                                              gfxMatrix* aUserToOuterSVG) {
Packit f0b94e
  if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
Packit f0b94e
    aFrame = aFrame->GetParent();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (!aFrame->StyleSVGReset()->HasNonScalingStroke()) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsIContent* content = aFrame->GetContent();
Packit f0b94e
  MOZ_ASSERT(content->IsSVGElement(), "bad cast");
Packit f0b94e
Packit f0b94e
  *aUserToOuterSVG = ThebesMatrix(
Packit f0b94e
      SVGContentUtils::GetCTM(static_cast<nsSVGElement*>(content), true));
Packit f0b94e
Packit f0b94e
  return !aUserToOuterSVG->IsIdentity();
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// The logic here comes from _cairo_stroke_style_max_distance_from_path
Packit f0b94e
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
Packit f0b94e
                                             nsIFrame* aFrame,
Packit f0b94e
                                             double aStyleExpansionFactor,
Packit f0b94e
                                             const gfxMatrix& aMatrix) {
Packit f0b94e
  double style_expansion =
Packit f0b94e
      aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
Packit f0b94e
Packit f0b94e
  gfxMatrix matrix = aMatrix;
Packit f0b94e
Packit f0b94e
  gfxMatrix outerSVGToUser;
Packit f0b94e
  if (nsSVGUtils::GetNonScalingStrokeTransform(aFrame, &outerSVGToUser)) {
Packit f0b94e
    outerSVGToUser.Invert();
Packit f0b94e
    matrix.PreMultiply(outerSVGToUser);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  double dx = style_expansion * (fabs(matrix._11) + fabs(matrix._21));
Packit f0b94e
  double dy = style_expansion * (fabs(matrix._22) + fabs(matrix._12));
Packit f0b94e
Packit f0b94e
  gfxRect strokeExtents = aPathExtents;
Packit f0b94e
  strokeExtents.Inflate(dx, dy);
Packit f0b94e
  return strokeExtents;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/*static*/ gfxRect nsSVGUtils::PathExtentsToMaxStrokeExtents(
Packit f0b94e
    const gfxRect& aPathExtents, nsTextFrame* aFrame,
Packit f0b94e
    const gfxMatrix& aMatrix) {
Packit f0b94e
  NS_ASSERTION(nsSVGUtils::IsInSVGTextSubtree(aFrame),
Packit f0b94e
               "expected an nsTextFrame for SVG text");
Packit f0b94e
  return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/*static*/ gfxRect nsSVGUtils::PathExtentsToMaxStrokeExtents(
Packit f0b94e
    const gfxRect& aPathExtents, SVGGeometryFrame* aFrame,
Packit f0b94e
    const gfxMatrix& aMatrix) {
Packit f0b94e
  bool strokeMayHaveCorners =
Packit f0b94e
      !SVGContentUtils::ShapeTypeHasNoCorners(aFrame->GetContent());
Packit f0b94e
Packit f0b94e
  // For a shape without corners the stroke can only extend half the stroke
Packit f0b94e
  // width from the path in the x/y-axis directions. For shapes with corners
Packit f0b94e
  // the stroke can extend by sqrt(1/2) (think 45 degree rotated rect, or line
Packit f0b94e
  // with stroke-linecaps="square").
Packit f0b94e
  double styleExpansionFactor = strokeMayHaveCorners ? M_SQRT1_2 : 0.5;
Packit f0b94e
Packit f0b94e
  // The stroke can extend even further for paths that can be affected by
Packit f0b94e
  // stroke-miterlimit.
Packit f0b94e
  bool affectedByMiterlimit = aFrame->GetContent()->IsAnyOfSVGElements(
Packit f0b94e
      nsGkAtoms::path, nsGkAtoms::polyline, nsGkAtoms::polygon);
Packit f0b94e
Packit f0b94e
  if (affectedByMiterlimit) {
Packit f0b94e
    const nsStyleSVG* style = aFrame->StyleSVG();
Packit f0b94e
    if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
Packit f0b94e
        styleExpansionFactor < style->mStrokeMiterlimit / 2.0) {
Packit f0b94e
      styleExpansionFactor = style->mStrokeMiterlimit / 2.0;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame,
Packit f0b94e
                                         styleExpansionFactor, aMatrix);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
// ----------------------------------------------------------------------
Packit f0b94e
Packit f0b94e
/* static */ nscolor nsSVGUtils::GetFallbackOrPaintColor(
Packit f0b94e
    nsStyleContext* aStyleContext, nsStyleSVGPaint nsStyleSVG::*aFillOrStroke) {
Packit f0b94e
  const nsStyleSVGPaint& paint = aStyleContext->StyleSVG()->*aFillOrStroke;
Packit f0b94e
  nsStyleContext* styleIfVisited = aStyleContext->GetStyleIfVisited();
Packit f0b94e
  nscolor color;
Packit f0b94e
  switch (paint.Type()) {
Packit f0b94e
    case eStyleSVGPaintType_Server:
Packit f0b94e
    case eStyleSVGPaintType_ContextStroke:
Packit f0b94e
      color = paint.GetFallbackType() == eStyleSVGFallbackType_Color
Packit f0b94e
                  ? paint.GetFallbackColor()
Packit f0b94e
                  : NS_RGBA(0, 0, 0, 0);
Packit f0b94e
      break;
Packit f0b94e
    case eStyleSVGPaintType_ContextFill:
Packit f0b94e
      color = paint.GetFallbackType() == eStyleSVGFallbackType_Color
Packit f0b94e
                  ? paint.GetFallbackColor()
Packit f0b94e
                  : NS_RGB(0, 0, 0);
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      color = paint.GetColor();
Packit f0b94e
      break;
Packit f0b94e
  }
Packit f0b94e
  if (styleIfVisited) {
Packit f0b94e
    const nsStyleSVGPaint& paintIfVisited =
Packit f0b94e
        styleIfVisited->StyleSVG()->*aFillOrStroke;
Packit f0b94e
    // To prevent Web content from detecting if a user has visited a URL
Packit f0b94e
    // (via URL loading triggered by paint servers or performance
Packit f0b94e
    // differences between paint servers or between a paint server and a
Packit f0b94e
    // color), we do not allow whether links are visited to change which
Packit f0b94e
    // paint server is used or switch between paint servers and simple
Packit f0b94e
    // colors.  A :visited style may only override a simple color with
Packit f0b94e
    // another simple color.
Packit f0b94e
    if (paintIfVisited.Type() == eStyleSVGPaintType_Color &&
Packit f0b94e
        paint.Type() == eStyleSVGPaintType_Color) {
Packit f0b94e
      nscolor colors[2] = {color, paintIfVisited.GetColor()};
Packit f0b94e
      return nsStyleContext::CombineVisitedColors(
Packit f0b94e
          colors, aStyleContext->RelevantLinkVisited());
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
  return color;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* static */ void nsSVGUtils::MakeFillPatternFor(
Packit f0b94e
    nsIFrame* aFrame, gfxContext* aContext, GeneralPattern* aOutPattern,
Packit f0b94e
    imgDrawingParams& aImgParams, SVGContextPaint* aContextPaint) {
Packit f0b94e
  const nsStyleSVG* style = aFrame->StyleSVG();
Packit f0b94e
  if (style->mFill.Type() == eStyleSVGPaintType_None) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const float opacity = aFrame->StyleEffects()->mOpacity;
Packit f0b94e
Packit f0b94e
  float fillOpacity = GetOpacity(style->FillOpacitySource(),
Packit f0b94e
                                 style->mFillOpacity, aContextPaint);
Packit f0b94e
  if (opacity < 1.0f && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
Packit f0b94e
    // Combine the group opacity into the fill opacity (we will have skipped
Packit f0b94e
    // creating an offscreen surface to apply the group opacity).
Packit f0b94e
    fillOpacity *= opacity;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const DrawTarget* dt = aContext->GetDrawTarget();
Packit f0b94e
Packit f0b94e
  nsSVGPaintServerFrame* ps = SVGObserverUtils::GetPaintServer(
Packit f0b94e
      aFrame, &nsStyleSVG::mFill, SVGObserverUtils::FillProperty());
Packit f0b94e
Packit f0b94e
  if (ps) {
Packit f0b94e
    RefPtr<gfxPattern> pattern =
Packit f0b94e
        ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrixDouble(),
Packit f0b94e
                                  &nsStyleSVG::mFill, fillOpacity, aImgParams);
Packit f0b94e
    if (pattern) {
Packit f0b94e
      pattern->CacheColorStops(dt);
Packit f0b94e
      aOutPattern->Init(*pattern->GetPattern(dt));
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aContextPaint) {
Packit f0b94e
    RefPtr<gfxPattern> pattern;
Packit f0b94e
    switch (style->mFill.Type()) {
Packit f0b94e
      case eStyleSVGPaintType_ContextFill:
Packit f0b94e
        pattern = aContextPaint->GetFillPattern(
Packit f0b94e
            dt, fillOpacity, aContext->CurrentMatrixDouble(), aImgParams);
Packit f0b94e
        break;
Packit f0b94e
      case eStyleSVGPaintType_ContextStroke:
Packit f0b94e
        pattern = aContextPaint->GetStrokePattern(
Packit f0b94e
            dt, fillOpacity, aContext->CurrentMatrixDouble(), aImgParams);
Packit f0b94e
        break;
Packit f0b94e
      default:;
Packit f0b94e
    }
Packit f0b94e
    if (pattern) {
Packit f0b94e
      aOutPattern->Init(*pattern->GetPattern(dt));
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (style->mFill.GetFallbackType() == eStyleSVGFallbackType_None) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // On failure, use the fallback colour in case we have an
Packit f0b94e
  // objectBoundingBox where the width or height of the object is zero.
Packit f0b94e
  // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
Packit f0b94e
  Color color(Color::FromABGR(
Packit f0b94e
      GetFallbackOrPaintColor(aFrame->StyleContext(), &nsStyleSVG::mFill)));
Packit f0b94e
  color.a *= fillOpacity;
Packit f0b94e
  aOutPattern->InitColorPattern(ToDeviceColor(color));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* static */ void nsSVGUtils::MakeStrokePatternFor(
Packit f0b94e
    nsIFrame* aFrame, gfxContext* aContext, GeneralPattern* aOutPattern,
Packit f0b94e
    imgDrawingParams& aImgParams, SVGContextPaint* aContextPaint) {
Packit f0b94e
  const nsStyleSVG* style = aFrame->StyleSVG();
Packit f0b94e
  if (style->mStroke.Type() == eStyleSVGPaintType_None) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const float opacity = aFrame->StyleEffects()->mOpacity;
Packit f0b94e
Packit f0b94e
  float strokeOpacity = GetOpacity(style->StrokeOpacitySource(),
Packit f0b94e
                                   style->mStrokeOpacity, aContextPaint);
Packit f0b94e
  if (opacity < 1.0f && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
Packit f0b94e
    // Combine the group opacity into the stroke opacity (we will have skipped
Packit f0b94e
    // creating an offscreen surface to apply the group opacity).
Packit f0b94e
    strokeOpacity *= opacity;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const DrawTarget* dt = aContext->GetDrawTarget();
Packit f0b94e
Packit f0b94e
  nsSVGPaintServerFrame* ps = SVGObserverUtils::GetPaintServer(
Packit f0b94e
      aFrame, &nsStyleSVG::mStroke, SVGObserverUtils::StrokeProperty());
Packit f0b94e
Packit f0b94e
  if (ps) {
Packit f0b94e
    RefPtr<gfxPattern> pattern = ps->GetPaintServerPattern(
Packit f0b94e
        aFrame, dt, aContext->CurrentMatrixDouble(), &nsStyleSVG::mStroke,
Packit f0b94e
        strokeOpacity, aImgParams);
Packit f0b94e
    if (pattern) {
Packit f0b94e
      pattern->CacheColorStops(dt);
Packit f0b94e
      aOutPattern->Init(*pattern->GetPattern(dt));
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (aContextPaint) {
Packit f0b94e
    RefPtr<gfxPattern> pattern;
Packit f0b94e
    switch (style->mStroke.Type()) {
Packit f0b94e
      case eStyleSVGPaintType_ContextFill:
Packit f0b94e
        pattern = aContextPaint->GetFillPattern(
Packit f0b94e
            dt, strokeOpacity, aContext->CurrentMatrixDouble(), aImgParams);
Packit f0b94e
        break;
Packit f0b94e
      case eStyleSVGPaintType_ContextStroke:
Packit f0b94e
        pattern = aContextPaint->GetStrokePattern(
Packit f0b94e
            dt, strokeOpacity, aContext->CurrentMatrixDouble(), aImgParams);
Packit f0b94e
        break;
Packit f0b94e
      default:;
Packit f0b94e
    }
Packit f0b94e
    if (pattern) {
Packit f0b94e
      aOutPattern->Init(*pattern->GetPattern(dt));
Packit f0b94e
      return;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (style->mStroke.GetFallbackType() == eStyleSVGFallbackType_None) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // On failure, use the fallback colour in case we have an
Packit f0b94e
  // objectBoundingBox where the width or height of the object is zero.
Packit f0b94e
  // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
Packit f0b94e
  Color color(Color::FromABGR(
Packit f0b94e
      GetFallbackOrPaintColor(aFrame->StyleContext(), &nsStyleSVG::mStroke)));
Packit f0b94e
  color.a *= strokeOpacity;
Packit f0b94e
  aOutPattern->InitColorPattern(ToDeviceColor(color));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/* static */ float nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
Packit f0b94e
                                          const float& aOpacity,
Packit f0b94e
                                          SVGContextPaint* aContextPaint) {
Packit f0b94e
  float opacity = 1.0f;
Packit f0b94e
  switch (aOpacityType) {
Packit f0b94e
    case eStyleSVGOpacitySource_Normal:
Packit f0b94e
      opacity = aOpacity;
Packit f0b94e
      break;
Packit f0b94e
    case eStyleSVGOpacitySource_ContextFillOpacity:
Packit f0b94e
      if (aContextPaint) {
Packit f0b94e
        opacity = aContextPaint->GetFillOpacity();
Packit f0b94e
      } else {
Packit f0b94e
        NS_WARNING(
Packit f0b94e
            "Content used context-fill-opacity when not in a context element");
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    case eStyleSVGOpacitySource_ContextStrokeOpacity:
Packit f0b94e
      if (aContextPaint) {
Packit f0b94e
        opacity = aContextPaint->GetStrokeOpacity();
Packit f0b94e
      } else {
Packit f0b94e
        NS_WARNING(
Packit f0b94e
            "Content used context-stroke-opacity when not in a context "
Packit f0b94e
            "element");
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
Packit f0b94e
  }
Packit f0b94e
  return opacity;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::HasStroke(nsIFrame* aFrame, SVGContextPaint* aContextPaint) {
Packit f0b94e
  const nsStyleSVG* style = aFrame->StyleSVG();
Packit f0b94e
  return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
float nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame,
Packit f0b94e
                                 SVGContextPaint* aContextPaint) {
Packit f0b94e
  const nsStyleSVG* style = aFrame->StyleSVG();
Packit f0b94e
  if (aContextPaint && style->StrokeWidthFromObject()) {
Packit f0b94e
    return aContextPaint->GetStrokeWidth();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsIContent* content = aFrame->GetContent();
Packit f0b94e
  if (content->IsNodeOfType(nsINode::eTEXT)) {
Packit f0b94e
    content = content->GetParent();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsSVGElement* ctx = static_cast<nsSVGElement*>(content);
Packit f0b94e
Packit f0b94e
  return SVGContentUtils::CoordToFloat(ctx, style->mStrokeWidth);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::SetupStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
Packit f0b94e
                                     SVGContextPaint* aContextPaint) {
Packit f0b94e
  SVGContentUtils::AutoStrokeOptions strokeOptions;
Packit f0b94e
  SVGContentUtils::GetStrokeOptions(
Packit f0b94e
      &strokeOptions, static_cast<nsSVGElement*>(aFrame->GetContent()),
Packit f0b94e
      aFrame->StyleContext(), aContextPaint);
Packit f0b94e
Packit f0b94e
  if (strokeOptions.mLineWidth <= 0) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  aContext->SetLineWidth(strokeOptions.mLineWidth);
Packit f0b94e
  aContext->SetLineCap(strokeOptions.mLineCap);
Packit f0b94e
  aContext->SetMiterLimit(strokeOptions.mMiterLimit);
Packit f0b94e
  aContext->SetLineJoin(strokeOptions.mLineJoin);
Packit f0b94e
  aContext->SetDash(strokeOptions.mDashPattern, strokeOptions.mDashLength,
Packit f0b94e
                    strokeOptions.mDashOffset);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
uint16_t nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame) {
Packit f0b94e
  uint16_t flags = 0;
Packit f0b94e
Packit f0b94e
  switch (aFrame->StyleUserInterface()->mPointerEvents) {
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_NONE:
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_AUTO:
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
Packit f0b94e
      if (aFrame->StyleVisibility()->IsVisible()) {
Packit f0b94e
        if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
Packit f0b94e
          flags |= SVG_HIT_TEST_FILL;
Packit f0b94e
        if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
Packit f0b94e
          flags |= SVG_HIT_TEST_STROKE;
Packit f0b94e
        if (aFrame->StyleSVG()->mStrokeOpacity > 0)
Packit f0b94e
          flags |= SVG_HIT_TEST_CHECK_MRECT;
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
Packit f0b94e
      if (aFrame->StyleVisibility()->IsVisible()) {
Packit f0b94e
        flags |= SVG_HIT_TEST_FILL;
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
Packit f0b94e
      if (aFrame->StyleVisibility()->IsVisible()) {
Packit f0b94e
        flags |= SVG_HIT_TEST_STROKE;
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_VISIBLE:
Packit f0b94e
      if (aFrame->StyleVisibility()->IsVisible()) {
Packit f0b94e
        flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
Packit f0b94e
      }
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_PAINTED:
Packit f0b94e
      if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
Packit f0b94e
        flags |= SVG_HIT_TEST_FILL;
Packit f0b94e
      if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
Packit f0b94e
        flags |= SVG_HIT_TEST_STROKE;
Packit f0b94e
      if (aFrame->StyleSVG()->mStrokeOpacity) flags |= SVG_HIT_TEST_CHECK_MRECT;
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_FILL:
Packit f0b94e
      flags |= SVG_HIT_TEST_FILL;
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_STROKE:
Packit f0b94e
      flags |= SVG_HIT_TEST_STROKE;
Packit f0b94e
      break;
Packit f0b94e
    case NS_STYLE_POINTER_EVENTS_ALL:
Packit f0b94e
      flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
Packit f0b94e
      break;
Packit f0b94e
    default:
Packit f0b94e
      NS_ERROR("not reached");
Packit f0b94e
      break;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return flags;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
void nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext) {
Packit f0b94e
  nsIFrame* frame = aElement->GetPrimaryFrame();
Packit f0b94e
  nsSVGDisplayableFrame* svgFrame = do_QueryFrame(frame);
Packit f0b94e
  if (!svgFrame) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
  gfxMatrix m;
Packit f0b94e
  if (frame->GetContent()->IsSVGElement()) {
Packit f0b94e
    // PaintSVG() expects the passed transform to be the transform to its own
Packit f0b94e
    // SVG user space, so we need to account for any 'transform' attribute:
Packit f0b94e
    m = static_cast<nsSVGElement*>(frame->GetContent())
Packit f0b94e
            ->PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // SVG-in-OpenType is not allowed to paint exteral resources, so we can
Packit f0b94e
  // just pass a dummy params into PatintSVG.
Packit f0b94e
  imgDrawingParams dummy;
Packit f0b94e
  svgFrame->PaintSVG(*aContext, m, dummy);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
Packit f0b94e
                                    const gfxMatrix& aSVGToAppSpace,
Packit f0b94e
                                    gfxRect* aResult) {
Packit f0b94e
  nsIFrame* frame = aElement->GetPrimaryFrame();
Packit f0b94e
  nsSVGDisplayableFrame* svgFrame = do_QueryFrame(frame);
Packit f0b94e
  if (!svgFrame) {
Packit f0b94e
    return false;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  gfxMatrix transform(aSVGToAppSpace);
Packit f0b94e
  nsIContent* content = frame->GetContent();
Packit f0b94e
  if (content->IsSVGElement()) {
Packit f0b94e
    transform = static_cast<nsSVGElement*>(content)->PrependLocalTransformsTo(
Packit f0b94e
        aSVGToAppSpace);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  *aResult =
Packit f0b94e
      svgFrame
Packit f0b94e
          ->GetBBoxContribution(gfx::ToMatrix(transform),
Packit f0b94e
                                nsSVGUtils::eBBoxIncludeFill |
Packit f0b94e
                                    nsSVGUtils::eBBoxIncludeFillGeometry |
Packit f0b94e
                                    nsSVGUtils::eBBoxIncludeStroke |
Packit f0b94e
                                    nsSVGUtils::eBBoxIncludeStrokeGeometry |
Packit f0b94e
                                    nsSVGUtils::eBBoxIncludeMarkers)
Packit f0b94e
          .ToThebesRect();
Packit f0b94e
  return true;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsRect nsSVGUtils::ToCanvasBounds(const gfxRect& aUserspaceRect,
Packit f0b94e
                                  const gfxMatrix& aToCanvas,
Packit f0b94e
                                  const nsPresContext* presContext) {
Packit f0b94e
  return nsLayoutUtils::RoundGfxRectToAppRect(
Packit f0b94e
      aToCanvas.TransformBounds(aUserspaceRect),
Packit f0b94e
      presContext->AppUnitsPerDevPixel());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
gfxMatrix nsSVGUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame) {
Packit f0b94e
  int32_t appUnitsPerDevPixel =
Packit f0b94e
      aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
Packit f0b94e
  float devPxPerCSSPx =
Packit f0b94e
      1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
Packit f0b94e
Packit f0b94e
  return gfxMatrix(devPxPerCSSPx, 0.0, 0.0, devPxPerCSSPx, 0.0, 0.0);
Packit f0b94e
}