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 #include "nsSVGPathGeometryFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxContext.h"
11 #include "gfxPlatform.h"
12 #include "gfxSVGGlyphs.h"
13 #include "nsDisplayList.h"
14 #include "nsGkAtoms.h"
15 #include "nsRenderingContext.h"
16 #include "nsSVGEffects.h"
17 #include "nsSVGIntegrationUtils.h"
18 #include "nsSVGMarkerFrame.h"
19 #include "nsSVGPathGeometryElement.h"
20 #include "nsSVGUtils.h"
21 #include "mozilla/ArrayUtils.h"
22 #include "SVGAnimatedTransformList.h"
23 #include "SVGGraphicsElement.h"
25 using namespace mozilla;
26 using namespace mozilla::gfx;
28 //----------------------------------------------------------------------
29 // Implementation
31 nsIFrame*
32 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
33 nsStyleContext* aContext)
34 {
35 return new (aPresShell) nsSVGPathGeometryFrame(aContext);
36 }
38 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPathGeometryFrame)
40 //----------------------------------------------------------------------
41 // nsQueryFrame methods
43 NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame)
44 NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
45 NS_QUERYFRAME_ENTRY(nsSVGPathGeometryFrame)
46 NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase)
48 //----------------------------------------------------------------------
49 // Display list item:
51 class nsDisplaySVGPathGeometry : public nsDisplayItem {
52 public:
53 nsDisplaySVGPathGeometry(nsDisplayListBuilder* aBuilder,
54 nsSVGPathGeometryFrame* aFrame)
55 : nsDisplayItem(aBuilder, aFrame)
56 {
57 MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry);
58 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
59 }
60 #ifdef NS_BUILD_REFCNT_LOGGING
61 virtual ~nsDisplaySVGPathGeometry() {
62 MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry);
63 }
64 #endif
66 NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY)
68 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
69 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
70 virtual void Paint(nsDisplayListBuilder* aBuilder,
71 nsRenderingContext* aCtx);
72 };
74 void
75 nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
76 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
77 {
78 nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame);
79 nsPoint pointRelativeToReferenceFrame = aRect.Center();
80 // ToReferenceFrame() includes frame->GetPosition(), our user space position.
81 nsPoint userSpacePt = pointRelativeToReferenceFrame -
82 (ToReferenceFrame() - frame->GetPosition());
83 if (frame->GetFrameForPoint(userSpacePt)) {
84 aOutFrames->AppendElement(frame);
85 }
86 }
88 void
89 nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder* aBuilder,
90 nsRenderingContext* aCtx)
91 {
92 // ToReferenceFrame includes our mRect offset, but painting takes
93 // account of that too. To avoid double counting, we subtract that
94 // here.
95 nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
97 aCtx->PushState();
98 aCtx->Translate(offset);
99 static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
100 aCtx->PopState();
101 }
103 //----------------------------------------------------------------------
104 // nsIFrame methods
106 void
107 nsSVGPathGeometryFrame::Init(nsIContent* aContent,
108 nsIFrame* aParent,
109 nsIFrame* aPrevInFlow)
110 {
111 AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
112 nsSVGPathGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
113 }
115 nsresult
116 nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID,
117 nsIAtom* aAttribute,
118 int32_t aModType)
119 {
120 // We don't invalidate for transform changes (the layers code does that).
121 // Also note that SVGTransformableElement::GetAttributeChangeHint will
122 // return nsChangeHint_UpdateOverflow for "transform" attribute changes
123 // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
125 if (aNameSpaceID == kNameSpaceID_None &&
126 (static_cast<nsSVGPathGeometryElement*>
127 (mContent)->AttributeDefinesGeometry(aAttribute))) {
128 nsSVGEffects::InvalidateRenderingObservers(this);
129 nsSVGUtils::ScheduleReflowSVG(this);
130 }
131 return NS_OK;
132 }
134 /* virtual */ void
135 nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
136 {
137 nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext);
139 if (aOldStyleContext) {
140 float oldOpacity = aOldStyleContext->PeekStyleDisplay()->mOpacity;
141 float newOpacity = StyleDisplay()->mOpacity;
142 if (newOpacity != oldOpacity &&
143 nsSVGUtils::CanOptimizeOpacity(this)) {
144 // nsIFrame::BuildDisplayListForStackingContext() is not going to create an
145 // nsDisplayOpacity display list item, so DLBI won't invalidate for us.
146 InvalidateFrame();
147 }
148 }
149 }
151 nsIAtom *
152 nsSVGPathGeometryFrame::GetType() const
153 {
154 return nsGkAtoms::svgPathGeometryFrame;
155 }
157 bool
158 nsSVGPathGeometryFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
159 gfx::Matrix *aFromParentTransform) const
160 {
161 bool foundTransform = false;
163 // Check if our parent has children-only transforms:
164 nsIFrame *parent = GetParent();
165 if (parent &&
166 parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
167 foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
168 HasChildrenOnlyTransform(aFromParentTransform);
169 }
171 nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
172 nsSVGAnimatedTransformList* transformList =
173 content->GetAnimatedTransformList();
174 if ((transformList && transformList->HasTransform()) ||
175 content->GetAnimateMotionTransform()) {
176 if (aOwnTransform) {
177 *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
178 nsSVGElement::eUserSpaceToParent));
179 }
180 foundTransform = true;
181 }
182 return foundTransform;
183 }
185 void
186 nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
187 const nsRect& aDirtyRect,
188 const nsDisplayListSet& aLists)
189 {
190 if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
191 return;
192 }
193 aLists.Content()->AppendNewToTop(
194 new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this));
195 }
197 //----------------------------------------------------------------------
198 // nsISVGChildFrame methods
200 nsresult
201 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
202 const nsIntRect *aDirtyRect,
203 nsIFrame* aTransformRoot)
204 {
205 if (!StyleVisibility()->IsVisible())
206 return NS_OK;
208 uint32_t paintOrder = StyleSVG()->mPaintOrder;
209 if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
210 Render(aContext, eRenderFill | eRenderStroke, aTransformRoot);
211 PaintMarkers(aContext);
212 } else {
213 while (paintOrder) {
214 uint32_t component =
215 paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
216 switch (component) {
217 case NS_STYLE_PAINT_ORDER_FILL:
218 Render(aContext, eRenderFill, aTransformRoot);
219 break;
220 case NS_STYLE_PAINT_ORDER_STROKE:
221 Render(aContext, eRenderStroke, aTransformRoot);
222 break;
223 case NS_STYLE_PAINT_ORDER_MARKERS:
224 PaintMarkers(aContext);
225 break;
226 }
227 paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
228 }
229 }
231 return NS_OK;
232 }
234 nsIFrame*
235 nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
236 {
237 gfxMatrix canvasTM = GetCanvasTM(FOR_HIT_TESTING);
238 if (canvasTM.IsSingular()) {
239 return nullptr;
240 }
241 uint16_t fillRule, hitTestFlags;
242 if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
243 hitTestFlags = SVG_HIT_TEST_FILL;
244 fillRule = StyleSVG()->mClipRule;
245 } else {
246 hitTestFlags = GetHitTestFlags();
247 // XXX once bug 614732 is fixed, aPoint won't need any conversion in order
248 // to compare it with mRect.
249 nsPoint point =
250 nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext());
251 if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) &&
252 !mRect.Contains(point)))
253 return nullptr;
254 fillRule = StyleSVG()->mFillRule;
255 }
257 bool isHit = false;
259 nsRefPtr<gfxContext> tmpCtx =
260 new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
262 GeneratePath(tmpCtx, ToMatrix(canvasTM));
263 gfxPoint userSpacePoint =
264 tmpCtx->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
265 PresContext()->AppUnitsToGfxUnits(aPoint.y)));
267 if (fillRule == NS_STYLE_FILL_RULE_EVENODD)
268 tmpCtx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
269 else
270 tmpCtx->SetFillRule(gfxContext::FILL_RULE_WINDING);
272 if (hitTestFlags & SVG_HIT_TEST_FILL)
273 isHit = tmpCtx->PointInFill(userSpacePoint);
274 if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
275 nsSVGUtils::SetupCairoStrokeGeometry(this, tmpCtx);
276 // tmpCtx's matrix may have transformed by SetupCairoStrokeGeometry
277 // if there is a non-scaling stroke. We need to transform userSpacePoint
278 // so that everything is using the same co-ordinate system.
279 userSpacePoint =
280 nsSVGUtils::GetStrokeTransform(this).Invert().Transform(userSpacePoint);
281 isHit = tmpCtx->PointInStroke(userSpacePoint);
282 }
284 if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
285 return this;
287 return nullptr;
288 }
290 nsRect
291 nsSVGPathGeometryFrame::GetCoveredRegion()
292 {
293 return nsSVGUtils::TransformFrameRectToOuterSVG(
294 mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
295 }
297 void
298 nsSVGPathGeometryFrame::ReflowSVG()
299 {
300 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
301 "This call is probably a wasteful mistake");
303 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
304 "ReflowSVG mechanism not designed for this");
306 if (!nsSVGUtils::NeedsReflowSVG(this)) {
307 return;
308 }
310 uint32_t flags = nsSVGUtils::eBBoxIncludeFill |
311 nsSVGUtils::eBBoxIncludeStroke |
312 nsSVGUtils::eBBoxIncludeMarkers;
313 // Our "visual" overflow rect needs to be valid for building display lists
314 // for hit testing, which means that for certain values of 'pointer-events'
315 // it needs to include the geometry of the fill or stroke even when the fill/
316 // stroke don't actually render (e.g. when stroke="none" or
317 // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
318 uint16_t hitTestFlags = GetHitTestFlags();
319 if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
320 flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
321 }
322 if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
323 flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
324 }
326 // We'd like to just pass the identity matrix to GetBBoxContribution, but if
327 // this frame's user space size is _very_ large/small then the extents we
328 // obtain below might have overflowed or otherwise be broken. This would
329 // cause us to end up with a broken mRect and visual overflow rect and break
330 // painting of this frame. This is particularly noticeable if the transforms
331 // between us and our nsSVGOuterSVGFrame scale this frame to a reasonable
332 // size. To avoid this we sadly have to do extra work to account for the
333 // transforms between us and our nsSVGOuterSVGFrame, even though the
334 // overwhelming number of SVGs will never have this problem.
335 // XXX Will Azure eventually save us from having to do this?
336 gfxSize scaleFactors = GetCanvasTM(FOR_OUTERSVG_TM).ScaleFactors(true);
337 bool applyScaling = fabs(scaleFactors.width) >= 1e-6 &&
338 fabs(scaleFactors.height) >= 1e-6;
339 gfx::Matrix scaling;
340 if (applyScaling) {
341 scaling.Scale(scaleFactors.width, scaleFactors.height);
342 }
343 gfxRect extent = GetBBoxContribution(scaling, flags).ToThebesRect();
344 if (applyScaling) {
345 extent.Scale(1 / scaleFactors.width, 1 / scaleFactors.height);
346 }
347 mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
348 PresContext()->AppUnitsPerCSSPixel());
350 if (mState & NS_FRAME_FIRST_REFLOW) {
351 // Make sure we have our filter property (if any) before calling
352 // FinishAndStoreOverflow (subsequent filter changes are handled off
353 // nsChangeHint_UpdateEffects):
354 nsSVGEffects::UpdateEffects(this);
355 }
357 nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
358 nsOverflowAreas overflowAreas(overflow, overflow);
359 FinishAndStoreOverflow(overflowAreas, mRect.Size());
361 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
362 NS_FRAME_HAS_DIRTY_CHILDREN);
364 // Invalidate, but only if this is not our first reflow (since if it is our
365 // first reflow then we haven't had our first paint yet).
366 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
367 InvalidateFrame();
368 }
369 }
371 void
372 nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags)
373 {
374 NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
375 "Invalidation logic may need adjusting");
377 // Changes to our ancestors may affect how we render when we are rendered as
378 // part of our ancestor (specifically, if our coordinate context changes size
379 // and we have percentage lengths defining our geometry, then we need to be
380 // reflowed). However, ancestor changes cannot affect how we render when we
381 // are rendered as part of any rendering observers that we may have.
382 // Therefore no need to notify rendering observers here.
384 // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls
385 // for the stroke properties examined below. Checking HasStroke() is not
386 // enough, since what we care about is whether we include the stroke in our
387 // overflow rects or not, and we sometimes deliberately include stroke
388 // when it's not visible. See the complexities of GetBBoxContribution.
390 if (aFlags & COORD_CONTEXT_CHANGED) {
391 // Stroke currently contributes to our mRect, which is why we have to take
392 // account of stroke-width here. Note that we do not need to take account
393 // of stroke-dashoffset since, although that can have a percentage value
394 // that is resolved against our coordinate context, it does not affect our
395 // mRect.
396 if (static_cast<nsSVGPathGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() ||
397 StyleSVG()->mStrokeWidth.HasPercent()) {
398 nsSVGUtils::ScheduleReflowSVG(this);
399 }
400 }
402 if ((aFlags & TRANSFORM_CHANGED) &&
403 StyleSVGReset()->mVectorEffect ==
404 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
405 // Stroke currently contributes to our mRect, and our stroke depends on
406 // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
407 nsSVGUtils::ScheduleReflowSVG(this);
408 }
409 }
411 SVGBBox
412 nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
413 uint32_t aFlags)
414 {
415 SVGBBox bbox;
417 if (aToBBoxUserspace.IsSingular()) {
418 // XXX ReportToConsole
419 return bbox;
420 }
422 nsRefPtr<gfxContext> tmpCtx =
423 new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
425 GeneratePath(tmpCtx, aToBBoxUserspace);
426 tmpCtx->IdentityMatrix();
428 // Be careful when replacing the following logic to get the fill and stroke
429 // extents independently (instead of computing the stroke extents from the
430 // path extents). You may think that you can just use the stroke extents if
431 // there is both a fill and a stroke. In reality it's necessary to calculate
432 // both the fill and stroke extents, and take the union of the two. There are
433 // two reasons for this:
434 //
435 // # Due to stroke dashing, in certain cases the fill extents could actually
436 // extend outside the stroke extents.
437 // # If the stroke is very thin, cairo won't paint any stroke, and so the
438 // stroke bounds that it will return will be empty.
440 gfxRect pathExtents = tmpCtx->GetUserPathExtent();
442 // Account for fill:
443 if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
444 ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
445 StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
446 bbox = pathExtents;
447 }
449 // Account for stroke:
450 if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
451 ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
452 nsSVGUtils::HasStroke(this))) {
453 // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for
454 // device space extents. Instead we approximate the stroke extents from
455 // pathExtents using PathExtentsToMaxStrokeExtents.
456 if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) {
457 // We have a zero length path, but it may still have non-empty stroke
458 // bounds depending on the value of stroke-linecap. We need to fix up
459 // pathExtents before it can be used with PathExtentsToMaxStrokeExtents
460 // though, because if pathExtents is empty, its position will not have
461 // been set. Happily we can use tmpCtx->GetUserStrokeExtent() to find
462 // the center point of the extents even though it gets the extents wrong.
463 nsSVGUtils::SetupCairoStrokeBBoxGeometry(this, tmpCtx);
464 pathExtents.MoveTo(tmpCtx->GetUserStrokeExtent().Center());
465 pathExtents.SizeTo(0, 0);
466 }
467 bbox.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
468 this,
469 ThebesMatrix(aToBBoxUserspace)));
470 }
472 // Account for markers:
473 if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 &&
474 static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
476 float strokeWidth = nsSVGUtils::GetStrokeWidth(this);
477 MarkerProperties properties = GetMarkerProperties(this);
479 if (properties.MarkersExist()) {
480 nsTArray<nsSVGMark> marks;
481 static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
482 uint32_t num = marks.Length();
484 // These are in the same order as the nsSVGMark::Type constants.
485 nsSVGMarkerFrame* markerFrames[] = {
486 properties.GetMarkerStartFrame(),
487 properties.GetMarkerMidFrame(),
488 properties.GetMarkerEndFrame(),
489 };
490 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
492 for (uint32_t i = 0; i < num; i++) {
493 nsSVGMark& mark = marks[i];
494 nsSVGMarkerFrame* frame = markerFrames[mark.type];
495 if (frame) {
496 SVGBBox mbbox =
497 frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
498 &marks[i], strokeWidth);
499 bbox.UnionEdges(mbbox);
500 }
501 }
502 }
503 }
505 return bbox;
506 }
508 //----------------------------------------------------------------------
509 // nsSVGPathGeometryFrame methods:
511 gfxMatrix
512 nsSVGPathGeometryFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
513 {
514 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
515 if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
516 (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
517 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
518 }
519 }
521 NS_ASSERTION(mParent, "null parent");
523 nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
524 dom::SVGGraphicsElement *content = static_cast<dom::SVGGraphicsElement*>(mContent);
526 return content->PrependLocalTransformsTo(
527 this == aTransformRoot ? gfxMatrix() :
528 parent->GetCanvasTM(aFor, aTransformRoot));
529 }
531 nsSVGPathGeometryFrame::MarkerProperties
532 nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame *aFrame)
533 {
534 NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
536 MarkerProperties result;
537 const nsStyleSVG *style = aFrame->StyleSVG();
538 result.mMarkerStart =
539 nsSVGEffects::GetMarkerProperty(style->mMarkerStart, aFrame,
540 nsSVGEffects::MarkerBeginProperty());
541 result.mMarkerMid =
542 nsSVGEffects::GetMarkerProperty(style->mMarkerMid, aFrame,
543 nsSVGEffects::MarkerMiddleProperty());
544 result.mMarkerEnd =
545 nsSVGEffects::GetMarkerProperty(style->mMarkerEnd, aFrame,
546 nsSVGEffects::MarkerEndProperty());
547 return result;
548 }
550 nsSVGMarkerFrame *
551 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
552 {
553 if (!mMarkerStart)
554 return nullptr;
555 return static_cast<nsSVGMarkerFrame *>
556 (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
557 }
559 nsSVGMarkerFrame *
560 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
561 {
562 if (!mMarkerMid)
563 return nullptr;
564 return static_cast<nsSVGMarkerFrame *>
565 (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
566 }
568 nsSVGMarkerFrame *
569 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
570 {
571 if (!mMarkerEnd)
572 return nullptr;
573 return static_cast<nsSVGMarkerFrame *>
574 (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
575 }
577 void
578 nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext,
579 uint32_t aRenderComponents,
580 nsIFrame* aTransformRoot)
581 {
582 gfxContext *gfx = aContext->ThebesContext();
584 uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext);
586 switch (StyleSVG()->mShapeRendering) {
587 case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
588 case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
589 gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
590 break;
591 default:
592 gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
593 break;
594 }
596 if (renderMode != SVGAutoRenderState::NORMAL) {
597 NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::CLIP ||
598 renderMode == SVGAutoRenderState::CLIP_MASK,
599 "Unknown render mode");
601 // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't
602 // use the path we generate here until further up the call stack when
603 // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for
604 // Moz2D which emits paths in user space (unlike cairo which emits paths in
605 // device space). gfxContext has hacks to deal with code changing the
606 // transform then using the current path when it is backed by Moz2D, but
607 // Moz2D itself does not since that would fundamentally go against its API.
608 // Therefore we do not want to Save()/Restore() the gfxContext here in the
609 // SVGAutoRenderState::CLIP case since that would block us from killing off
610 // gfxContext and using Moz2D directly. Not bothering to Save()/Restore()
611 // is actually okay, since we know that doesn't matter in the
612 // SVGAutoRenderState::CLIP case (at least for the current implementation).
613 gfxContextMatrixAutoSaveRestore autoSaveRestore;
614 // For now revent back to doing the save even for CLIP to fix bug 959128.
615 // Undo in bug 987193.
616 //if (renderMode != SVGAutoRenderState::CLIP) {
617 autoSaveRestore.SetContext(gfx);
618 //}
620 GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
622 // We used to call gfx->Restore() here, since for the
623 // SVGAutoRenderState::CLIP case it is important to leave the fill rule
624 // that we set below untouched so that the value is still set when return
625 // to gfxContext::Clip() further up the call stack. Since we no longer
626 // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to
627 // worry that autoSaveRestore will delay the Restore() call for the
628 // CLIP_MASK case until we exit this function.
630 gfxContext::FillRule oldFillRull = gfx->CurrentFillRule();
632 if (StyleSVG()->mClipRule == NS_STYLE_FILL_RULE_EVENODD)
633 gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
634 else
635 gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
637 if (renderMode == SVGAutoRenderState::CLIP_MASK) {
638 gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
639 gfx->Fill();
640 gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK
641 gfx->NewPath();
642 }
644 return;
645 }
647 gfxContextAutoSaveRestore autoSaveRestore(gfx);
649 GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
651 gfxTextContextPaint *contextPaint =
652 (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
654 if ((aRenderComponents & eRenderFill) &&
655 nsSVGUtils::SetupCairoFillPaint(this, gfx, contextPaint)) {
656 gfx->Fill();
657 }
659 if ((aRenderComponents & eRenderStroke) &&
660 nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) {
661 gfx->Stroke();
662 }
664 gfx->NewPath();
665 }
667 void
668 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
669 const Matrix &aTransform)
670 {
671 if (aTransform.IsSingular()) {
672 aContext->IdentityMatrix();
673 aContext->NewPath();
674 return;
675 }
677 aContext->MultiplyAndNudgeToIntegers(ThebesMatrix(aTransform));
679 // Hack to let SVGPathData::ConstructPath know if we have square caps:
680 const nsStyleSVG* style = StyleSVG();
681 if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
682 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
683 }
685 aContext->NewPath();
686 static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
687 }
689 void
690 nsSVGPathGeometryFrame::PaintMarkers(nsRenderingContext* aContext)
691 {
692 gfxTextContextPaint *contextPaint =
693 (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
695 if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
696 MarkerProperties properties = GetMarkerProperties(this);
698 if (properties.MarkersExist()) {
699 float strokeWidth = nsSVGUtils::GetStrokeWidth(this, contextPaint);
701 nsTArray<nsSVGMark> marks;
702 static_cast<nsSVGPathGeometryElement*>
703 (mContent)->GetMarkPoints(&marks);
705 uint32_t num = marks.Length();
706 if (num) {
707 // These are in the same order as the nsSVGMark::Type constants.
708 nsSVGMarkerFrame* markerFrames[] = {
709 properties.GetMarkerStartFrame(),
710 properties.GetMarkerMidFrame(),
711 properties.GetMarkerEndFrame(),
712 };
713 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
715 for (uint32_t i = 0; i < num; i++) {
716 nsSVGMark& mark = marks[i];
717 nsSVGMarkerFrame* frame = markerFrames[mark.type];
718 if (frame) {
719 frame->PaintMark(aContext, this, &mark, strokeWidth);
720 }
721 }
722 }
723 }
724 }
725 }
727 uint16_t
728 nsSVGPathGeometryFrame::GetHitTestFlags()
729 {
730 return nsSVGUtils::GetGeometryHitTestFlags(this);
731 }