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 +}