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 // Keep in (case-insensitive) order:
7 #include "nsIAnonymousContentCreator.h"
8 #include "nsSVGEffects.h"
9 #include "nsSVGGFrame.h"
10 #include "mozilla/dom/SVGUseElement.h"
11 #include "nsContentList.h"
13 typedef nsSVGGFrame nsSVGUseFrameBase
;
15 using namespace mozilla::dom
;
17 class nsSVGUseFrame
: public nsSVGUseFrameBase
,
18 public nsIAnonymousContentCreator
21 NS_NewSVGUseFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
);
24 explicit nsSVGUseFrame(nsStyleContext
* aContext
) :
25 nsSVGUseFrameBase(aContext
),
26 mHasValidDimensions(true)
31 NS_DECL_FRAMEARENA_HELPERS
34 // nsIFrame interface:
35 virtual void Init(nsIContent
* aContent
,
36 nsContainerFrame
* aParent
,
37 nsIFrame
* aPrevInFlow
) MOZ_OVERRIDE
;
39 virtual nsresult
AttributeChanged(int32_t aNameSpaceID
,
41 int32_t aModType
) MOZ_OVERRIDE
;
43 virtual void DestroyFrom(nsIFrame
* aDestructRoot
) MOZ_OVERRIDE
;
46 * Get the "type" of the frame
48 * @see nsGkAtoms::svgUseFrame
50 virtual nsIAtom
* GetType() const MOZ_OVERRIDE
;
52 virtual bool IsLeaf() const MOZ_OVERRIDE
;
54 #ifdef DEBUG_FRAME_DUMP
55 virtual nsresult
GetFrameName(nsAString
& aResult
) const MOZ_OVERRIDE
57 return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult
);
61 // nsISVGChildFrame interface:
62 virtual void ReflowSVG() MOZ_OVERRIDE
;
63 virtual void NotifySVGChanged(uint32_t aFlags
) MOZ_OVERRIDE
;
65 // nsIAnonymousContentCreator
66 virtual nsresult
CreateAnonymousContent(nsTArray
<ContentInfo
>& aElements
) MOZ_OVERRIDE
;
67 virtual void AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
68 uint32_t aFilter
) MOZ_OVERRIDE
;
71 bool mHasValidDimensions
;
74 //----------------------------------------------------------------------
78 NS_NewSVGUseFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
80 return new (aPresShell
) nsSVGUseFrame(aContext
);
83 NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame
)
86 nsSVGUseFrame::GetType() const
88 return nsGkAtoms::svgUseFrame
;
91 //----------------------------------------------------------------------
92 // nsQueryFrame methods
94 NS_QUERYFRAME_HEAD(nsSVGUseFrame
)
95 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
96 NS_QUERYFRAME_TAIL_INHERITING(nsSVGUseFrameBase
)
98 //----------------------------------------------------------------------
102 nsSVGUseFrame::Init(nsIContent
* aContent
,
103 nsContainerFrame
* aParent
,
104 nsIFrame
* aPrevInFlow
)
106 NS_ASSERTION(aContent
->IsSVG(nsGkAtoms::use
),
107 "Content is not an SVG use!");
109 mHasValidDimensions
=
110 static_cast<SVGUseElement
*>(aContent
)->HasValidDimensions();
112 nsSVGUseFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
116 nsSVGUseFrame::AttributeChanged(int32_t aNameSpaceID
,
120 SVGUseElement
*useElement
= static_cast<SVGUseElement
*>(mContent
);
122 if (aNameSpaceID
== kNameSpaceID_None
) {
123 if (aAttribute
== nsGkAtoms::x
||
124 aAttribute
== nsGkAtoms::y
) {
125 // make sure our cached transform matrix gets (lazily) updated
127 nsSVGEffects::InvalidateRenderingObservers(this);
128 nsSVGUtils::ScheduleReflowSVG(this);
129 nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED
);
130 } else if (aAttribute
== nsGkAtoms::width
||
131 aAttribute
== nsGkAtoms::height
) {
132 bool invalidate
= false;
133 if (mHasValidDimensions
!= useElement
->HasValidDimensions()) {
134 mHasValidDimensions
= !mHasValidDimensions
;
137 if (useElement
->OurWidthAndHeightAreUsed()) {
139 useElement
->SyncWidthOrHeight(aAttribute
);
142 nsSVGEffects::InvalidateRenderingObservers(this);
143 nsSVGUtils::ScheduleReflowSVG(this);
146 } else if (aNameSpaceID
== kNameSpaceID_XLink
&&
147 aAttribute
== nsGkAtoms::href
) {
148 // we're changing our nature, clear out the clone information
149 nsSVGEffects::InvalidateRenderingObservers(this);
150 nsSVGUtils::ScheduleReflowSVG(this);
151 useElement
->mOriginal
= nullptr;
152 useElement
->UnlinkSource();
153 useElement
->TriggerReclone();
156 return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID
,
157 aAttribute
, aModType
);
161 nsSVGUseFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
163 nsRefPtr
<SVGUseElement
> use
= static_cast<SVGUseElement
*>(mContent
);
164 nsSVGUseFrameBase::DestroyFrom(aDestructRoot
);
165 use
->DestroyAnonymousContent();
169 nsSVGUseFrame::IsLeaf() const
175 //----------------------------------------------------------------------
176 // nsISVGChildFrame methods
179 nsSVGUseFrame::ReflowSVG()
181 // We only handle x/y offset here, since any width/height that is in force is
182 // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
183 // created for that purpose.
185 static_cast<SVGUseElement
*>(mContent
)->
186 GetAnimatedLengthValues(&x
, &y
, nullptr);
187 mRect
.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
188 gfxRect(x
, y
, 0.0, 0.0),
189 PresContext()->AppUnitsPerCSSPixel()).TopLeft());
191 // If we have a filter, we need to invalidate ourselves because filter
192 // output can change even if none of our descendants need repainting.
193 if (StyleSVGReset()->HasFilters()) {
197 nsSVGUseFrameBase::ReflowSVG();
201 nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags
)
203 if (aFlags
& COORD_CONTEXT_CHANGED
&&
204 !(aFlags
& TRANSFORM_CHANGED
)) {
205 // Coordinate context changes affect mCanvasTM if we have a
206 // percentage 'x' or 'y'
207 SVGUseElement
*use
= static_cast<SVGUseElement
*>(mContent
);
208 if (use
->mLengthAttributes
[SVGUseElement::ATTR_X
].IsPercentage() ||
209 use
->mLengthAttributes
[SVGUseElement::ATTR_Y
].IsPercentage()) {
210 aFlags
|= TRANSFORM_CHANGED
;
211 // Ancestor changes can't affect how we render from the perspective of
212 // any rendering observers that we may have, so we don't need to
213 // invalidate them. We also don't need to invalidate ourself, since our
214 // changed ancestor will have invalidated its entire area, which includes
216 // For perf reasons we call this before calling NotifySVGChanged() below.
217 nsSVGUtils::ScheduleReflowSVG(this);
221 // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
222 // non-percentage width/height, since if they're set then they are cloned to
223 // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.
225 nsSVGUseFrameBase::NotifySVGChanged(aFlags
);
228 //----------------------------------------------------------------------
229 // nsIAnonymousContentCreator methods:
232 nsSVGUseFrame::CreateAnonymousContent(nsTArray
<ContentInfo
>& aElements
)
234 SVGUseElement
*use
= static_cast<SVGUseElement
*>(mContent
);
236 nsIContent
* clone
= use
->CreateAnonymousContent();
237 nsSVGEffects::InvalidateRenderingObservers(this);
239 return NS_ERROR_FAILURE
;
240 if (!aElements
.AppendElement(clone
))
241 return NS_ERROR_OUT_OF_MEMORY
;
246 nsSVGUseFrame::AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
249 SVGUseElement
*use
= static_cast<SVGUseElement
*>(mContent
);
250 nsIContent
* clone
= use
->GetAnonymousContent();
252 aElements
.AppendElement(clone
);