/src/mozilla-central/layout/svg/nsSVGGradientFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // Main header first: |
8 | | #include "nsSVGGradientFrame.h" |
9 | | #include <algorithm> |
10 | | |
11 | | // Keep others in (case-insensitive) order: |
12 | | #include "AutoReferenceChainGuard.h" |
13 | | #include "gfxPattern.h" |
14 | | #include "mozilla/dom/SVGGradientElement.h" |
15 | | #include "mozilla/dom/SVGGradientElementBinding.h" |
16 | | #include "mozilla/dom/SVGStopElement.h" |
17 | | #include "mozilla/dom/SVGUnitTypesBinding.h" |
18 | | #include "nsContentUtils.h" |
19 | | #include "SVGObserverUtils.h" |
20 | | #include "nsSVGAnimatedTransformList.h" |
21 | | |
22 | | // XXX Tight coupling with content classes ahead! |
23 | | |
24 | | using namespace mozilla; |
25 | | using namespace mozilla::dom; |
26 | | using namespace mozilla::dom::SVGGradientElement_Binding; |
27 | | using namespace mozilla::dom::SVGUnitTypes_Binding; |
28 | | using namespace mozilla::gfx; |
29 | | |
30 | | //---------------------------------------------------------------------- |
31 | | // Implementation |
32 | | |
33 | | nsSVGGradientFrame::nsSVGGradientFrame(ComputedStyle* aStyle, |
34 | | ClassID aID) |
35 | | : nsSVGPaintServerFrame(aStyle, aID) |
36 | | , mSource(nullptr) |
37 | | , mLoopFlag(false) |
38 | | , mNoHRefURI(false) |
39 | 0 | { |
40 | 0 | } |
41 | | |
42 | | //---------------------------------------------------------------------- |
43 | | // nsIFrame methods: |
44 | | |
45 | | nsresult |
46 | | nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, |
47 | | nsAtom* aAttribute, |
48 | | int32_t aModType) |
49 | 0 | { |
50 | 0 | if (aNameSpaceID == kNameSpaceID_None && |
51 | 0 | (aAttribute == nsGkAtoms::gradientUnits || |
52 | 0 | aAttribute == nsGkAtoms::gradientTransform || |
53 | 0 | aAttribute == nsGkAtoms::spreadMethod)) { |
54 | 0 | SVGObserverUtils::InvalidateDirectRenderingObservers(this); |
55 | 0 | } else if ((aNameSpaceID == kNameSpaceID_XLink || |
56 | 0 | aNameSpaceID == kNameSpaceID_None) && |
57 | 0 | aAttribute == nsGkAtoms::href) { |
58 | 0 | // Blow away our reference, if any |
59 | 0 | DeleteProperty(SVGObserverUtils::HrefToTemplateProperty()); |
60 | 0 | mNoHRefURI = false; |
61 | 0 | // And update whoever references us |
62 | 0 | SVGObserverUtils::InvalidateDirectRenderingObservers(this); |
63 | 0 | } |
64 | 0 |
|
65 | 0 | return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID, |
66 | 0 | aAttribute, aModType); |
67 | 0 | } |
68 | | |
69 | | //---------------------------------------------------------------------- |
70 | | |
71 | | uint16_t |
72 | | nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) |
73 | 0 | { |
74 | 0 | const nsSVGEnum& thisEnum = |
75 | 0 | static_cast<dom::SVGGradientElement*>(GetContent())->mEnumAttributes[aIndex]; |
76 | 0 |
|
77 | 0 | if (thisEnum.IsExplicitlySet()) |
78 | 0 | return thisEnum.GetAnimValue(); |
79 | 0 | |
80 | 0 | // Before we recurse, make sure we'll break reference loops and over long |
81 | 0 | // reference chains: |
82 | 0 | static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; |
83 | 0 | AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, |
84 | 0 | &sRefChainLengthCounter); |
85 | 0 | if (MOZ_UNLIKELY(!refChainGuard.Reference())) { |
86 | 0 | // Break reference chain |
87 | 0 | return static_cast<dom::SVGGradientElement*>(aDefault)-> |
88 | 0 | mEnumAttributes[aIndex].GetAnimValue(); |
89 | 0 | } |
90 | 0 | |
91 | 0 | nsSVGGradientFrame *next = GetReferencedGradient(); |
92 | 0 |
|
93 | 0 | return next ? next->GetEnumValue(aIndex, aDefault) |
94 | 0 | : static_cast<dom::SVGGradientElement*>(aDefault)-> |
95 | 0 | mEnumAttributes[aIndex].GetAnimValue(); |
96 | 0 | } |
97 | | |
98 | | uint16_t |
99 | | nsSVGGradientFrame::GetGradientUnits() |
100 | 0 | { |
101 | 0 | // This getter is called every time the others are called - maybe cache it? |
102 | 0 | return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS); |
103 | 0 | } |
104 | | |
105 | | uint16_t |
106 | | nsSVGGradientFrame::GetSpreadMethod() |
107 | 0 | { |
108 | 0 | return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); |
109 | 0 | } |
110 | | |
111 | | const nsSVGAnimatedTransformList* |
112 | | nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault) |
113 | 0 | { |
114 | 0 | nsSVGAnimatedTransformList *thisTransformList = |
115 | 0 | static_cast<dom::SVGGradientElement*>(GetContent())->GetAnimatedTransformList(); |
116 | 0 |
|
117 | 0 | if (thisTransformList && thisTransformList->IsExplicitlySet()) |
118 | 0 | return thisTransformList; |
119 | 0 | |
120 | 0 | // Before we recurse, make sure we'll break reference loops and over long |
121 | 0 | // reference chains: |
122 | 0 | static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; |
123 | 0 | AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, |
124 | 0 | &sRefChainLengthCounter); |
125 | 0 | if (MOZ_UNLIKELY(!refChainGuard.Reference())) { |
126 | 0 | // Break reference chain |
127 | 0 | return static_cast<const dom::SVGGradientElement*>(aDefault)-> |
128 | 0 | mGradientTransform.get(); |
129 | 0 | } |
130 | 0 | |
131 | 0 | nsSVGGradientFrame *next = GetReferencedGradient(); |
132 | 0 |
|
133 | 0 | return next ? next->GetGradientTransformList(aDefault) |
134 | 0 | : static_cast<const dom::SVGGradientElement*>(aDefault)-> |
135 | 0 | mGradientTransform.get(); |
136 | 0 | } |
137 | | |
138 | | gfxMatrix |
139 | | nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, |
140 | | const gfxRect *aOverrideBounds) |
141 | 0 | { |
142 | 0 | gfxMatrix bboxMatrix; |
143 | 0 |
|
144 | 0 | uint16_t gradientUnits = GetGradientUnits(); |
145 | 0 | if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { |
146 | 0 | NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, |
147 | 0 | "Unknown gradientUnits type"); |
148 | 0 | // objectBoundingBox is the default anyway |
149 | 0 |
|
150 | 0 | gfxRect bbox = |
151 | 0 | aOverrideBounds |
152 | 0 | ? *aOverrideBounds |
153 | 0 | : nsSVGUtils::GetBBox(aSource, nsSVGUtils::eUseFrameBoundsForOuterSVG | |
154 | 0 | nsSVGUtils::eBBoxIncludeFillGeometry); |
155 | 0 | bboxMatrix = |
156 | 0 | gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); |
157 | 0 | } |
158 | 0 |
|
159 | 0 | const nsSVGAnimatedTransformList* animTransformList = |
160 | 0 | GetGradientTransformList(GetContent()); |
161 | 0 | if (!animTransformList) |
162 | 0 | return bboxMatrix; |
163 | 0 | |
164 | 0 | gfxMatrix gradientTransform = |
165 | 0 | animTransformList->GetAnimValue().GetConsolidationMatrix(); |
166 | 0 | return bboxMatrix.PreMultiply(gradientTransform); |
167 | 0 | } |
168 | | |
169 | | dom::SVGLinearGradientElement* |
170 | | nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, |
171 | | dom::SVGLinearGradientElement* aDefault) |
172 | 0 | { |
173 | 0 | // If this was a linear gradient with the required length, we would have |
174 | 0 | // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength. |
175 | 0 | // Since we didn't find the length, continue looking down the chain. |
176 | 0 |
|
177 | 0 | // Before we recurse, make sure we'll break reference loops and over long |
178 | 0 | // reference chains: |
179 | 0 | static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; |
180 | 0 | AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, |
181 | 0 | &sRefChainLengthCounter); |
182 | 0 | if (MOZ_UNLIKELY(!refChainGuard.Reference())) { |
183 | 0 | // Break reference chain |
184 | 0 | return aDefault; |
185 | 0 | } |
186 | 0 | |
187 | 0 | nsSVGGradientFrame *next = GetReferencedGradient(); |
188 | 0 | return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; |
189 | 0 | } |
190 | | |
191 | | dom::SVGRadialGradientElement* |
192 | | nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, |
193 | | dom::SVGRadialGradientElement* aDefault) |
194 | 0 | { |
195 | 0 | // If this was a radial gradient with the required length, we would have |
196 | 0 | // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength. |
197 | 0 | // Since we didn't find the length, continue looking down the chain. |
198 | 0 |
|
199 | 0 | // Before we recurse, make sure we'll break reference loops and over long |
200 | 0 | // reference chains: |
201 | 0 | static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; |
202 | 0 | AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, |
203 | 0 | &sRefChainLengthCounter); |
204 | 0 | if (MOZ_UNLIKELY(!refChainGuard.Reference())) { |
205 | 0 | // Break reference chain |
206 | 0 | return aDefault; |
207 | 0 | } |
208 | 0 | |
209 | 0 | nsSVGGradientFrame *next = GetReferencedGradient(); |
210 | 0 | return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; |
211 | 0 | } |
212 | | |
213 | | //---------------------------------------------------------------------- |
214 | | // nsSVGPaintServerFrame methods: |
215 | | |
216 | | //helper |
217 | | static void GetStopInformation(nsIFrame* aStopFrame, |
218 | | float *aOffset, |
219 | | nscolor *aStopColor, |
220 | | float *aStopOpacity) |
221 | 0 | { |
222 | 0 | nsIContent* stopContent = aStopFrame->GetContent(); |
223 | 0 | MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop)); |
224 | 0 |
|
225 | 0 | static_cast<SVGStopElement*>(stopContent)-> |
226 | 0 | GetAnimatedNumberValues(aOffset, nullptr); |
227 | 0 |
|
228 | 0 | const nsStyleSVGReset* styleSVGReset = aStopFrame->StyleSVGReset(); |
229 | 0 | *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f); |
230 | 0 | *aStopColor = styleSVGReset->mStopColor.CalcColor(aStopFrame); |
231 | 0 | *aStopOpacity = styleSVGReset->mStopOpacity; |
232 | 0 | } |
233 | | |
234 | | already_AddRefed<gfxPattern> |
235 | | nsSVGGradientFrame::GetPaintServerPattern(nsIFrame* aSource, |
236 | | const DrawTarget* aDrawTarget, |
237 | | const gfxMatrix& aContextMatrix, |
238 | | nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, |
239 | | float aGraphicOpacity, |
240 | | imgDrawingParams& aImgParams, |
241 | | const gfxRect* aOverrideBounds) |
242 | 0 | { |
243 | 0 | uint16_t gradientUnits = GetGradientUnits(); |
244 | 0 | MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX || |
245 | 0 | gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE); |
246 | 0 | if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { |
247 | 0 | // Set mSource for this consumer. |
248 | 0 | // If this gradient is applied to text, our caller will be the glyph, which |
249 | 0 | // is not an element, so we need to get the parent |
250 | 0 | mSource = aSource->GetContent()->IsText() ? |
251 | 0 | aSource->GetParent() : aSource; |
252 | 0 | } |
253 | 0 |
|
254 | 0 | AutoTArray<nsIFrame*,8> stopFrames; |
255 | 0 | GetStopFrames(&stopFrames); |
256 | 0 |
|
257 | 0 | uint32_t nStops = stopFrames.Length(); |
258 | 0 |
|
259 | 0 | // SVG specification says that no stops should be treated like |
260 | 0 | // the corresponding fill or stroke had "none" specified. |
261 | 0 | if (nStops == 0) { |
262 | 0 | RefPtr<gfxPattern> pattern = new gfxPattern(Color()); |
263 | 0 | return do_AddRef(new gfxPattern(Color())); |
264 | 0 | } |
265 | 0 | |
266 | 0 | if (nStops == 1 || GradientVectorLengthIsZero()) { |
267 | 0 | auto lastStopFrame = stopFrames[nStops-1]; |
268 | 0 | auto svgReset = lastStopFrame->StyleSVGReset(); |
269 | 0 | // The gradient paints a single colour, using the stop-color of the last |
270 | 0 | // gradient step if there are more than one. |
271 | 0 | float stopOpacity = svgReset->mStopOpacity; |
272 | 0 | nscolor stopColor = svgReset->mStopColor.CalcColor(lastStopFrame); |
273 | 0 |
|
274 | 0 | Color stopColor2 = Color::FromABGR(stopColor); |
275 | 0 | stopColor2.a *= stopOpacity * aGraphicOpacity; |
276 | 0 | return do_AddRef(new gfxPattern(stopColor2)); |
277 | 0 | } |
278 | 0 | |
279 | 0 | // Get the transform list (if there is one). We do this after the returns |
280 | 0 | // above since this call can be expensive when "gradientUnits" is set to |
281 | 0 | // "objectBoundingBox" (since that requiring a GetBBox() call). |
282 | 0 | gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); |
283 | 0 |
|
284 | 0 | if (patternMatrix.IsSingular()) { |
285 | 0 | return nullptr; |
286 | 0 | } |
287 | 0 | |
288 | 0 | // revert any vector effect transform so that the gradient appears unchanged |
289 | 0 | if (aFillOrStroke == &nsStyleSVG::mStroke) { |
290 | 0 | gfxMatrix userToOuterSVG; |
291 | 0 | if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) { |
292 | 0 | patternMatrix *= userToOuterSVG; |
293 | 0 | } |
294 | 0 | } |
295 | 0 |
|
296 | 0 | if (!patternMatrix.Invert()) { |
297 | 0 | return nullptr; |
298 | 0 | } |
299 | 0 | |
300 | 0 | RefPtr<gfxPattern> gradient = CreateGradient(); |
301 | 0 | if (!gradient) { |
302 | 0 | return nullptr; |
303 | 0 | } |
304 | 0 | |
305 | 0 | uint16_t aSpread = GetSpreadMethod(); |
306 | 0 | if (aSpread == SVG_SPREADMETHOD_PAD) |
307 | 0 | gradient->SetExtend(ExtendMode::CLAMP); |
308 | 0 | else if (aSpread == SVG_SPREADMETHOD_REFLECT) |
309 | 0 | gradient->SetExtend(ExtendMode::REFLECT); |
310 | 0 | else if (aSpread == SVG_SPREADMETHOD_REPEAT) |
311 | 0 | gradient->SetExtend(ExtendMode::REPEAT); |
312 | 0 |
|
313 | 0 | gradient->SetMatrix(patternMatrix); |
314 | 0 |
|
315 | 0 | // setup stops |
316 | 0 | float lastOffset = 0.0f; |
317 | 0 |
|
318 | 0 | for (uint32_t i = 0; i < nStops; i++) { |
319 | 0 | float offset, stopOpacity; |
320 | 0 | nscolor stopColor; |
321 | 0 |
|
322 | 0 | GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity); |
323 | 0 |
|
324 | 0 | if (offset < lastOffset) |
325 | 0 | offset = lastOffset; |
326 | 0 | else |
327 | 0 | lastOffset = offset; |
328 | 0 |
|
329 | 0 | Color stopColor2 = Color::FromABGR(stopColor); |
330 | 0 | stopColor2.a *= stopOpacity * aGraphicOpacity; |
331 | 0 | gradient->AddColorStop(offset, stopColor2); |
332 | 0 | } |
333 | 0 |
|
334 | 0 | return gradient.forget(); |
335 | 0 | } |
336 | | |
337 | | // Private (helper) methods |
338 | | |
339 | | nsSVGGradientFrame * |
340 | | nsSVGGradientFrame::GetReferencedGradient() |
341 | 0 | { |
342 | 0 | if (mNoHRefURI) |
343 | 0 | return nullptr; |
344 | 0 | |
345 | 0 | SVGTemplateElementObserver* observer = |
346 | 0 | GetProperty(SVGObserverUtils::HrefToTemplateProperty()); |
347 | 0 |
|
348 | 0 | if (!observer) { |
349 | 0 | // Fetch our gradient element's href or xlink:href attribute |
350 | 0 | dom::SVGGradientElement* grad = |
351 | 0 | static_cast<dom::SVGGradientElement*>(GetContent()); |
352 | 0 | nsAutoString href; |
353 | 0 | if (grad->mStringAttributes[dom::SVGGradientElement::HREF] |
354 | 0 | .IsExplicitlySet()) { |
355 | 0 | grad->mStringAttributes[dom::SVGGradientElement::HREF] |
356 | 0 | .GetAnimValue(href, grad); |
357 | 0 | } else { |
358 | 0 | grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF] |
359 | 0 | .GetAnimValue(href, grad); |
360 | 0 | } |
361 | 0 |
|
362 | 0 | if (href.IsEmpty()) { |
363 | 0 | mNoHRefURI = true; |
364 | 0 | return nullptr; // no URL |
365 | 0 | } |
366 | 0 | |
367 | 0 | // Convert href to an nsIURI |
368 | 0 | nsCOMPtr<nsIURI> targetURI; |
369 | 0 | nsCOMPtr<nsIURI> base = mContent->GetBaseURI(); |
370 | 0 | nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, |
371 | 0 | mContent->GetUncomposedDoc(), base); |
372 | 0 |
|
373 | 0 | // There's no clear refererer policy spec about non-CSS SVG resource references |
374 | 0 | // Bug 1415044 to investigate which referrer we should use |
375 | 0 | RefPtr<URLAndReferrerInfo> target = |
376 | 0 | new URLAndReferrerInfo(targetURI, |
377 | 0 | mContent->OwnerDoc()->GetDocumentURI(), |
378 | 0 | mContent->OwnerDoc()->GetReferrerPolicy()); |
379 | 0 |
|
380 | 0 | observer = SVGObserverUtils::GetTemplateElementObserver(target, this, |
381 | 0 | SVGObserverUtils::HrefToTemplateProperty()); |
382 | 0 | if (!observer) { |
383 | 0 | return nullptr; |
384 | 0 | } |
385 | 0 | } |
386 | 0 | |
387 | 0 | nsIFrame* result = observer->GetReferencedFrame(); |
388 | 0 | if (!result) |
389 | 0 | return nullptr; |
390 | 0 | |
391 | 0 | LayoutFrameType frameType = result->Type(); |
392 | 0 | if (frameType != LayoutFrameType::SVGLinearGradient && |
393 | 0 | frameType != LayoutFrameType::SVGRadialGradient) |
394 | 0 | return nullptr; |
395 | 0 | |
396 | 0 | return static_cast<nsSVGGradientFrame*>(result); |
397 | 0 | } |
398 | | |
399 | | void |
400 | | nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames) |
401 | 0 | { |
402 | 0 | nsIFrame *stopFrame = nullptr; |
403 | 0 | for (stopFrame = mFrames.FirstChild(); stopFrame; |
404 | 0 | stopFrame = stopFrame->GetNextSibling()) { |
405 | 0 | if (stopFrame->IsSVGStopFrame()) { |
406 | 0 | aStopFrames->AppendElement(stopFrame); |
407 | 0 | } |
408 | 0 | } |
409 | 0 | if (aStopFrames->Length() > 0) { |
410 | 0 | return; |
411 | 0 | } |
412 | 0 | |
413 | 0 | // Our gradient element doesn't have stops - try to "inherit" them |
414 | 0 | |
415 | 0 | // Before we recurse, make sure we'll break reference loops and over long |
416 | 0 | // reference chains: |
417 | 0 | static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; |
418 | 0 | AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, |
419 | 0 | &sRefChainLengthCounter); |
420 | 0 | if (MOZ_UNLIKELY(!refChainGuard.Reference())) { |
421 | 0 | // Break reference chain |
422 | 0 | return; |
423 | 0 | } |
424 | 0 | |
425 | 0 | nsSVGGradientFrame* next = GetReferencedGradient(); |
426 | 0 | if (next) { |
427 | 0 | next->GetStopFrames(aStopFrames); |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | // ------------------------------------------------------------------------- |
432 | | // Linear Gradients |
433 | | // ------------------------------------------------------------------------- |
434 | | |
435 | | #ifdef DEBUG |
436 | | void |
437 | | nsSVGLinearGradientFrame::Init(nsIContent* aContent, |
438 | | nsContainerFrame* aParent, |
439 | | nsIFrame* aPrevInFlow) |
440 | | { |
441 | | NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient), |
442 | | "Content is not an SVG linearGradient"); |
443 | | |
444 | | nsSVGGradientFrame::Init(aContent, aParent, aPrevInFlow); |
445 | | } |
446 | | #endif /* DEBUG */ |
447 | | |
448 | | nsresult |
449 | | nsSVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID, |
450 | | nsAtom* aAttribute, |
451 | | int32_t aModType) |
452 | 0 | { |
453 | 0 | if (aNameSpaceID == kNameSpaceID_None && |
454 | 0 | (aAttribute == nsGkAtoms::x1 || |
455 | 0 | aAttribute == nsGkAtoms::y1 || |
456 | 0 | aAttribute == nsGkAtoms::x2 || |
457 | 0 | aAttribute == nsGkAtoms::y2)) { |
458 | 0 | SVGObserverUtils::InvalidateDirectRenderingObservers(this); |
459 | 0 | } |
460 | 0 |
|
461 | 0 | return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, |
462 | 0 | aAttribute, aModType); |
463 | 0 | } |
464 | | |
465 | | //---------------------------------------------------------------------- |
466 | | |
467 | | float |
468 | | nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) |
469 | 0 | { |
470 | 0 | dom::SVGLinearGradientElement* lengthElement = |
471 | 0 | GetLinearGradientWithLength(aIndex, |
472 | 0 | static_cast<dom::SVGLinearGradientElement*>(GetContent())); |
473 | 0 | // We passed in mContent as a fallback, so, assuming mContent is non-null, the |
474 | 0 | // return value should also be non-null. |
475 | 0 | MOZ_ASSERT(lengthElement, |
476 | 0 | "Got unexpected null element from GetLinearGradientWithLength"); |
477 | 0 | const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex]; |
478 | 0 |
|
479 | 0 | // Object bounding box units are handled by setting the appropriate |
480 | 0 | // transform in GetGradientTransform, but we need to handle user |
481 | 0 | // space units as part of the individual Get* routines. Fixes 323669. |
482 | 0 |
|
483 | 0 | uint16_t gradientUnits = GetGradientUnits(); |
484 | 0 | if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { |
485 | 0 | return nsSVGUtils::UserSpace(mSource, &length); |
486 | 0 | } |
487 | 0 | |
488 | 0 | NS_ASSERTION( |
489 | 0 | gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, |
490 | 0 | "Unknown gradientUnits type"); |
491 | 0 |
|
492 | 0 | return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr)); |
493 | 0 | } |
494 | | |
495 | | dom::SVGLinearGradientElement* |
496 | | nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, |
497 | | dom::SVGLinearGradientElement* aDefault) |
498 | 0 | { |
499 | 0 | dom::SVGLinearGradientElement* thisElement = |
500 | 0 | static_cast<dom::SVGLinearGradientElement*>(GetContent()); |
501 | 0 | const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; |
502 | 0 |
|
503 | 0 | if (length.IsExplicitlySet()) { |
504 | 0 | return thisElement; |
505 | 0 | } |
506 | 0 | |
507 | 0 | return nsSVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault); |
508 | 0 | } |
509 | | |
510 | | bool |
511 | | nsSVGLinearGradientFrame::GradientVectorLengthIsZero() |
512 | 0 | { |
513 | 0 | return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) == |
514 | 0 | GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) && |
515 | 0 | GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) == |
516 | 0 | GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); |
517 | 0 | } |
518 | | |
519 | | already_AddRefed<gfxPattern> |
520 | | nsSVGLinearGradientFrame::CreateGradient() |
521 | 0 | { |
522 | 0 | float x1, y1, x2, y2; |
523 | 0 |
|
524 | 0 | x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1); |
525 | 0 | y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1); |
526 | 0 | x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2); |
527 | 0 | y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); |
528 | 0 |
|
529 | 0 | RefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2); |
530 | 0 | return pattern.forget(); |
531 | 0 | } |
532 | | |
533 | | // ------------------------------------------------------------------------- |
534 | | // Radial Gradients |
535 | | // ------------------------------------------------------------------------- |
536 | | |
537 | | #ifdef DEBUG |
538 | | void |
539 | | nsSVGRadialGradientFrame::Init(nsIContent* aContent, |
540 | | nsContainerFrame* aParent, |
541 | | nsIFrame* aPrevInFlow) |
542 | | { |
543 | | NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient), |
544 | | "Content is not an SVG radialGradient"); |
545 | | |
546 | | nsSVGGradientFrame::Init(aContent, aParent, aPrevInFlow); |
547 | | } |
548 | | #endif /* DEBUG */ |
549 | | |
550 | | nsresult |
551 | | nsSVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID, |
552 | | nsAtom* aAttribute, |
553 | | int32_t aModType) |
554 | 0 | { |
555 | 0 | if (aNameSpaceID == kNameSpaceID_None && |
556 | 0 | (aAttribute == nsGkAtoms::r || |
557 | 0 | aAttribute == nsGkAtoms::cx || |
558 | 0 | aAttribute == nsGkAtoms::cy || |
559 | 0 | aAttribute == nsGkAtoms::fx || |
560 | 0 | aAttribute == nsGkAtoms::fy)) { |
561 | 0 | SVGObserverUtils::InvalidateDirectRenderingObservers(this); |
562 | 0 | } |
563 | 0 |
|
564 | 0 | return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, |
565 | 0 | aAttribute, aModType); |
566 | 0 | } |
567 | | |
568 | | //---------------------------------------------------------------------- |
569 | | |
570 | | float |
571 | | nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) |
572 | 0 | { |
573 | 0 | dom::SVGRadialGradientElement* lengthElement = |
574 | 0 | GetRadialGradientWithLength(aIndex, |
575 | 0 | static_cast<dom::SVGRadialGradientElement*>(GetContent())); |
576 | 0 | // We passed in mContent as a fallback, so, assuming mContent is non-null, |
577 | 0 | // the return value should also be non-null. |
578 | 0 | MOZ_ASSERT(lengthElement, |
579 | 0 | "Got unexpected null element from GetRadialGradientWithLength"); |
580 | 0 | return GetLengthValueFromElement(aIndex, *lengthElement); |
581 | 0 | } |
582 | | |
583 | | float |
584 | | nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue) |
585 | 0 | { |
586 | 0 | dom::SVGRadialGradientElement* lengthElement = |
587 | 0 | GetRadialGradientWithLength(aIndex, nullptr); |
588 | 0 |
|
589 | 0 | return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) |
590 | 0 | : aDefaultValue; |
591 | 0 | } |
592 | | |
593 | | float |
594 | | nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex, |
595 | | dom::SVGRadialGradientElement& aElement) |
596 | 0 | { |
597 | 0 | const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex]; |
598 | 0 |
|
599 | 0 | // Object bounding box units are handled by setting the appropriate |
600 | 0 | // transform in GetGradientTransform, but we need to handle user |
601 | 0 | // space units as part of the individual Get* routines. Fixes 323669. |
602 | 0 |
|
603 | 0 | uint16_t gradientUnits = GetGradientUnits(); |
604 | 0 | if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { |
605 | 0 | return nsSVGUtils::UserSpace(mSource, &length); |
606 | 0 | } |
607 | 0 | |
608 | 0 | NS_ASSERTION( |
609 | 0 | gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, |
610 | 0 | "Unknown gradientUnits type"); |
611 | 0 |
|
612 | 0 | return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr)); |
613 | 0 | } |
614 | | |
615 | | dom::SVGRadialGradientElement* |
616 | | nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, |
617 | | dom::SVGRadialGradientElement* aDefault) |
618 | 0 | { |
619 | 0 | dom::SVGRadialGradientElement* thisElement = |
620 | 0 | static_cast<dom::SVGRadialGradientElement*>(GetContent()); |
621 | 0 | const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; |
622 | 0 |
|
623 | 0 | if (length.IsExplicitlySet()) { |
624 | 0 | return thisElement; |
625 | 0 | } |
626 | 0 | |
627 | 0 | return nsSVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault); |
628 | 0 | } |
629 | | |
630 | | bool |
631 | | nsSVGRadialGradientFrame::GradientVectorLengthIsZero() |
632 | 0 | { |
633 | 0 | return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0; |
634 | 0 | } |
635 | | |
636 | | already_AddRefed<gfxPattern> |
637 | | nsSVGRadialGradientFrame::CreateGradient() |
638 | 0 | { |
639 | 0 | float cx, cy, r, fx, fy, fr; |
640 | 0 |
|
641 | 0 | cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); |
642 | 0 | cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); |
643 | 0 | r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); |
644 | 0 | // If fx or fy are not set, use cx/cy instead |
645 | 0 | fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); |
646 | 0 | fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); |
647 | 0 | fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR); |
648 | 0 |
|
649 | 0 | if (fx != cx || fy != cy) { |
650 | 0 | // The focal point (fFx and fFy) must be clamped to be *inside* - not on - |
651 | 0 | // the circumference of the gradient or we'll get rendering anomalies. We |
652 | 0 | // calculate the distance from the focal point to the gradient center and |
653 | 0 | // make sure it is *less* than the gradient radius. |
654 | 0 | // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point |
655 | 0 | // representation divided by 2 to ensure that we get different cairo |
656 | 0 | // fractions |
657 | 0 | double dMax = std::max(0.0, r - 1.0/128); |
658 | 0 | float dx = fx - cx; |
659 | 0 | float dy = fy - cy; |
660 | 0 | double d = sqrt((dx * dx) + (dy * dy)); |
661 | 0 | if (d > dMax) { |
662 | 0 | double angle = atan2(dy, dx); |
663 | 0 | fx = (float)(dMax * cos(angle)) + cx; |
664 | 0 | fy = (float)(dMax * sin(angle)) + cy; |
665 | 0 | } |
666 | 0 | } |
667 | 0 |
|
668 | 0 | RefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, fr, cx, cy, r); |
669 | 0 | return pattern.forget(); |
670 | 0 | } |
671 | | |
672 | | // ------------------------------------------------------------------------- |
673 | | // Public functions |
674 | | // ------------------------------------------------------------------------- |
675 | | |
676 | | nsIFrame* |
677 | | NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, |
678 | | ComputedStyle* aStyle) |
679 | 0 | { |
680 | 0 | return new (aPresShell) nsSVGLinearGradientFrame(aStyle); |
681 | 0 | } |
682 | | |
683 | | NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame) |
684 | | |
685 | | nsIFrame* |
686 | | NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, |
687 | | ComputedStyle* aStyle) |
688 | 0 | { |
689 | 0 | return new (aPresShell) nsSVGRadialGradientFrame(aStyle); |
690 | 0 | } |
691 | | |
692 | | NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame) |