|
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/. */ |
|
5 |
|
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" |
|
12 |
|
13 typedef nsSVGGFrame nsSVGUseFrameBase; |
|
14 |
|
15 using namespace mozilla::dom; |
|
16 |
|
17 class nsSVGUseFrame : public nsSVGUseFrameBase, |
|
18 public nsIAnonymousContentCreator |
|
19 { |
|
20 friend nsIFrame* |
|
21 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); |
|
22 |
|
23 protected: |
|
24 nsSVGUseFrame(nsStyleContext* aContext) : |
|
25 nsSVGUseFrameBase(aContext), |
|
26 mHasValidDimensions(true) |
|
27 {} |
|
28 |
|
29 public: |
|
30 NS_DECL_QUERYFRAME |
|
31 NS_DECL_FRAMEARENA_HELPERS |
|
32 |
|
33 |
|
34 // nsIFrame interface: |
|
35 virtual void Init(nsIContent* aContent, |
|
36 nsIFrame* aParent, |
|
37 nsIFrame* aPrevInFlow) MOZ_OVERRIDE; |
|
38 |
|
39 virtual nsresult AttributeChanged(int32_t aNameSpaceID, |
|
40 nsIAtom* aAttribute, |
|
41 int32_t aModType) MOZ_OVERRIDE; |
|
42 |
|
43 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; |
|
44 |
|
45 /** |
|
46 * Get the "type" of the frame |
|
47 * |
|
48 * @see nsGkAtoms::svgUseFrame |
|
49 */ |
|
50 virtual nsIAtom* GetType() const MOZ_OVERRIDE; |
|
51 |
|
52 virtual bool IsLeaf() const MOZ_OVERRIDE; |
|
53 |
|
54 #ifdef DEBUG_FRAME_DUMP |
|
55 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE |
|
56 { |
|
57 return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult); |
|
58 } |
|
59 #endif |
|
60 |
|
61 // nsISVGChildFrame interface: |
|
62 virtual void ReflowSVG() MOZ_OVERRIDE; |
|
63 virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE; |
|
64 |
|
65 // nsIAnonymousContentCreator |
|
66 virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE; |
|
67 virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, |
|
68 uint32_t aFilter) MOZ_OVERRIDE; |
|
69 |
|
70 private: |
|
71 bool mHasValidDimensions; |
|
72 }; |
|
73 |
|
74 //---------------------------------------------------------------------- |
|
75 // Implementation |
|
76 |
|
77 nsIFrame* |
|
78 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
79 { |
|
80 return new (aPresShell) nsSVGUseFrame(aContext); |
|
81 } |
|
82 |
|
83 NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame) |
|
84 |
|
85 nsIAtom * |
|
86 nsSVGUseFrame::GetType() const |
|
87 { |
|
88 return nsGkAtoms::svgUseFrame; |
|
89 } |
|
90 |
|
91 //---------------------------------------------------------------------- |
|
92 // nsQueryFrame methods |
|
93 |
|
94 NS_QUERYFRAME_HEAD(nsSVGUseFrame) |
|
95 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) |
|
96 NS_QUERYFRAME_TAIL_INHERITING(nsSVGUseFrameBase) |
|
97 |
|
98 //---------------------------------------------------------------------- |
|
99 // nsIFrame methods: |
|
100 |
|
101 void |
|
102 nsSVGUseFrame::Init(nsIContent* aContent, |
|
103 nsIFrame* aParent, |
|
104 nsIFrame* aPrevInFlow) |
|
105 { |
|
106 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::use), |
|
107 "Content is not an SVG use!"); |
|
108 |
|
109 mHasValidDimensions = |
|
110 static_cast<SVGUseElement*>(aContent)->HasValidDimensions(); |
|
111 |
|
112 nsSVGUseFrameBase::Init(aContent, aParent, aPrevInFlow); |
|
113 } |
|
114 |
|
115 nsresult |
|
116 nsSVGUseFrame::AttributeChanged(int32_t aNameSpaceID, |
|
117 nsIAtom* aAttribute, |
|
118 int32_t aModType) |
|
119 { |
|
120 SVGUseElement *useElement = static_cast<SVGUseElement*>(mContent); |
|
121 |
|
122 if (aNameSpaceID == kNameSpaceID_None) { |
|
123 if (aAttribute == nsGkAtoms::x || |
|
124 aAttribute == nsGkAtoms::y) { |
|
125 // make sure our cached transform matrix gets (lazily) updated |
|
126 mCanvasTM = nullptr; |
|
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; |
|
135 invalidate = true; |
|
136 } |
|
137 if (useElement->OurWidthAndHeightAreUsed()) { |
|
138 invalidate = true; |
|
139 useElement->SyncWidthOrHeight(aAttribute); |
|
140 } |
|
141 if (invalidate) { |
|
142 nsSVGEffects::InvalidateRenderingObservers(this); |
|
143 nsSVGUtils::ScheduleReflowSVG(this); |
|
144 } |
|
145 } |
|
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(); |
|
154 } |
|
155 |
|
156 return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID, |
|
157 aAttribute, aModType); |
|
158 } |
|
159 |
|
160 void |
|
161 nsSVGUseFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
162 { |
|
163 nsRefPtr<SVGUseElement> use = static_cast<SVGUseElement*>(mContent); |
|
164 nsSVGUseFrameBase::DestroyFrom(aDestructRoot); |
|
165 use->DestroyAnonymousContent(); |
|
166 } |
|
167 |
|
168 bool |
|
169 nsSVGUseFrame::IsLeaf() const |
|
170 { |
|
171 return true; |
|
172 } |
|
173 |
|
174 |
|
175 //---------------------------------------------------------------------- |
|
176 // nsISVGChildFrame methods |
|
177 |
|
178 void |
|
179 nsSVGUseFrame::ReflowSVG() |
|
180 { |
|
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. |
|
184 float x, y; |
|
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()); |
|
190 |
|
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()) { |
|
194 InvalidateFrame(); |
|
195 } |
|
196 |
|
197 nsSVGUseFrameBase::ReflowSVG(); |
|
198 } |
|
199 |
|
200 void |
|
201 nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags) |
|
202 { |
|
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 |
|
215 // our area. |
|
216 // For perf reasons we call this before calling NotifySVGChanged() below. |
|
217 nsSVGUtils::ScheduleReflowSVG(this); |
|
218 } |
|
219 } |
|
220 |
|
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. |
|
224 |
|
225 nsSVGUseFrameBase::NotifySVGChanged(aFlags); |
|
226 } |
|
227 |
|
228 //---------------------------------------------------------------------- |
|
229 // nsIAnonymousContentCreator methods: |
|
230 |
|
231 nsresult |
|
232 nsSVGUseFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) |
|
233 { |
|
234 SVGUseElement *use = static_cast<SVGUseElement*>(mContent); |
|
235 |
|
236 nsIContent* clone = use->CreateAnonymousContent(); |
|
237 nsSVGEffects::InvalidateRenderingObservers(this); |
|
238 if (!clone) |
|
239 return NS_ERROR_FAILURE; |
|
240 if (!aElements.AppendElement(clone)) |
|
241 return NS_ERROR_OUT_OF_MEMORY; |
|
242 return NS_OK; |
|
243 } |
|
244 |
|
245 void |
|
246 nsSVGUseFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, |
|
247 uint32_t aFilter) |
|
248 { |
|
249 SVGUseElement *use = static_cast<SVGUseElement*>(mContent); |
|
250 nsIContent* clone = use->GetAnonymousContent(); |
|
251 aElements.MaybeAppendElement(clone); |
|
252 } |