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 nsLayoutUtils::PostRestyleEvent(
128 useElement
, nsRestyleHint(0),
129 nsChangeHint_InvalidateRenderingObservers
);
130 nsSVGUtils::ScheduleReflowSVG(this);
131 nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED
);
132 } else if (aAttribute
== nsGkAtoms::width
||
133 aAttribute
== nsGkAtoms::height
) {
134 bool invalidate
= false;
135 if (mHasValidDimensions
!= useElement
->HasValidDimensions()) {
136 mHasValidDimensions
= !mHasValidDimensions
;
139 if (useElement
->OurWidthAndHeightAreUsed()) {
141 useElement
->SyncWidthOrHeight(aAttribute
);
144 nsLayoutUtils::PostRestyleEvent(
145 useElement
, nsRestyleHint(0),
146 nsChangeHint_InvalidateRenderingObservers
);
147 nsSVGUtils::ScheduleReflowSVG(this);
150 } else if (aNameSpaceID
== kNameSpaceID_XLink
&&
151 aAttribute
== nsGkAtoms::href
) {
152 // we're changing our nature, clear out the clone information
153 nsLayoutUtils::PostRestyleEvent(
154 useElement
, nsRestyleHint(0),
155 nsChangeHint_InvalidateRenderingObservers
);
156 nsSVGUtils::ScheduleReflowSVG(this);
157 useElement
->mOriginal
= nullptr;
158 useElement
->UnlinkSource();
159 useElement
->TriggerReclone();
162 return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID
,
163 aAttribute
, aModType
);
167 nsSVGUseFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
169 nsRefPtr
<SVGUseElement
> use
= static_cast<SVGUseElement
*>(mContent
);
170 nsSVGUseFrameBase::DestroyFrom(aDestructRoot
);
171 use
->DestroyAnonymousContent();
175 nsSVGUseFrame::IsLeaf() const
181 //----------------------------------------------------------------------
182 // nsISVGChildFrame methods
185 nsSVGUseFrame::ReflowSVG()
187 // We only handle x/y offset here, since any width/height that is in force is
188 // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
189 // created for that purpose.
191 static_cast<SVGUseElement
*>(mContent
)->
192 GetAnimatedLengthValues(&x
, &y
, nullptr);
193 mRect
.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
194 gfxRect(x
, y
, 0.0, 0.0),
195 PresContext()->AppUnitsPerCSSPixel()).TopLeft());
197 // If we have a filter, we need to invalidate ourselves because filter
198 // output can change even if none of our descendants need repainting.
199 if (StyleSVGReset()->HasFilters()) {
203 nsSVGUseFrameBase::ReflowSVG();
207 nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags
)
209 if (aFlags
& COORD_CONTEXT_CHANGED
&&
210 !(aFlags
& TRANSFORM_CHANGED
)) {
211 // Coordinate context changes affect mCanvasTM if we have a
212 // percentage 'x' or 'y'
213 SVGUseElement
*use
= static_cast<SVGUseElement
*>(mContent
);
214 if (use
->mLengthAttributes
[SVGUseElement::ATTR_X
].IsPercentage() ||
215 use
->mLengthAttributes
[SVGUseElement::ATTR_Y
].IsPercentage()) {
216 aFlags
|= TRANSFORM_CHANGED
;
217 // Ancestor changes can't affect how we render from the perspective of
218 // any rendering observers that we may have, so we don't need to
219 // invalidate them. We also don't need to invalidate ourself, since our
220 // changed ancestor will have invalidated its entire area, which includes
222 // For perf reasons we call this before calling NotifySVGChanged() below.
223 nsSVGUtils::ScheduleReflowSVG(this);
227 // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
228 // non-percentage width/height, since if they're set then they are cloned to
229 // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.
231 nsSVGUseFrameBase::NotifySVGChanged(aFlags
);
234 //----------------------------------------------------------------------
235 // nsIAnonymousContentCreator methods:
238 nsSVGUseFrame::CreateAnonymousContent(nsTArray
<ContentInfo
>& aElements
)
240 SVGUseElement
*use
= static_cast<SVGUseElement
*>(mContent
);
242 nsIContent
* clone
= use
->CreateAnonymousContent();
243 nsLayoutUtils::PostRestyleEvent(
244 use
, nsRestyleHint(0), nsChangeHint_InvalidateRenderingObservers
);
246 return NS_ERROR_FAILURE
;
247 if (!aElements
.AppendElement(clone
))
248 return NS_ERROR_OUT_OF_MEMORY
;
253 nsSVGUseFrame::AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
256 SVGUseElement
*use
= static_cast<SVGUseElement
*>(mContent
);
257 nsIContent
* clone
= use
->GetAnonymousContent();
259 aElements
.AppendElement(clone
);