Line data Source code
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 : using namespace mozilla::dom;
14 :
15 0 : class nsSVGUseFrame final
16 : : public nsSVGGFrame
17 : , public nsIAnonymousContentCreator
18 : {
19 : friend nsIFrame*
20 : NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
21 :
22 : protected:
23 23 : explicit nsSVGUseFrame(nsStyleContext* aContext)
24 23 : : nsSVGGFrame(aContext, kClassID)
25 23 : , mHasValidDimensions(true)
26 23 : {}
27 :
28 : public:
29 : NS_DECL_QUERYFRAME
30 39 : NS_DECL_FRAMEARENA_HELPERS(nsSVGUseFrame)
31 :
32 : // nsIFrame interface:
33 : virtual void Init(nsIContent* aContent,
34 : nsContainerFrame* aParent,
35 : nsIFrame* aPrevInFlow) override;
36 :
37 : virtual nsresult AttributeChanged(int32_t aNameSpaceID,
38 : nsIAtom* aAttribute,
39 : int32_t aModType) override;
40 :
41 : virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
42 :
43 : #ifdef DEBUG_FRAME_DUMP
44 0 : virtual nsresult GetFrameName(nsAString& aResult) const override
45 : {
46 0 : return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult);
47 : }
48 : #endif
49 :
50 : // nsSVGDisplayableFrame interface:
51 : virtual void ReflowSVG() override;
52 : virtual void NotifySVGChanged(uint32_t aFlags) override;
53 :
54 : // nsIAnonymousContentCreator
55 : virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
56 : virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
57 : uint32_t aFilter) override;
58 :
59 : private:
60 : bool mHasValidDimensions;
61 : };
62 :
63 : //----------------------------------------------------------------------
64 : // Implementation
65 :
66 : nsIFrame*
67 23 : NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
68 : {
69 23 : return new (aPresShell) nsSVGUseFrame(aContext);
70 : }
71 :
72 23 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame)
73 :
74 : //----------------------------------------------------------------------
75 : // nsQueryFrame methods
76 :
77 111 : NS_QUERYFRAME_HEAD(nsSVGUseFrame)
78 23 : NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
79 88 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGGFrame)
80 :
81 : //----------------------------------------------------------------------
82 : // nsIFrame methods:
83 :
84 : void
85 23 : nsSVGUseFrame::Init(nsIContent* aContent,
86 : nsContainerFrame* aParent,
87 : nsIFrame* aPrevInFlow)
88 : {
89 23 : NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::use),
90 : "Content is not an SVG use!");
91 :
92 23 : mHasValidDimensions =
93 23 : static_cast<SVGUseElement*>(aContent)->HasValidDimensions();
94 :
95 23 : nsSVGGFrame::Init(aContent, aParent, aPrevInFlow);
96 23 : }
97 :
98 : nsresult
99 0 : nsSVGUseFrame::AttributeChanged(int32_t aNameSpaceID,
100 : nsIAtom* aAttribute,
101 : int32_t aModType)
102 : {
103 0 : SVGUseElement *useElement = static_cast<SVGUseElement*>(mContent);
104 :
105 0 : if (aNameSpaceID == kNameSpaceID_None) {
106 0 : if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
107 : // make sure our cached transform matrix gets (lazily) updated
108 0 : mCanvasTM = nullptr;
109 : nsLayoutUtils::PostRestyleEvent(
110 : useElement, nsRestyleHint(0),
111 0 : nsChangeHint_InvalidateRenderingObservers);
112 0 : nsSVGUtils::ScheduleReflowSVG(this);
113 0 : nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
114 0 : } else if (aAttribute == nsGkAtoms::width ||
115 0 : aAttribute == nsGkAtoms::height) {
116 0 : bool invalidate = false;
117 0 : if (mHasValidDimensions != useElement->HasValidDimensions()) {
118 0 : mHasValidDimensions = !mHasValidDimensions;
119 0 : invalidate = true;
120 : }
121 0 : if (useElement->OurWidthAndHeightAreUsed()) {
122 0 : invalidate = true;
123 0 : useElement->SyncWidthOrHeight(aAttribute);
124 : }
125 0 : if (invalidate) {
126 : nsLayoutUtils::PostRestyleEvent(
127 : useElement, nsRestyleHint(0),
128 0 : nsChangeHint_InvalidateRenderingObservers);
129 0 : nsSVGUtils::ScheduleReflowSVG(this);
130 : }
131 : }
132 : }
133 :
134 0 : if ((aNameSpaceID == kNameSpaceID_XLink ||
135 0 : aNameSpaceID == kNameSpaceID_None) &&
136 0 : aAttribute == nsGkAtoms::href) {
137 : // we're changing our nature, clear out the clone information
138 : nsLayoutUtils::PostRestyleEvent(
139 : useElement, nsRestyleHint(0),
140 0 : nsChangeHint_InvalidateRenderingObservers);
141 0 : nsSVGUtils::ScheduleReflowSVG(this);
142 0 : useElement->mOriginal = nullptr;
143 0 : useElement->UnlinkSource();
144 0 : useElement->TriggerReclone();
145 : }
146 :
147 0 : return nsSVGGFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
148 : }
149 :
150 : void
151 0 : nsSVGUseFrame::DestroyFrom(nsIFrame* aDestructRoot)
152 : {
153 0 : RefPtr<SVGUseElement> use = static_cast<SVGUseElement*>(mContent);
154 0 : nsSVGGFrame::DestroyFrom(aDestructRoot);
155 0 : use->DestroyAnonymousContent();
156 0 : }
157 :
158 :
159 : //----------------------------------------------------------------------
160 : // nsSVGDisplayableFrame methods
161 :
162 : void
163 33 : nsSVGUseFrame::ReflowSVG()
164 : {
165 : // We only handle x/y offset here, since any width/height that is in force is
166 : // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
167 : // created for that purpose.
168 : float x, y;
169 33 : static_cast<SVGUseElement*>(mContent)->
170 33 : GetAnimatedLengthValues(&x, &y, nullptr);
171 132 : mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
172 66 : gfxRect(x, y, 0.0, 0.0),
173 99 : PresContext()->AppUnitsPerCSSPixel()).TopLeft());
174 :
175 : // If we have a filter, we need to invalidate ourselves because filter
176 : // output can change even if none of our descendants need repainting.
177 33 : if (StyleEffects()->HasFilters()) {
178 0 : InvalidateFrame();
179 : }
180 :
181 33 : nsSVGGFrame::ReflowSVG();
182 33 : }
183 :
184 : void
185 16 : nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags)
186 : {
187 16 : if (aFlags & COORD_CONTEXT_CHANGED &&
188 0 : !(aFlags & TRANSFORM_CHANGED)) {
189 : // Coordinate context changes affect mCanvasTM if we have a
190 : // percentage 'x' or 'y'
191 0 : SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
192 0 : if (use->mLengthAttributes[SVGUseElement::ATTR_X].IsPercentage() ||
193 0 : use->mLengthAttributes[SVGUseElement::ATTR_Y].IsPercentage()) {
194 0 : aFlags |= TRANSFORM_CHANGED;
195 : // Ancestor changes can't affect how we render from the perspective of
196 : // any rendering observers that we may have, so we don't need to
197 : // invalidate them. We also don't need to invalidate ourself, since our
198 : // changed ancestor will have invalidated its entire area, which includes
199 : // our area.
200 : // For perf reasons we call this before calling NotifySVGChanged() below.
201 0 : nsSVGUtils::ScheduleReflowSVG(this);
202 : }
203 : }
204 :
205 : // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
206 : // non-percentage width/height, since if they're set then they are cloned to
207 : // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.
208 :
209 16 : nsSVGGFrame::NotifySVGChanged(aFlags);
210 16 : }
211 :
212 : //----------------------------------------------------------------------
213 : // nsIAnonymousContentCreator methods:
214 :
215 : nsresult
216 23 : nsSVGUseFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
217 : {
218 23 : SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
219 :
220 23 : nsIContent* clone = use->CreateAnonymousContent();
221 : nsLayoutUtils::PostRestyleEvent(
222 23 : use, nsRestyleHint(0), nsChangeHint_InvalidateRenderingObservers);
223 23 : if (!clone)
224 0 : return NS_ERROR_FAILURE;
225 23 : if (!aElements.AppendElement(clone))
226 0 : return NS_ERROR_OUT_OF_MEMORY;
227 23 : return NS_OK;
228 : }
229 :
230 : void
231 0 : nsSVGUseFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
232 : uint32_t aFilter)
233 : {
234 0 : SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
235 0 : nsIContent* clone = use->GetAnonymousContent();
236 0 : if (clone) {
237 0 : aElements.AppendElement(clone);
238 : }
239 0 : }
|