Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // Main header first:
7 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
8 #include "nsSVGUtils.h"
9 #include <algorithm>
11 // Keep others in (case-insensitive) order:
12 #include "gfx2DGlue.h"
13 #include "gfxContext.h"
14 #include "gfxMatrix.h"
15 #include "gfxPlatform.h"
16 #include "gfxRect.h"
17 #include "gfxUtils.h"
18 #include "mozilla/gfx/2D.h"
19 #include "mozilla/Preferences.h"
20 #include "nsCSSFrameConstructor.h"
21 #include "nsDisplayList.h"
22 #include "nsFilterInstance.h"
23 #include "nsFrameList.h"
24 #include "nsGkAtoms.h"
25 #include "nsIContent.h"
26 #include "nsIDocument.h"
27 #include "nsIFrame.h"
28 #include "nsIPresShell.h"
29 #include "nsISVGChildFrame.h"
30 #include "nsPresContext.h"
31 #include "nsRenderingContext.h"
32 #include "nsStyleCoord.h"
33 #include "nsStyleStruct.h"
34 #include "nsSVGClipPathFrame.h"
35 #include "nsSVGContainerFrame.h"
36 #include "nsSVGEffects.h"
37 #include "nsSVGFilterPaintCallback.h"
38 #include "nsSVGForeignObjectFrame.h"
39 #include "gfxSVGGlyphs.h"
40 #include "nsSVGInnerSVGFrame.h"
41 #include "nsSVGIntegrationUtils.h"
42 #include "nsSVGLength2.h"
43 #include "nsSVGMaskFrame.h"
44 #include "nsSVGOuterSVGFrame.h"
45 #include "mozilla/dom/SVGPathElement.h"
46 #include "nsSVGPathGeometryElement.h"
47 #include "nsSVGPathGeometryFrame.h"
48 #include "nsSVGPaintServerFrame.h"
49 #include "mozilla/dom/SVGSVGElement.h"
50 #include "nsTextFrame.h"
51 #include "SVGContentUtils.h"
52 #include "mozilla/unused.h"
54 using namespace mozilla;
55 using namespace mozilla::dom;
56 using namespace mozilla::gfx;
58 static bool sSVGDisplayListHitTestingEnabled;
59 static bool sSVGDisplayListPaintingEnabled;
61 bool
62 NS_SVGDisplayListHitTestingEnabled()
63 {
64 return sSVGDisplayListHitTestingEnabled;
65 }
67 bool
68 NS_SVGDisplayListPaintingEnabled()
69 {
70 return sSVGDisplayListPaintingEnabled;
71 }
73 // we only take the address of this:
74 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
76 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
77 RenderMode aMode
78 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
79 : mContext(aContext)
80 , mOriginalRenderState(nullptr)
81 , mMode(aMode)
82 , mPaintingToWindow(false)
83 {
84 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
85 mOriginalRenderState = aContext->RemoveUserData(&sSVGAutoRenderStateKey);
86 // We always remove ourselves from aContext before it dies, so
87 // passing nullptr as the destroy function is okay.
88 aContext->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
89 }
91 SVGAutoRenderState::~SVGAutoRenderState()
92 {
93 mContext->RemoveUserData(&sSVGAutoRenderStateKey);
94 if (mOriginalRenderState) {
95 mContext->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState, nullptr);
96 }
97 }
99 void
100 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
101 {
102 mPaintingToWindow = aPaintingToWindow;
103 }
105 /* static */ SVGAutoRenderState::RenderMode
106 SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
107 {
108 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
109 if (state) {
110 return static_cast<SVGAutoRenderState*>(state)->mMode;
111 }
112 return NORMAL;
113 }
115 /* static */ bool
116 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
117 {
118 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
119 if (state) {
120 return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
121 }
122 return false;
123 }
125 void
126 nsSVGUtils::Init()
127 {
128 Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
129 "svg.display-lists.hit-testing.enabled");
131 Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
132 "svg.display-lists.painting.enabled");
133 }
135 nsSVGDisplayContainerFrame*
136 nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
137 {
138 NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
139 if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
140 return nullptr;
141 }
142 while ((aFrame = aFrame->GetParent())) {
143 NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
144 if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame ||
145 aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
146 return do_QueryFrame(aFrame);
147 }
148 }
149 NS_NOTREACHED("This is not reached. It's only needed to compile.");
150 return nullptr;
151 }
153 nsRect
154 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
155 const nsRect &aPreFilterRect)
156 {
157 NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
158 "Called on invalid frame type");
160 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
161 if (!property || !property->ReferencesValidResources()) {
162 return aPreFilterRect;
163 }
165 return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
166 }
168 bool
169 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
170 {
171 return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
172 }
174 bool
175 nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame)
176 {
177 nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
178 do {
179 if (outer->IsCallingReflowSVG()) {
180 return true;
181 }
182 outer = GetOuterSVGFrame(outer->GetParent());
183 } while (outer);
184 return false;
185 }
187 void
188 nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
189 {
190 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
191 "Passed bad frame!");
193 // If this is triggered, the callers should be fixed to call us before
194 // ReflowSVG is called. If we try to mark dirty bits on frames while we're
195 // in the process of removing them, things will get messed up.
196 NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
197 "Do not call under nsISVGChildFrame::ReflowSVG!");
199 // We don't call nsSVGEffects::InvalidateRenderingObservers here because
200 // we should only be called under InvalidateAndScheduleReflowSVG (which
201 // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
202 // (at which point the frame has no observers).
204 if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
205 return;
206 }
208 if (aFrame->GetStateBits() &
209 (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
210 // Nothing to do if we're already dirty, or if the outer-<svg>
211 // hasn't yet had its initial reflow.
212 return;
213 }
215 nsSVGOuterSVGFrame *outerSVGFrame = nullptr;
217 // We must not add dirty bits to the nsSVGOuterSVGFrame or else
218 // PresShell::FrameNeedsReflow won't work when we pass it in below.
219 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
220 outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
221 } else {
222 aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
224 nsIFrame *f = aFrame->GetParent();
225 while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
226 if (f->GetStateBits() &
227 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
228 return;
229 }
230 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
231 f = f->GetParent();
232 NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG),
233 "NS_STATE_IS_OUTER_SVG check above not valid!");
234 }
236 outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
238 NS_ABORT_IF_FALSE(outerSVGFrame &&
239 outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame,
240 "Did not find nsSVGOuterSVGFrame!");
241 }
243 if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
244 // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
245 // need to call PresShell::FrameNeedsReflow, since we have an
246 // nsSVGOuterSVGFrame::DidReflow call pending.
247 return;
248 }
250 nsFrameState dirtyBit =
251 (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN);
253 aFrame->PresContext()->PresShell()->FrameNeedsReflow(
254 outerSVGFrame, nsIPresShell::eResize, dirtyBit);
255 }
257 bool
258 nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame)
259 {
260 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
261 "SVG uses bits differently!");
263 // The flags we test here may change, hence why we have this separate
264 // function.
265 return NS_SUBTREE_DIRTY(aFrame);
266 }
268 void
269 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
270 {
271 NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
272 "Not expecting to be called on the outer SVG Frame");
274 aFrame = aFrame->GetParent();
276 while (aFrame) {
277 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
278 return;
280 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
281 if (property) {
282 property->Invalidate();
283 }
284 aFrame = aFrame->GetParent();
285 }
286 }
288 float
289 nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
290 {
291 float axis;
293 switch (aLength->GetCtxType()) {
294 case SVGContentUtils::X:
295 axis = aRect.Width();
296 break;
297 case SVGContentUtils::Y:
298 axis = aRect.Height();
299 break;
300 case SVGContentUtils::XY:
301 axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
302 aRect.Width(), aRect.Height()));
303 break;
304 default:
305 NS_NOTREACHED("unexpected ctx type");
306 axis = 0.0f;
307 break;
308 }
309 if (aLength->IsPercentage()) {
310 // Multiply first to avoid precision errors:
311 return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
312 }
313 return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis;
314 }
316 float
317 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
318 {
319 return aLength->GetAnimValue(aSVGElement);
320 }
322 float
323 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
324 {
325 return aLength->GetAnimValue(aNonSVGContext);
326 }
328 nsSVGOuterSVGFrame *
329 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
330 {
331 while (aFrame) {
332 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
333 return static_cast<nsSVGOuterSVGFrame*>(aFrame);
334 }
335 aFrame = aFrame->GetParent();
336 }
338 return nullptr;
339 }
341 nsIFrame*
342 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
343 {
344 nsISVGChildFrame* svg = do_QueryFrame(aFrame);
345 if (!svg)
346 return nullptr;
347 *aRect = (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
348 nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
349 return GetOuterSVGFrame(aFrame);
350 }
352 gfxMatrix
353 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame, uint32_t aFor,
354 nsIFrame* aTransformRoot)
355 {
356 // XXX yuck, we really need a common interface for GetCanvasTM
358 if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
359 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
360 }
362 if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
363 !aTransformRoot) {
364 if ((aFor == nsISVGChildFrame::FOR_PAINTING &&
365 NS_SVGDisplayListPaintingEnabled()) ||
366 (aFor == nsISVGChildFrame::FOR_HIT_TESTING &&
367 NS_SVGDisplayListHitTestingEnabled())) {
368 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
369 }
370 }
372 nsIAtom* type = aFrame->GetType();
373 if (type == nsGkAtoms::svgForeignObjectFrame) {
374 return static_cast<nsSVGForeignObjectFrame*>(aFrame)->
375 GetCanvasTM(aFor, aTransformRoot);
376 }
377 if (type == nsGkAtoms::svgOuterSVGFrame) {
378 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
379 }
381 nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
382 if (containerFrame) {
383 return containerFrame->GetCanvasTM(aFor, aTransformRoot);
384 }
386 return static_cast<nsSVGPathGeometryFrame*>(aFrame)->
387 GetCanvasTM(aFor, aTransformRoot);
388 }
390 gfxMatrix
391 nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame, uint32_t aFor)
392 {
393 NS_ASSERTION(aFor == nsISVGChildFrame::FOR_OUTERSVG_TM,
394 "Unexpected aFor?");
396 nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
397 NS_ASSERTION(svgFrame, "bad frame");
399 gfxMatrix tm;
400 if (svgFrame) {
401 nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
402 tm = content->PrependLocalTransformsTo(
403 GetCanvasTM(aFrame->GetParent(), aFor),
404 nsSVGElement::eUserSpaceToParent);
405 }
406 return tm;
407 }
409 void
410 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
411 {
412 nsIFrame *kid = aFrame->GetFirstPrincipalChild();
414 while (kid) {
415 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
416 if (SVGFrame) {
417 SVGFrame->NotifySVGChanged(aFlags);
418 } else {
419 NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) || kid->IsSVGText(),
420 "SVG frame expected");
421 // recurse into the children of container frames e.g. <clipPath>, <mask>
422 // in case they have child frames with transformation matrices
423 if (kid->IsFrameOfType(nsIFrame::eSVG)) {
424 NotifyChildrenOfSVGChange(kid, aFlags);
425 }
426 }
427 kid = kid->GetNextSibling();
428 }
429 }
431 // ************************************************************
433 class SVGPaintCallback : public nsSVGFilterPaintCallback
434 {
435 public:
436 virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
437 const nsIntRect* aDirtyRect,
438 nsIFrame* aTransformRoot) MOZ_OVERRIDE
439 {
440 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
441 NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
443 nsIntRect* dirtyRect = nullptr;
444 nsIntRect tmpDirtyRect;
446 // aDirtyRect is in user-space pixels, we need to convert to
447 // outer-SVG-frame-relative device pixels.
448 if (aDirtyRect) {
449 gfxMatrix userToDeviceSpace =
450 nsSVGUtils::GetCanvasTM(aTarget, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
451 if (userToDeviceSpace.IsSingular()) {
452 return;
453 }
454 gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
455 gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
456 dirtyBounds.RoundOut();
457 if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
458 dirtyRect = &tmpDirtyRect;
459 }
460 }
462 svgChildFrame->PaintSVG(aContext, dirtyRect, aTransformRoot);
463 }
464 };
466 void
467 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
468 const nsIntRect *aDirtyRect,
469 nsIFrame *aFrame,
470 nsIFrame *aTransformRoot)
471 {
472 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
473 (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
474 aFrame->PresContext()->IsGlyph(),
475 "If display lists are enabled, only painting of non-display "
476 "SVG should take this code path");
478 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
479 if (!svgChildFrame)
480 return;
482 float opacity = aFrame->StyleDisplay()->mOpacity;
483 if (opacity == 0.0f)
484 return;
486 const nsIContent* content = aFrame->GetContent();
487 if (content->IsSVG() &&
488 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
489 return;
490 }
492 /* Properties are added lazily and may have been removed by a restyle,
493 so make sure all applicable ones are set again. */
495 nsSVGEffects::EffectProperties effectProperties =
496 nsSVGEffects::GetEffectProperties(aFrame);
498 bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
500 if (aDirtyRect &&
501 !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
502 // Here we convert aFrame's paint bounds to outer-<svg> device space,
503 // compare it to aDirtyRect, and return early if they don't intersect.
504 // We don't do this optimization for nondisplay SVG since nondisplay
505 // SVG doesn't maintain bounds/overflow rects.
506 nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
507 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
508 aFrame->IsSVGText()) {
509 // Unlike containers, leaf frames do not include GetPosition() in
510 // GetCanvasTM().
511 overflowRect = overflowRect + aFrame->GetPosition();
512 }
513 int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
514 gfxMatrix tm = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
515 if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
516 gfx::Matrix childrenOnlyTM;
517 if (static_cast<nsSVGContainerFrame*>(aFrame)->
518 HasChildrenOnlyTransform(&childrenOnlyTM)) {
519 // Undo the children-only transform:
520 tm = ThebesMatrix(childrenOnlyTM).Invert() * tm;
521 }
522 }
523 nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
524 tm, aFrame->PresContext()).
525 ToOutsidePixels(appUnitsPerDevPx);
526 if (!aDirtyRect->Intersects(bounds)) {
527 return;
528 }
529 }
531 /* SVG defines the following rendering model:
532 *
533 * 1. Render fill
534 * 2. Render stroke
535 * 3. Render markers
536 * 4. Apply filter
537 * 5. Apply clipping, masking, group opacity
538 *
539 * We follow this, but perform a couple of optimizations:
540 *
541 * + Use cairo's clipPath when representable natively (single object
542 * clip region).
543 *
544 * + Merge opacity and masking if both used together.
545 */
547 if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
548 opacity = 1.0f;
550 gfxContext *gfx = aContext->ThebesContext();
551 bool complexEffects = false;
553 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
554 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
556 bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
558 if (!isOK) {
559 // Some resource is invalid. We shouldn't paint anything.
560 return;
561 }
563 gfxMatrix matrix;
564 if (clipPathFrame || maskFrame)
565 matrix = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
567 /* Check if we need to do additional operations on this child's
568 * rendering, which necessitates rendering into another surface. */
569 if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
570 || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
571 complexEffects = true;
572 gfx->Save();
573 if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
574 // aFrame has a valid visual overflow rect, so clip to it before calling
575 // PushGroup() to minimize the size of the surfaces we'll composite:
576 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
577 gfx->Multiply(GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot));
578 nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
579 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
580 aFrame->IsSVGText()) {
581 // Unlike containers, leaf frames do not include GetPosition() in
582 // GetCanvasTM().
583 overflowRect = overflowRect + aFrame->GetPosition();
584 }
585 aContext->IntersectClip(overflowRect);
586 }
587 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
588 }
590 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
591 * we can just do normal painting and get it clipped appropriately.
592 */
593 if (clipPathFrame && isTrivialClip) {
594 gfx->Save();
595 clipPathFrame->ClipPaint(aContext, aFrame, matrix);
596 }
598 /* Paint the child */
599 if (effectProperties.HasValidFilter()) {
600 nsRegion* dirtyRegion = nullptr;
601 nsRegion tmpDirtyRegion;
602 if (aDirtyRect) {
603 // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
604 // it in frame space.
605 gfxMatrix userToDeviceSpace =
606 GetUserToCanvasTM(aFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
607 if (userToDeviceSpace.IsSingular()) {
608 return;
609 }
610 gfxMatrix deviceToUserSpace = userToDeviceSpace;
611 deviceToUserSpace.Invert();
612 gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
613 gfxRect(aDirtyRect->x, aDirtyRect->y,
614 aDirtyRect->width, aDirtyRect->height));
615 tmpDirtyRegion =
616 nsLayoutUtils::RoundGfxRectToAppRect(
617 dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
618 aFrame->GetPosition();
619 dirtyRegion = &tmpDirtyRegion;
620 }
621 SVGPaintCallback paintCallback;
622 nsFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
623 dirtyRegion, aTransformRoot);
624 } else {
625 svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
626 }
628 if (clipPathFrame && isTrivialClip) {
629 gfx->Restore();
630 }
632 /* No more effects, we're done. */
633 if (!complexEffects)
634 return;
636 gfx->PopGroupToSource();
638 nsRefPtr<gfxPattern> maskSurface =
639 maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
640 matrix, opacity) : nullptr;
642 nsRefPtr<gfxPattern> clipMaskSurface;
643 if (clipPathFrame && !isTrivialClip) {
644 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
646 nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
647 clipMaskSurface = gfx->PopGroup();
649 if (NS_SUCCEEDED(rv) && clipMaskSurface) {
650 // Still more set after clipping, so clip to another surface
651 if (maskSurface || opacity != 1.0f) {
652 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
653 gfx->Mask(clipMaskSurface);
654 gfx->PopGroupToSource();
655 } else {
656 gfx->Mask(clipMaskSurface);
657 }
658 }
659 }
661 if (maskSurface) {
662 gfx->Mask(maskSurface);
663 } else if (opacity != 1.0f) {
664 gfx->Paint(opacity);
665 }
667 gfx->Restore();
668 }
670 bool
671 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
672 {
673 nsSVGEffects::EffectProperties props =
674 nsSVGEffects::GetEffectProperties(aFrame);
675 if (!props.mClipPath)
676 return true;
678 bool isOK = true;
679 nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
680 if (!isOK) {
681 // clipPath is not a valid resource, so nothing gets painted, so
682 // hit-testing must fail.
683 return false;
684 }
685 if (!clipPathFrame) {
686 // clipPath doesn't exist, ignore it.
687 return true;
688 }
690 return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame,
691 nsISVGChildFrame::FOR_HIT_TESTING), aPoint);
692 }
694 nsIFrame *
695 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
696 {
697 // Traverse the list in reverse order, so that if we get a hit we know that's
698 // the topmost frame that intersects the point; then we can just return it.
699 nsIFrame* result = nullptr;
700 for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
701 current;
702 current = current->GetPrevSibling()) {
703 nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
704 if (SVGFrame) {
705 const nsIContent* content = current->GetContent();
706 if (content->IsSVG() &&
707 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
708 continue;
709 }
710 result = SVGFrame->GetFrameForPoint(aPoint);
711 if (result)
712 break;
713 }
714 }
716 if (result && !HitTestClip(aFrame, aPoint))
717 result = nullptr;
719 return result;
720 }
722 nsRect
723 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
724 {
725 nsRect rect;
727 for (nsIFrame* kid = aFrames.FirstChild();
728 kid;
729 kid = kid->GetNextSibling()) {
730 nsISVGChildFrame* child = do_QueryFrame(kid);
731 if (child) {
732 nsRect childRect = child->GetCoveredRegion();
733 rect.UnionRect(rect, childRect);
734 }
735 }
737 return rect;
738 }
740 nsPoint
741 nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint,
742 const gfxMatrix& aFrameToCanvasTM,
743 nsPresContext* aPresContext)
744 {
745 NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(),
746 "Callers must not pass a singular matrix");
747 gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM;
748 canvasDevToFrameUserSpace.Invert();
749 gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) /
750 aPresContext->AppUnitsPerDevPixel();
751 gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt);
752 gfxPoint appPt = (userPt * aPresContext->AppUnitsPerCSSPixel()).Round();
753 userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
754 userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
755 // now guaranteed to be safe:
756 return nsPoint(nscoord(userPt.x), nscoord(userPt.y));
757 }
759 nsRect
760 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
761 const gfxMatrix& aMatrix,
762 nsPresContext* aPresContext)
763 {
764 gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
765 r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
766 return nsLayoutUtils::RoundGfxRectToAppRect(
767 aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
768 }
770 gfxIntSize
771 nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
772 bool *aResultOverflows)
773 {
774 gfxIntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
776 *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
777 surfaceSize.height != ceil(aSize.height);
779 if (!gfxASurface::CheckSurfaceSize(surfaceSize)) {
780 surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
781 surfaceSize.width);
782 surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
783 surfaceSize.height);
784 *aResultOverflows = true;
785 }
787 return surfaceSize;
788 }
790 bool
791 nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix,
792 float aRX, float aRY, float aRWidth, float aRHeight,
793 float aX, float aY)
794 {
795 gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
796 if (rect.IsEmpty() || aMatrix.IsSingular()) {
797 return false;
798 }
799 gfx::Matrix toRectSpace = aMatrix;
800 toRectSpace.Invert();
801 gfx::Point p = toRectSpace * gfx::Point(aX, aY);
802 return rect.x <= p.x && p.x <= rect.XMost() &&
803 rect.y <= p.y && p.y <= rect.YMost();
804 }
806 gfxRect
807 nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
808 float aX, float aY, float aWidth, float aHeight)
809 {
810 const nsStyleDisplay* disp = aFrame->StyleDisplay();
812 if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) {
813 NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO,
814 "We don't know about this type of clip.");
815 return gfxRect(aX, aY, aWidth, aHeight);
816 }
818 if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
819 disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
821 nsIntRect clipPxRect =
822 disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
823 gfxRect clipRect =
824 gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
826 if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) {
827 clipRect.width = aWidth - clipRect.X();
828 }
829 if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) {
830 clipRect.height = aHeight - clipRect.Y();
831 }
833 if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
834 clipRect.x = aX;
835 clipRect.width = aWidth;
836 }
837 if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
838 clipRect.y = aY;
839 clipRect.height = aHeight;
840 }
842 return clipRect;
843 }
844 return gfxRect(aX, aY, aWidth, aHeight);
845 }
847 void
848 nsSVGUtils::SetClipRect(gfxContext *aContext,
849 const gfxMatrix &aCTM,
850 const gfxRect &aRect)
851 {
852 if (aCTM.IsSingular())
853 return;
855 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
856 aContext->Multiply(aCTM);
857 aContext->Clip(aRect);
858 }
860 gfxRect
861 nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
862 {
863 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
864 aFrame = aFrame->GetParent();
865 }
866 gfxRect bbox;
867 nsISVGChildFrame *svg = do_QueryFrame(aFrame);
868 if (svg || aFrame->IsSVGText()) {
869 // It is possible to apply a gradient, pattern, clipping path, mask or
870 // filter to text. When one of these facilities is applied to text
871 // the bounding box is the entire text element in all
872 // cases.
873 if (aFrame->IsSVGText()) {
874 nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
875 if (ancestor && ancestor->IsSVGText()) {
876 while (ancestor->GetType() != nsGkAtoms::svgTextFrame) {
877 ancestor = ancestor->GetParent();
878 }
879 }
880 svg = do_QueryFrame(ancestor);
881 }
882 nsIContent* content = aFrame->GetContent();
883 if (content->IsSVG() &&
884 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
885 return bbox;
886 }
887 gfxMatrix matrix;
888 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
889 // The spec says getBBox "Returns the tight bounding box in *current user
890 // space*". So we should really be doing this for all elements, but that
891 // needs investigation to check that we won't break too much content.
892 // NOTE: When changing this to apply to other frame types, make sure to
893 // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
894 NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
895 nsSVGElement *element = static_cast<nsSVGElement*>(content);
896 matrix = element->PrependLocalTransformsTo(matrix,
897 nsSVGElement::eChildToUserSpace);
898 }
899 return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
900 }
901 return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
902 }
904 gfxPoint
905 nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
906 {
907 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
908 // The user space for non-SVG frames is defined as the bounding box of the
909 // frame's border-box rects over all continuations.
910 return gfxPoint();
911 }
913 // Leaf frames apply their own offset inside their user space.
914 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
915 aFrame->IsSVGText()) {
916 return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
917 nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
918 }
920 // For foreignObject frames, nsSVGUtils::GetBBox applies their local
921 // transform, so we need to do the same here.
922 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
923 gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
924 PrependLocalTransformsTo(gfxMatrix(),
925 nsSVGElement::eChildToUserSpace);
926 NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
927 return transform.GetTranslation();
928 }
930 return gfxPoint();
931 }
933 gfxRect
934 nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
935 const gfxRect &aBBox, nsIFrame *aFrame)
936 {
937 float x, y, width, height;
938 if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
939 x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]);
940 y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]);
941 width = ObjectSpace(aBBox, &aXYWH[2]);
942 height = ObjectSpace(aBBox, &aXYWH[3]);
943 } else {
944 x = UserSpace(aFrame, &aXYWH[0]);
945 y = UserSpace(aFrame, &aXYWH[1]);
946 width = UserSpace(aFrame, &aXYWH[2]);
947 height = UserSpace(aFrame, &aXYWH[3]);
948 }
949 return gfxRect(x, y, width, height);
950 }
952 bool
953 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
954 {
955 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
956 return false;
957 }
958 nsIAtom *type = aFrame->GetType();
959 if (type != nsGkAtoms::svgImageFrame &&
960 type != nsGkAtoms::svgPathGeometryFrame) {
961 return false;
962 }
963 if (aFrame->StyleSVGReset()->HasFilters()) {
964 return false;
965 }
966 // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
967 if (type == nsGkAtoms::svgImageFrame) {
968 return true;
969 }
970 const nsStyleSVG *style = aFrame->StyleSVG();
971 if (style->HasMarker()) {
972 return false;
973 }
974 if (!style->HasFill() || !HasStroke(aFrame)) {
975 return true;
976 }
977 return false;
978 }
980 gfxMatrix
981 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
982 nsSVGEnum *aUnits,
983 nsIFrame *aFrame)
984 {
985 if (aFrame &&
986 aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
987 gfxRect bbox = GetBBox(aFrame);
988 return gfxMatrix().Scale(bbox.Width(), bbox.Height()) *
989 gfxMatrix().Translate(gfxPoint(bbox.X(), bbox.Y())) *
990 aMatrix;
991 }
992 return aMatrix;
993 }
995 nsIFrame*
996 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
997 {
998 for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
999 ancestorFrame = ancestorFrame->GetParent()) {
1000 if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
1001 return ancestorFrame;
1002 }
1003 }
1004 return nullptr;
1005 }
1007 gfxMatrix
1008 nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame)
1009 {
1010 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
1011 aFrame = aFrame->GetParent();
1012 }
1014 if (aFrame->StyleSVGReset()->mVectorEffect ==
1015 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
1017 nsIContent *content = aFrame->GetContent();
1018 NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
1020 // a non-scaling stroke is in the screen co-ordinate
1021 // space rather so we need to invert the transform
1022 // to the screen co-ordinate space to get there.
1023 // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
1024 gfx::Matrix transform = SVGContentUtils::GetCTM(
1025 static_cast<nsSVGElement*>(content), true);
1026 if (!transform.IsSingular()) {
1027 transform.Invert();
1028 return ThebesMatrix(transform);
1029 }
1030 }
1031 return gfxMatrix();
1032 }
1034 // The logic here comes from _cairo_stroke_style_max_distance_from_path
1035 static gfxRect
1036 PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1037 nsIFrame* aFrame,
1038 double aStyleExpansionFactor,
1039 const gfxMatrix& aMatrix)
1040 {
1041 double style_expansion =
1042 aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
1044 gfxMatrix matrix = aMatrix;
1045 matrix.Multiply(nsSVGUtils::GetStrokeTransform(aFrame));
1047 double dx = style_expansion * (fabs(matrix.xx) + fabs(matrix.xy));
1048 double dy = style_expansion * (fabs(matrix.yy) + fabs(matrix.yx));
1050 gfxRect strokeExtents = aPathExtents;
1051 strokeExtents.Inflate(dx, dy);
1052 return strokeExtents;
1053 }
1055 /*static*/ gfxRect
1056 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1057 nsTextFrame* aFrame,
1058 const gfxMatrix& aMatrix)
1059 {
1060 NS_ASSERTION(aFrame->IsSVGText(), "expected an nsTextFrame for SVG text");
1061 return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
1062 }
1064 /*static*/ gfxRect
1065 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
1066 nsSVGPathGeometryFrame* aFrame,
1067 const gfxMatrix& aMatrix)
1068 {
1069 double styleExpansionFactor = 0.5;
1071 if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
1072 const nsStyleSVG* style = aFrame->StyleSVG();
1074 if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
1075 styleExpansionFactor = M_SQRT1_2;
1076 }
1078 if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
1079 styleExpansionFactor < style->mStrokeMiterlimit &&
1080 aFrame->GetContent()->Tag() != nsGkAtoms::line) {
1081 styleExpansionFactor = style->mStrokeMiterlimit;
1082 }
1083 }
1085 return ::PathExtentsToMaxStrokeExtents(aPathExtents,
1086 aFrame,
1087 styleExpansionFactor,
1088 aMatrix);
1089 }
1091 // ----------------------------------------------------------------------
1093 /* static */ nscolor
1094 nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
1095 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
1096 {
1097 const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
1098 nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
1099 bool isServer = paint.mType == eStyleSVGPaintType_Server ||
1100 paint.mType == eStyleSVGPaintType_ContextFill ||
1101 paint.mType == eStyleSVGPaintType_ContextStroke;
1102 nscolor color = isServer ? paint.mFallbackColor : paint.mPaint.mColor;
1103 if (styleIfVisited) {
1104 const nsStyleSVGPaint &paintIfVisited =
1105 styleIfVisited->StyleSVG()->*aFillOrStroke;
1106 // To prevent Web content from detecting if a user has visited a URL
1107 // (via URL loading triggered by paint servers or performance
1108 // differences between paint servers or between a paint server and a
1109 // color), we do not allow whether links are visited to change which
1110 // paint server is used or switch between paint servers and simple
1111 // colors. A :visited style may only override a simple color with
1112 // another simple color.
1113 if (paintIfVisited.mType == eStyleSVGPaintType_Color &&
1114 paint.mType == eStyleSVGPaintType_Color) {
1115 nscolor colors[2] = { color, paintIfVisited.mPaint.mColor };
1116 return nsStyleContext::CombineVisitedColors(
1117 colors, aStyleContext->RelevantLinkVisited());
1118 }
1119 }
1120 return color;
1121 }
1123 static void
1124 SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
1125 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
1126 float aOpacity)
1127 {
1128 nscolor color = nsSVGUtils::GetFallbackOrPaintColor(
1129 aContext, aStyleContext, aFillOrStroke);
1131 aContext->SetColor(gfxRGBA(NS_GET_R(color)/255.0,
1132 NS_GET_G(color)/255.0,
1133 NS_GET_B(color)/255.0,
1134 NS_GET_A(color)/255.0 * aOpacity));
1135 }
1137 static float
1138 MaybeOptimizeOpacity(nsIFrame *aFrame, float aFillOrStrokeOpacity)
1139 {
1140 float opacity = aFrame->StyleDisplay()->mOpacity;
1141 if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
1142 return aFillOrStrokeOpacity * opacity;
1143 }
1144 return aFillOrStrokeOpacity;
1145 }
1147 /* static */ bool
1148 nsSVGUtils::SetupContextPaint(gfxContext *aContext,
1149 gfxTextContextPaint *aContextPaint,
1150 const nsStyleSVGPaint &aPaint,
1151 float aOpacity)
1152 {
1153 nsRefPtr<gfxPattern> pattern;
1155 if (!aContextPaint) {
1156 return false;
1157 }
1159 switch (aPaint.mType) {
1160 case eStyleSVGPaintType_ContextFill:
1161 pattern = aContextPaint->GetFillPattern(aOpacity, aContext->CurrentMatrix());
1162 break;
1163 case eStyleSVGPaintType_ContextStroke:
1164 pattern = aContextPaint->GetStrokePattern(aOpacity, aContext->CurrentMatrix());
1165 break;
1166 default:
1167 return false;
1168 }
1170 if (!pattern) {
1171 return false;
1172 }
1174 aContext->SetPattern(pattern);
1176 return true;
1177 }
1179 bool
1180 nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext,
1181 gfxTextContextPaint *aContextPaint)
1182 {
1183 const nsStyleSVG* style = aFrame->StyleSVG();
1184 if (style->mFill.mType == eStyleSVGPaintType_None)
1185 return false;
1187 if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
1188 aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
1189 else
1190 aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
1192 float opacity = MaybeOptimizeOpacity(aFrame,
1193 GetOpacity(style->mFillOpacitySource,
1194 style->mFillOpacity,
1195 aContextPaint));
1196 nsSVGPaintServerFrame *ps =
1197 nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty());
1198 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity))
1199 return true;
1201 if (SetupContextPaint(aContext, aContextPaint, style->mFill, opacity)) {
1202 return true;
1203 }
1205 // On failure, use the fallback colour in case we have an
1206 // objectBoundingBox where the width or height of the object is zero.
1207 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
1208 SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
1209 &nsStyleSVG::mFill, opacity);
1211 return true;
1212 }
1214 bool
1215 nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext,
1216 gfxTextContextPaint *aContextPaint)
1217 {
1218 const nsStyleSVG* style = aFrame->StyleSVG();
1219 if (style->mStroke.mType == eStyleSVGPaintType_None)
1220 return false;
1222 float opacity = MaybeOptimizeOpacity(aFrame,
1223 GetOpacity(style->mStrokeOpacitySource,
1224 style->mStrokeOpacity,
1225 aContextPaint));
1227 nsSVGPaintServerFrame *ps =
1228 nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty());
1229 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity))
1230 return true;
1232 if (SetupContextPaint(aContext, aContextPaint, style->mStroke, opacity)) {
1233 return true;
1234 }
1236 // On failure, use the fallback colour in case we have an
1237 // objectBoundingBox where the width or height of the object is zero.
1238 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
1239 SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
1240 &nsStyleSVG::mStroke, opacity);
1242 return true;
1243 }
1245 /* static */ float
1246 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
1247 const float& aOpacity,
1248 gfxTextContextPaint *aOuterContextPaint)
1249 {
1250 float opacity = 1.0f;
1251 switch (aOpacityType) {
1252 case eStyleSVGOpacitySource_Normal:
1253 opacity = aOpacity;
1254 break;
1255 case eStyleSVGOpacitySource_ContextFillOpacity:
1256 if (aOuterContextPaint) {
1257 opacity = aOuterContextPaint->GetFillOpacity();
1258 } else {
1259 NS_WARNING("context-fill-opacity used outside of an SVG glyph");
1260 }
1261 break;
1262 case eStyleSVGOpacitySource_ContextStrokeOpacity:
1263 if (aOuterContextPaint) {
1264 opacity = aOuterContextPaint->GetStrokeOpacity();
1265 } else {
1266 NS_WARNING("context-stroke-opacity used outside of an SVG glyph");
1267 }
1268 break;
1269 default:
1270 NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
1271 }
1272 return opacity;
1273 }
1275 bool
1276 nsSVGUtils::HasStroke(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
1277 {
1278 const nsStyleSVG *style = aFrame->StyleSVG();
1279 return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
1280 }
1282 float
1283 nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
1284 {
1285 const nsStyleSVG *style = aFrame->StyleSVG();
1286 if (aContextPaint && style->mStrokeWidthFromObject) {
1287 return aContextPaint->GetStrokeWidth();
1288 }
1290 nsIContent* content = aFrame->GetContent();
1291 if (content->IsNodeOfType(nsINode::eTEXT)) {
1292 content = content->GetParent();
1293 }
1295 nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
1297 return SVGContentUtils::CoordToFloat(aFrame->PresContext(), ctx,
1298 style->mStrokeWidth);
1299 }
1301 void
1302 nsSVGUtils::SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame,
1303 gfxContext *aContext,
1304 gfxTextContextPaint *aContextPaint)
1305 {
1306 float width = GetStrokeWidth(aFrame, aContextPaint);
1307 if (width <= 0)
1308 return;
1309 aContext->SetLineWidth(width);
1311 // Apply any stroke-specific transform
1312 gfxMatrix strokeTransform = GetStrokeTransform(aFrame);
1313 if (!strokeTransform.IsIdentity()) {
1314 aContext->Multiply(strokeTransform);
1315 }
1317 const nsStyleSVG* style = aFrame->StyleSVG();
1319 switch (style->mStrokeLinecap) {
1320 case NS_STYLE_STROKE_LINECAP_BUTT:
1321 aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
1322 break;
1323 case NS_STYLE_STROKE_LINECAP_ROUND:
1324 aContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
1325 break;
1326 case NS_STYLE_STROKE_LINECAP_SQUARE:
1327 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
1328 break;
1329 }
1331 aContext->SetMiterLimit(style->mStrokeMiterlimit);
1333 switch (style->mStrokeLinejoin) {
1334 case NS_STYLE_STROKE_LINEJOIN_MITER:
1335 aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
1336 break;
1337 case NS_STYLE_STROKE_LINEJOIN_ROUND:
1338 aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND);
1339 break;
1340 case NS_STYLE_STROKE_LINEJOIN_BEVEL:
1341 aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
1342 break;
1343 }
1344 }
1346 static bool
1347 GetStrokeDashData(nsIFrame* aFrame,
1348 FallibleTArray<gfxFloat>& aDashes,
1349 gfxFloat* aDashOffset,
1350 gfxTextContextPaint *aContextPaint)
1351 {
1352 const nsStyleSVG* style = aFrame->StyleSVG();
1353 nsPresContext *presContext = aFrame->PresContext();
1354 nsIContent *content = aFrame->GetContent();
1355 nsSVGElement *ctx = static_cast<nsSVGElement*>
1356 (content->IsNodeOfType(nsINode::eTEXT) ?
1357 content->GetParent() : content);
1359 gfxFloat totalLength = 0.0;
1360 if (aContextPaint && style->mStrokeDasharrayFromObject) {
1361 aDashes = aContextPaint->GetStrokeDashArray();
1363 for (uint32_t i = 0; i < aDashes.Length(); i++) {
1364 if (aDashes[i] < 0.0) {
1365 return false;
1366 }
1367 totalLength += aDashes[i];
1368 }
1370 } else {
1371 uint32_t count = style->mStrokeDasharrayLength;
1372 if (!count || !aDashes.SetLength(count)) {
1373 return false;
1374 }
1376 gfxFloat pathScale = 1.0;
1378 if (content->Tag() == nsGkAtoms::path) {
1379 pathScale = static_cast<SVGPathElement*>(content)->
1380 GetPathLengthScale(SVGPathElement::eForStroking);
1381 if (pathScale <= 0) {
1382 return false;
1383 }
1384 }
1386 const nsStyleCoord *dasharray = style->mStrokeDasharray;
1388 for (uint32_t i = 0; i < count; i++) {
1389 aDashes[i] = SVGContentUtils::CoordToFloat(presContext,
1390 ctx,
1391 dasharray[i]) * pathScale;
1392 if (aDashes[i] < 0.0) {
1393 return false;
1394 }
1395 totalLength += aDashes[i];
1396 }
1397 }
1399 if (aContextPaint && style->mStrokeDashoffsetFromObject) {
1400 *aDashOffset = aContextPaint->GetStrokeDashOffset();
1401 } else {
1402 *aDashOffset = SVGContentUtils::CoordToFloat(presContext,
1403 ctx,
1404 style->mStrokeDashoffset);
1405 }
1407 return (totalLength > 0.0);
1408 }
1410 void
1411 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
1412 gfxTextContextPaint *aContextPaint)
1413 {
1414 SetupCairoStrokeBBoxGeometry(aFrame, aContext, aContextPaint);
1416 AutoFallibleTArray<gfxFloat, 10> dashes;
1417 gfxFloat dashOffset;
1418 if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) {
1419 aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
1420 }
1421 }
1423 uint16_t
1424 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
1425 {
1426 uint16_t flags = 0;
1428 switch(aFrame->StyleVisibility()->mPointerEvents) {
1429 case NS_STYLE_POINTER_EVENTS_NONE:
1430 break;
1431 case NS_STYLE_POINTER_EVENTS_AUTO:
1432 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
1433 if (aFrame->StyleVisibility()->IsVisible()) {
1434 if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
1435 flags |= SVG_HIT_TEST_FILL;
1436 if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
1437 flags |= SVG_HIT_TEST_STROKE;
1438 if (aFrame->StyleSVG()->mStrokeOpacity > 0)
1439 flags |= SVG_HIT_TEST_CHECK_MRECT;
1440 }
1441 break;
1442 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
1443 if (aFrame->StyleVisibility()->IsVisible()) {
1444 flags |= SVG_HIT_TEST_FILL;
1445 }
1446 break;
1447 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
1448 if (aFrame->StyleVisibility()->IsVisible()) {
1449 flags |= SVG_HIT_TEST_STROKE;
1450 }
1451 break;
1452 case NS_STYLE_POINTER_EVENTS_VISIBLE:
1453 if (aFrame->StyleVisibility()->IsVisible()) {
1454 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
1455 }
1456 break;
1457 case NS_STYLE_POINTER_EVENTS_PAINTED:
1458 if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
1459 flags |= SVG_HIT_TEST_FILL;
1460 if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
1461 flags |= SVG_HIT_TEST_STROKE;
1462 if (aFrame->StyleSVG()->mStrokeOpacity)
1463 flags |= SVG_HIT_TEST_CHECK_MRECT;
1464 break;
1465 case NS_STYLE_POINTER_EVENTS_FILL:
1466 flags |= SVG_HIT_TEST_FILL;
1467 break;
1468 case NS_STYLE_POINTER_EVENTS_STROKE:
1469 flags |= SVG_HIT_TEST_STROKE;
1470 break;
1471 case NS_STYLE_POINTER_EVENTS_ALL:
1472 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
1473 break;
1474 default:
1475 NS_ERROR("not reached");
1476 break;
1477 }
1479 return flags;
1480 }
1482 bool
1483 nsSVGUtils::SetupCairoStroke(nsIFrame* aFrame, gfxContext* aContext,
1484 gfxTextContextPaint *aContextPaint)
1485 {
1486 if (!HasStroke(aFrame, aContextPaint)) {
1487 return false;
1488 }
1489 SetupCairoStrokeGeometry(aFrame, aContext, aContextPaint);
1491 return SetupCairoStrokePaint(aFrame, aContext, aContextPaint);
1492 }
1494 bool
1495 nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext,
1496 DrawMode aDrawMode,
1497 gfxTextContextPaint* aContextPaint)
1498 {
1499 nsIFrame* frame = aElement->GetPrimaryFrame();
1500 nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
1501 if (!svgFrame) {
1502 return false;
1503 }
1504 nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
1505 context->Init(frame->PresContext()->DeviceContext(), aContext);
1506 context->AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint,
1507 nullptr);
1508 svgFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
1509 nsresult rv = svgFrame->PaintSVG(context, nullptr, frame);
1510 return NS_SUCCEEDED(rv);
1511 }
1513 bool
1514 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
1515 const gfxMatrix& aSVGToAppSpace,
1516 gfxRect* aResult)
1517 {
1518 nsIFrame* frame = aElement->GetPrimaryFrame();
1519 nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
1520 if (!svgFrame) {
1521 return false;
1522 }
1524 gfxMatrix transform(aSVGToAppSpace);
1525 nsIContent* content = frame->GetContent();
1526 if (content->IsSVG()) {
1527 transform = static_cast<nsSVGElement*>(content)->
1528 PrependLocalTransformsTo(aSVGToAppSpace);
1529 }
1531 *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform),
1532 nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
1533 nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
1534 nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect();
1535 return true;
1536 }
1538 nsRect
1539 nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect,
1540 const gfxMatrix &aToCanvas,
1541 const nsPresContext *presContext)
1542 {
1543 return nsLayoutUtils::RoundGfxRectToAppRect(
1544 aToCanvas.TransformBounds(aUserspaceRect),
1545 presContext->AppUnitsPerDevPixel());
1546 }