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/. */
7 #include "nsSVGClipPathFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxContext.h"
11 #include "nsGkAtoms.h"
12 #include "nsRenderingContext.h"
13 #include "mozilla/dom/SVGClipPathElement.h"
14 #include "nsSVGEffects.h"
15 #include "nsSVGUtils.h"
17 using namespace mozilla::dom
;
19 //----------------------------------------------------------------------
23 NS_NewSVGClipPathFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
25 return new (aPresShell
) nsSVGClipPathFrame(aContext
);
28 NS_IMPL_FRAMEARENA_HELPERS(nsSVGClipPathFrame
)
31 nsSVGClipPathFrame::ApplyClipOrPaintClipMask(nsRenderingContext
* aContext
,
32 nsIFrame
* aClippedFrame
,
33 const gfxMatrix
& aMatrix
)
35 // If the flag is set when we get here, it means this clipPath frame
36 // has already been used painting the current clip, and the document
37 // has a clip reference loop.
39 NS_WARNING("Clip loop detected!");
42 AutoClipPathReferencer
clipRef(this);
44 mMatrixForChildren
= GetClipPathTransform(aClippedFrame
) * aMatrix
;
46 gfxContext
*gfx
= aContext
->ThebesContext();
48 nsISVGChildFrame
*singleClipPathChild
= nullptr;
50 if (IsTrivial(&singleClipPathChild
)) {
51 // Notify our child that it's painting as part of a clipPath, and that
52 // we only require it to draw its path (it should skip filling, etc.):
53 SVGAutoRenderState
mode(aContext
, SVGAutoRenderState::CLIP
);
55 if (!singleClipPathChild
) {
56 // We have no children - the spec says clip away everything:
57 gfx
->Rectangle(gfxRect());
59 singleClipPathChild
->NotifySVGChanged(
60 nsISVGChildFrame::TRANSFORM_CHANGED
);
61 singleClipPathChild
->PaintSVG(aContext
, nullptr);
68 // Seems like this is a non-trivial clipPath, so we need to use a clip mask.
70 // Notify our children that they're painting into a clip mask:
71 SVGAutoRenderState
mode(aContext
, SVGAutoRenderState::CLIP_MASK
);
73 // Check if this clipPath is itself clipped by another clipPath:
74 nsSVGClipPathFrame
*clipPathFrame
=
75 nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
76 bool referencedClipIsTrivial
;
78 referencedClipIsTrivial
= clipPathFrame
->IsTrivial();
80 if (referencedClipIsTrivial
) {
81 clipPathFrame
->ApplyClipOrPaintClipMask(aContext
, aClippedFrame
, aMatrix
);
83 gfx
->PushGroup(gfxContentType::ALPHA
);
87 for (nsIFrame
* kid
= mFrames
.FirstChild(); kid
;
88 kid
= kid
->GetNextSibling()) {
89 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(kid
);
91 // The CTM of each frame referencing us can be different.
92 SVGFrame
->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED
);
95 nsSVGClipPathFrame
*clipPathFrame
=
96 nsSVGEffects::GetEffectProperties(kid
).GetClipPathFrame(&isOK
);
104 isTrivial
= clipPathFrame
->IsTrivial();
107 clipPathFrame
->ApplyClipOrPaintClipMask(aContext
, aClippedFrame
, aMatrix
);
109 gfx
->PushGroup(gfxContentType::ALPHA
);
113 SVGFrame
->PaintSVG(aContext
, nullptr);
117 gfx
->PopGroupToSource();
119 nsRefPtr
<gfxPattern
> clipMaskSurface
;
120 gfx
->PushGroup(gfxContentType::ALPHA
);
122 clipPathFrame
->ApplyClipOrPaintClipMask(aContext
, aClippedFrame
, aMatrix
);
123 clipMaskSurface
= gfx
->PopGroup();
125 if (clipMaskSurface
) {
126 gfx
->Mask(clipMaskSurface
);
135 if (!referencedClipIsTrivial
) {
136 gfx
->PopGroupToSource();
138 nsRefPtr
<gfxPattern
> clipMaskSurface
;
139 gfx
->PushGroup(gfxContentType::ALPHA
);
141 clipPathFrame
->ApplyClipOrPaintClipMask(aContext
, aClippedFrame
, aMatrix
);
142 clipMaskSurface
= gfx
->PopGroup();
144 if (clipMaskSurface
) {
145 gfx
->Mask(clipMaskSurface
);
155 nsSVGClipPathFrame::PointIsInsideClipPath(nsIFrame
* aClippedFrame
,
156 const gfxPoint
&aPoint
)
158 // If the flag is set when we get here, it means this clipPath frame
159 // has already been used in hit testing against the current clip,
160 // and the document has a clip reference loop.
162 NS_WARNING("Clip loop detected!");
165 AutoClipPathReferencer
clipRef(this);
167 gfxMatrix matrix
= GetClipPathTransform(aClippedFrame
);
168 if (!matrix
.Invert()) {
171 gfxPoint point
= matrix
.Transform(aPoint
);
173 // clipPath elements can themselves be clipped by a different clip path. In
174 // that case the other clip path further clips away the element that is being
175 // clipped by the original clipPath. If this clipPath is being clipped by a
176 // different clip path we need to check if it prevents the original element
177 // from recieving events at aPoint:
178 nsSVGClipPathFrame
*clipPathFrame
=
179 nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
181 !clipPathFrame
->PointIsInsideClipPath(aClippedFrame
, aPoint
)) {
185 for (nsIFrame
* kid
= mFrames
.FirstChild(); kid
;
186 kid
= kid
->GetNextSibling()) {
187 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(kid
);
189 gfxPoint pointForChild
= point
;
190 gfxMatrix m
= static_cast<nsSVGElement
*>(kid
->GetContent())->
191 PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eUserSpaceToParent
);
192 if (!m
.IsIdentity()) {
196 pointForChild
= m
.Transform(point
);
198 if (SVGFrame
->GetFrameForPoint(pointForChild
)) {
207 nsSVGClipPathFrame::IsTrivial(nsISVGChildFrame
**aSingleChild
)
209 // If the clip path is clipped then it's non-trivial
210 if (nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr))
214 *aSingleChild
= nullptr;
217 nsISVGChildFrame
*foundChild
= nullptr;
219 for (nsIFrame
* kid
= mFrames
.FirstChild(); kid
;
220 kid
= kid
->GetNextSibling()) {
221 nsISVGChildFrame
*svgChild
= do_QueryFrame(kid
);
223 // We consider a non-trivial clipPath to be one containing
224 // either more than one svg child and/or a svg container
225 if (foundChild
|| svgChild
->IsDisplayContainer())
228 // or where the child is itself clipped
229 if (nsSVGEffects::GetEffectProperties(kid
).GetClipPathFrame(nullptr))
232 foundChild
= svgChild
;
236 *aSingleChild
= foundChild
;
242 nsSVGClipPathFrame::IsValid()
245 NS_WARNING("Clip loop detected!");
248 AutoClipPathReferencer
clipRef(this);
251 nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(&isOK
);
256 for (nsIFrame
* kid
= mFrames
.FirstChild(); kid
;
257 kid
= kid
->GetNextSibling()) {
259 nsIAtom
*type
= kid
->GetType();
261 if (type
== nsGkAtoms::svgUseFrame
) {
262 for (nsIFrame
* grandKid
= kid
->GetFirstPrincipalChild(); grandKid
;
263 grandKid
= grandKid
->GetNextSibling()) {
265 nsIAtom
*type
= grandKid
->GetType();
267 if (type
!= nsGkAtoms::svgPathGeometryFrame
&&
268 type
!= nsGkAtoms::svgTextFrame
) {
274 if (type
!= nsGkAtoms::svgPathGeometryFrame
&&
275 type
!= nsGkAtoms::svgTextFrame
) {
283 nsSVGClipPathFrame::AttributeChanged(int32_t aNameSpaceID
,
287 if (aNameSpaceID
== kNameSpaceID_None
) {
288 if (aAttribute
== nsGkAtoms::transform
) {
289 nsSVGEffects::InvalidateDirectRenderingObservers(this);
290 nsSVGUtils::NotifyChildrenOfSVGChange(this,
291 nsISVGChildFrame::TRANSFORM_CHANGED
);
293 if (aAttribute
== nsGkAtoms::clipPathUnits
) {
294 nsSVGEffects::InvalidateRenderingObservers(this);
298 return nsSVGClipPathFrameBase::AttributeChanged(aNameSpaceID
,
299 aAttribute
, aModType
);
303 nsSVGClipPathFrame::Init(nsIContent
* aContent
,
304 nsContainerFrame
* aParent
,
305 nsIFrame
* aPrevInFlow
)
307 NS_ASSERTION(aContent
->IsSVG(nsGkAtoms::clipPath
),
308 "Content is not an SVG clipPath!");
310 AddStateBits(NS_STATE_SVG_CLIPPATH_CHILD
);
311 nsSVGClipPathFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
315 nsSVGClipPathFrame::GetType() const
317 return nsGkAtoms::svgClipPathFrame
;
321 nsSVGClipPathFrame::GetCanvasTM(uint32_t aFor
, nsIFrame
* aTransformRoot
)
323 return mMatrixForChildren
;
327 nsSVGClipPathFrame::GetClipPathTransform(nsIFrame
* aClippedFrame
)
329 SVGClipPathElement
*content
= static_cast<SVGClipPathElement
*>(mContent
);
331 gfxMatrix tm
= content
->PrependLocalTransformsTo(gfxMatrix());
333 nsSVGEnum
* clipPathUnits
=
334 &content
->mEnumAttributes
[SVGClipPathElement::CLIPPATHUNITS
];
336 return nsSVGUtils::AdjustMatrixForUnits(tm
, clipPathUnits
, aClippedFrame
);
340 nsSVGClipPathFrame::GetBBoxForClipPathFrame(const SVGBBox
&aBBox
,
341 const gfxMatrix
&aMatrix
)
343 nsIContent
* node
= GetContent()->GetFirstChild();
344 SVGBBox unionBBox
, tmpBBox
;
345 for (; node
; node
= node
->GetNextSibling()) {
347 static_cast<nsSVGElement
*>(node
)->GetPrimaryFrame();
349 nsISVGChildFrame
*svg
= do_QueryFrame(frame
);
351 tmpBBox
= svg
->GetBBoxContribution(mozilla::gfx::ToMatrix(aMatrix
),
352 nsSVGUtils::eBBoxIncludeFill
);
353 nsSVGEffects::EffectProperties effectProperties
=
354 nsSVGEffects::GetEffectProperties(frame
);
356 nsSVGClipPathFrame
*clipPathFrame
=
357 effectProperties
.GetClipPathFrame(&isOK
);
358 if (clipPathFrame
&& isOK
) {
359 tmpBBox
= clipPathFrame
->GetBBoxForClipPathFrame(tmpBBox
, aMatrix
);
361 tmpBBox
.Intersect(aBBox
);
362 unionBBox
.UnionEdges(tmpBBox
);
366 nsSVGEffects::EffectProperties props
=
367 nsSVGEffects::GetEffectProperties(this);
368 if (props
.mClipPath
) {
370 nsSVGClipPathFrame
*clipPathFrame
= props
.GetClipPathFrame(&isOK
);
371 if (clipPathFrame
&& isOK
) {
372 tmpBBox
= clipPathFrame
->GetBBoxForClipPathFrame(aBBox
, aMatrix
);
373 unionBBox
.Intersect(tmpBBox
);
375 unionBBox
= SVGBBox();