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 #include "mozilla/ArrayUtils.h"
8
9 #include "nsSVGElement.h"
10 #include "nsGkAtoms.h"
11 #include "nsSVGNumber2.h"
12 #include "nsSVGNumberPair.h"
13 #include "nsSVGInteger.h"
14 #include "nsSVGIntegerPair.h"
15 #include "nsSVGBoolean.h"
16 #include "nsCOMPtr.h"
17 #include "nsSVGFilterInstance.h"
18 #include "nsSVGEnum.h"
19 #include "SVGNumberList.h"
20 #include "SVGAnimatedNumberList.h"
21 #include "DOMSVGAnimatedNumberList.h"
22 #include "nsSVGFilters.h"
23 #include "nsLayoutUtils.h"
24 #include "nsSVGUtils.h"
25 #include "nsStyleContext.h"
26 #include "nsIFrame.h"
27 #include "imgIContainer.h"
28 #include "mozilla/dom/SVGFilterElement.h"
29 #include "nsSVGString.h"
30 #include "SVGContentUtils.h"
31 #include <algorithm>
32 #include "mozilla/dom/SVGAnimatedLength.h"
33 #include "mozilla/dom/SVGComponentTransferFunctionElement.h"
34 #include "mozilla/dom/SVGFEDistantLightElement.h"
35 #include "mozilla/dom/SVGFEFuncAElementBinding.h"
36 #include "mozilla/dom/SVGFEFuncBElementBinding.h"
37 #include "mozilla/dom/SVGFEFuncGElementBinding.h"
38 #include "mozilla/dom/SVGFEFuncRElementBinding.h"
39 #include "mozilla/dom/SVGFEPointLightElement.h"
40 #include "mozilla/dom/SVGFESpotLightElement.h"
41 #include "mozilla/dom/SVGLengthBinding.h"
42
43 #if defined(XP_WIN)
44 // Prevent Windows redefining LoadImage
45 #undef LoadImage
46 #endif
47
48 using namespace mozilla;
49 using namespace mozilla::dom;
50 using namespace mozilla::gfx;
51
52 //--------------------Filter Element Base Class-----------------------
53
54 nsSVGElement::LengthInfo nsSVGFE::sLengthInfo[4] = {
55 {&nsGkAtoms::x, 0, SVGLengthBinding::SVG_LENGTHTYPE_PERCENTAGE,
56 SVGContentUtils::X},
57 {&nsGkAtoms::y, 0, SVGLengthBinding::SVG_LENGTHTYPE_PERCENTAGE,
58 SVGContentUtils::Y},
59 {&nsGkAtoms::width, 100, SVGLengthBinding::SVG_LENGTHTYPE_PERCENTAGE,
60 SVGContentUtils::X},
61 {&nsGkAtoms::height, 100, SVGLengthBinding::SVG_LENGTHTYPE_PERCENTAGE,
62 SVGContentUtils::Y}};
63
64 //----------------------------------------------------------------------
65 // nsISupports methods
66
67 NS_IMPL_ADDREF_INHERITED(nsSVGFE, nsSVGFEBase)
68 NS_IMPL_RELEASE_INHERITED(nsSVGFE, nsSVGFEBase)
69
70 NS_INTERFACE_MAP_BEGIN(nsSVGFE)
71 // nsISupports is an ambiguous base of nsSVGFE so we have to work
72 // around that
73 if (aIID.Equals(NS_GET_IID(nsSVGFE)))
74 foundInterface = static_cast<nsISupports*>(static_cast<void*>(this));
75 else
NS_INTERFACE_MAP_END_INHERITING(nsSVGFEBase)76 NS_INTERFACE_MAP_END_INHERITING(nsSVGFEBase)
77
78 //----------------------------------------------------------------------
79 // Implementation
80
81 void nsSVGFE::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources) {}
82
OutputIsTainted(const nsTArray<bool> & aInputsAreTainted,nsIPrincipal * aReferencePrincipal)83 bool nsSVGFE::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted,
84 nsIPrincipal* aReferencePrincipal) {
85 // This is the default implementation for OutputIsTainted.
86 // Our output is tainted if we have at least one tainted input.
87 for (uint32_t i = 0; i < aInputsAreTainted.Length(); i++) {
88 if (aInputsAreTainted[i]) {
89 return true;
90 }
91 }
92 return false;
93 }
94
AttributeAffectsRendering(int32_t aNameSpaceID,nsAtom * aAttribute) const95 bool nsSVGFE::AttributeAffectsRendering(int32_t aNameSpaceID,
96 nsAtom* aAttribute) const {
97 return aNameSpaceID == kNameSpaceID_None &&
98 (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
99 aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
100 aAttribute == nsGkAtoms::result);
101 }
102
X()103 already_AddRefed<SVGAnimatedLength> nsSVGFE::X() {
104 return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
105 }
106
Y()107 already_AddRefed<SVGAnimatedLength> nsSVGFE::Y() {
108 return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
109 }
110
Width()111 already_AddRefed<SVGAnimatedLength> nsSVGFE::Width() {
112 return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
113 }
114
Height()115 already_AddRefed<SVGAnimatedLength> nsSVGFE::Height() {
116 return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
117 }
118
Result()119 already_AddRefed<SVGAnimatedString> nsSVGFE::Result() {
120 return GetResultImageName().ToDOMAnimatedString(this);
121 }
122
123 //----------------------------------------------------------------------
124 // nsIContent methods
125
NS_IMETHODIMP_(bool)126 NS_IMETHODIMP_(bool)
127 nsSVGFE::IsAttributeMapped(const nsAtom* name) const {
128 static const MappedAttributeEntry* const map[] = {sFiltersMap};
129
130 return FindAttributeDependence(name, map) ||
131 nsSVGFEBase::IsAttributeMapped(name);
132 }
133
134 //----------------------------------------------------------------------
135 // nsSVGElement methods
136
StyleIsSetToSRGB()137 bool nsSVGFE::StyleIsSetToSRGB() {
138 nsIFrame* frame = GetPrimaryFrame();
139 if (!frame) return false;
140
141 nsStyleContext* style = frame->StyleContext();
142 return style->StyleSVG()->mColorInterpolationFilters ==
143 NS_STYLE_COLOR_INTERPOLATION_SRGB;
144 }
145
HasValidDimensions() const146 /* virtual */ bool nsSVGFE::HasValidDimensions() const {
147 return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
148 mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
149 (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
150 mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
151 }
152
GetKernelUnitLength(nsSVGFilterInstance * aInstance,nsSVGNumberPair * aKernelUnitLength)153 Size nsSVGFE::GetKernelUnitLength(nsSVGFilterInstance* aInstance,
154 nsSVGNumberPair* aKernelUnitLength) {
155 if (!aKernelUnitLength->IsExplicitlySet()) {
156 return Size(1, 1);
157 }
158
159 float kernelX = aInstance->GetPrimitiveNumber(
160 SVGContentUtils::X, aKernelUnitLength, nsSVGNumberPair::eFirst);
161 float kernelY = aInstance->GetPrimitiveNumber(
162 SVGContentUtils::Y, aKernelUnitLength, nsSVGNumberPair::eSecond);
163 return Size(kernelX, kernelY);
164 }
165
GetLengthInfo()166 nsSVGElement::LengthAttributesInfo nsSVGFE::GetLengthInfo() {
167 return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
168 ArrayLength(sLengthInfo));
169 }
170
171 namespace mozilla {
172 namespace dom {
173
174 nsSVGElement::NumberListInfo
175 SVGComponentTransferFunctionElement::sNumberListInfo[1] = {
176 {&nsGkAtoms::tableValues}};
177
178 nsSVGElement::NumberInfo SVGComponentTransferFunctionElement::sNumberInfo[5] = {
179 {&nsGkAtoms::slope, 1, false},
180 {&nsGkAtoms::intercept, 0, false},
181 {&nsGkAtoms::amplitude, 1, false},
182 {&nsGkAtoms::exponent, 1, false},
183 {&nsGkAtoms::offset, 0, false}};
184
185 nsSVGEnumMapping SVGComponentTransferFunctionElement::sTypeMap[] = {
186 {&nsGkAtoms::identity, SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY},
187 {&nsGkAtoms::table, SVG_FECOMPONENTTRANSFER_TYPE_TABLE},
188 {&nsGkAtoms::discrete, SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE},
189 {&nsGkAtoms::linear, SVG_FECOMPONENTTRANSFER_TYPE_LINEAR},
190 {&nsGkAtoms::gamma, SVG_FECOMPONENTTRANSFER_TYPE_GAMMA},
191 {nullptr, 0}};
192
193 nsSVGElement::EnumInfo SVGComponentTransferFunctionElement::sEnumInfo[1] = {
194 {&nsGkAtoms::type, sTypeMap, SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY}};
195
196 //----------------------------------------------------------------------
197 // nsISupports methods
198
199 NS_IMPL_ADDREF_INHERITED(SVGComponentTransferFunctionElement,
200 SVGComponentTransferFunctionElementBase)
201 NS_IMPL_RELEASE_INHERITED(SVGComponentTransferFunctionElement,
202 SVGComponentTransferFunctionElementBase)
203
204 NS_INTERFACE_MAP_BEGIN(SVGComponentTransferFunctionElement)
205 // nsISupports is an ambiguous base of nsSVGFE so we have to work
206 // around that
207 if (aIID.Equals(NS_GET_IID(SVGComponentTransferFunctionElement)))
208 foundInterface = static_cast<nsISupports*>(static_cast<void*>(this));
209 else
NS_INTERFACE_MAP_END_INHERITING(SVGComponentTransferFunctionElementBase)210 NS_INTERFACE_MAP_END_INHERITING(SVGComponentTransferFunctionElementBase)
211
212 //----------------------------------------------------------------------
213 // nsFEUnstyledElement methods
214
215 bool SVGComponentTransferFunctionElement::AttributeAffectsRendering(
216 int32_t aNameSpaceID, nsAtom* aAttribute) const {
217 return aNameSpaceID == kNameSpaceID_None &&
218 (aAttribute == nsGkAtoms::tableValues ||
219 aAttribute == nsGkAtoms::slope ||
220 aAttribute == nsGkAtoms::intercept ||
221 aAttribute == nsGkAtoms::amplitude ||
222 aAttribute == nsGkAtoms::exponent ||
223 aAttribute == nsGkAtoms::offset || aAttribute == nsGkAtoms::type);
224 }
225
226 //----------------------------------------------------------------------
227
228 already_AddRefed<SVGAnimatedEnumeration>
Type()229 SVGComponentTransferFunctionElement::Type() {
230 return mEnumAttributes[TYPE].ToDOMAnimatedEnum(this);
231 }
232
233 already_AddRefed<DOMSVGAnimatedNumberList>
TableValues()234 SVGComponentTransferFunctionElement::TableValues() {
235 return DOMSVGAnimatedNumberList::GetDOMWrapper(
236 &mNumberListAttributes[TABLEVALUES], this, TABLEVALUES);
237 }
238
239 already_AddRefed<SVGAnimatedNumber>
Slope()240 SVGComponentTransferFunctionElement::Slope() {
241 return mNumberAttributes[SLOPE].ToDOMAnimatedNumber(this);
242 }
243
244 already_AddRefed<SVGAnimatedNumber>
Intercept()245 SVGComponentTransferFunctionElement::Intercept() {
246 return mNumberAttributes[INTERCEPT].ToDOMAnimatedNumber(this);
247 }
248
249 already_AddRefed<SVGAnimatedNumber>
Amplitude()250 SVGComponentTransferFunctionElement::Amplitude() {
251 return mNumberAttributes[AMPLITUDE].ToDOMAnimatedNumber(this);
252 }
253
254 already_AddRefed<SVGAnimatedNumber>
Exponent()255 SVGComponentTransferFunctionElement::Exponent() {
256 return mNumberAttributes[EXPONENT].ToDOMAnimatedNumber(this);
257 }
258
259 already_AddRefed<SVGAnimatedNumber>
Offset()260 SVGComponentTransferFunctionElement::Offset() {
261 return mNumberAttributes[OFFSET].ToDOMAnimatedNumber(this);
262 }
263
ComputeAttributes()264 AttributeMap SVGComponentTransferFunctionElement::ComputeAttributes() {
265 uint32_t type = mEnumAttributes[TYPE].GetAnimValue();
266
267 float slope, intercept, amplitude, exponent, offset;
268 GetAnimatedNumberValues(&slope, &intercept, &litude, &exponent, &offset,
269 nullptr);
270
271 const SVGNumberList& tableValues =
272 mNumberListAttributes[TABLEVALUES].GetAnimValue();
273
274 AttributeMap map;
275 map.Set(eComponentTransferFunctionType, type);
276 map.Set(eComponentTransferFunctionSlope, slope);
277 map.Set(eComponentTransferFunctionIntercept, intercept);
278 map.Set(eComponentTransferFunctionAmplitude, amplitude);
279 map.Set(eComponentTransferFunctionExponent, exponent);
280 map.Set(eComponentTransferFunctionOffset, offset);
281 if (tableValues.Length()) {
282 map.Set(eComponentTransferFunctionTableValues, &tableValues[0],
283 tableValues.Length());
284 } else {
285 map.Set(eComponentTransferFunctionTableValues, nullptr, 0);
286 }
287 return map;
288 }
289
290 //----------------------------------------------------------------------
291 // nsSVGElement methods
292
293 nsSVGElement::NumberListAttributesInfo
GetNumberListInfo()294 SVGComponentTransferFunctionElement::GetNumberListInfo() {
295 return NumberListAttributesInfo(mNumberListAttributes, sNumberListInfo,
296 ArrayLength(sNumberListInfo));
297 }
298
299 nsSVGElement::EnumAttributesInfo
GetEnumInfo()300 SVGComponentTransferFunctionElement::GetEnumInfo() {
301 return EnumAttributesInfo(mEnumAttributes, sEnumInfo, ArrayLength(sEnumInfo));
302 }
303
304 nsSVGElement::NumberAttributesInfo
GetNumberInfo()305 SVGComponentTransferFunctionElement::GetNumberInfo() {
306 return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
307 ArrayLength(sNumberInfo));
308 }
309
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)310 /* virtual */ JSObject* SVGFEFuncRElement::WrapNode(
311 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
312 return SVGFEFuncRElementBinding::Wrap(aCx, this, aGivenProto);
313 }
314
315 } // namespace dom
316 } // namespace mozilla
317
318 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncR)
319
320 namespace mozilla {
321 namespace dom {
322
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncRElement)323 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncRElement)
324
325 /* virtual */ JSObject* SVGFEFuncGElement::WrapNode(
326 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
327 return SVGFEFuncGElementBinding::Wrap(aCx, this, aGivenProto);
328 }
329
330 } // namespace dom
331 } // namespace mozilla
332
333 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncG)
334
335 namespace mozilla {
336 namespace dom {
337
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncGElement)338 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncGElement)
339
340 /* virtual */ JSObject* SVGFEFuncBElement::WrapNode(
341 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
342 return SVGFEFuncBElementBinding::Wrap(aCx, this, aGivenProto);
343 }
344
345 } // namespace dom
346 } // namespace mozilla
347
348 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncB)
349
350 namespace mozilla {
351 namespace dom {
352
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncBElement)353 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncBElement)
354
355 /* virtual */ JSObject* SVGFEFuncAElement::WrapNode(
356 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
357 return SVGFEFuncAElementBinding::Wrap(aCx, this, aGivenProto);
358 }
359
360 } // namespace dom
361 } // namespace mozilla
362
363 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEFuncA)
364
365 namespace mozilla {
366 namespace dom {
367
368 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEFuncAElement)
369
370 } // namespace dom
371 } // namespace mozilla
372
373 //--------------------------------------------------------------------
374 //
375 nsSVGElement::NumberInfo nsSVGFELightingElement::sNumberInfo[4] = {
376 {&nsGkAtoms::surfaceScale, 1, false},
377 {&nsGkAtoms::diffuseConstant, 1, false},
378 {&nsGkAtoms::specularConstant, 1, false},
379 {&nsGkAtoms::specularExponent, 1, false}};
380
381 nsSVGElement::NumberPairInfo nsSVGFELightingElement::sNumberPairInfo[1] = {
382 {&nsGkAtoms::kernelUnitLength, 0, 0}};
383
384 nsSVGElement::StringInfo nsSVGFELightingElement::sStringInfo[2] = {
385 {&nsGkAtoms::result, kNameSpaceID_None, true},
386 {&nsGkAtoms::in, kNameSpaceID_None, true}};
387
388 //----------------------------------------------------------------------
389 // Implementation
390
NS_IMETHODIMP_(bool)391 NS_IMETHODIMP_(bool)
392 nsSVGFELightingElement::IsAttributeMapped(const nsAtom* name) const {
393 static const MappedAttributeEntry* const map[] = {sLightingEffectsMap};
394
395 return FindAttributeDependence(name, map) ||
396 nsSVGFELightingElementBase::IsAttributeMapped(name);
397 }
398
GetSourceImageNames(nsTArray<nsSVGStringInfo> & aSources)399 void nsSVGFELightingElement::GetSourceImageNames(
400 nsTArray<nsSVGStringInfo>& aSources) {
401 aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
402 }
403
ComputeLightAttributes(nsSVGFilterInstance * aInstance)404 AttributeMap nsSVGFELightingElement::ComputeLightAttributes(
405 nsSVGFilterInstance* aInstance) {
406 // find specified light
407 for (nsCOMPtr<nsIContent> child = nsINode::GetFirstChild(); child;
408 child = child->GetNextSibling()) {
409 if (child->IsAnyOfSVGElements(nsGkAtoms::feDistantLight,
410 nsGkAtoms::fePointLight,
411 nsGkAtoms::feSpotLight)) {
412 return static_cast<SVGFELightElement*>(child.get())
413 ->ComputeLightAttributes(aInstance);
414 }
415 }
416
417 AttributeMap map;
418 map.Set(eLightType, (uint32_t)eLightTypeNone);
419 return map;
420 }
421
AddLightingAttributes(const FilterPrimitiveDescription & aDescription,nsSVGFilterInstance * aInstance)422 FilterPrimitiveDescription nsSVGFELightingElement::AddLightingAttributes(
423 const FilterPrimitiveDescription& aDescription,
424 nsSVGFilterInstance* aInstance) {
425 nsIFrame* frame = GetPrimaryFrame();
426 if (!frame) {
427 return FilterPrimitiveDescription(PrimitiveType::Empty);
428 }
429
430 nsStyleContext* style = frame->StyleContext();
431 Color color(Color::FromABGR(style->StyleSVGReset()->mLightingColor));
432 color.a = 1.f;
433 float surfaceScale = mNumberAttributes[SURFACE_SCALE].GetAnimValue();
434 Size kernelUnitLength = GetKernelUnitLength(
435 aInstance, &mNumberPairAttributes[KERNEL_UNIT_LENGTH]);
436
437 if (kernelUnitLength.width <= 0 || kernelUnitLength.height <= 0) {
438 // According to spec, A negative or zero value is an error. See link below
439 // for details.
440 // https://www.w3.org/TR/SVG/filters.html#feSpecularLightingKernelUnitLengthAttribute
441 return FilterPrimitiveDescription(PrimitiveType::Empty);
442 }
443
444 FilterPrimitiveDescription descr = aDescription;
445 descr.Attributes().Set(eLightingLight, ComputeLightAttributes(aInstance));
446 descr.Attributes().Set(eLightingSurfaceScale, surfaceScale);
447 descr.Attributes().Set(eLightingKernelUnitLength, kernelUnitLength);
448 descr.Attributes().Set(eLightingColor, color);
449 return descr;
450 }
451
AttributeAffectsRendering(int32_t aNameSpaceID,nsAtom * aAttribute) const452 bool nsSVGFELightingElement::AttributeAffectsRendering(
453 int32_t aNameSpaceID, nsAtom* aAttribute) const {
454 return nsSVGFELightingElementBase::AttributeAffectsRendering(aNameSpaceID,
455 aAttribute) ||
456 (aNameSpaceID == kNameSpaceID_None &&
457 (aAttribute == nsGkAtoms::in ||
458 aAttribute == nsGkAtoms::surfaceScale ||
459 aAttribute == nsGkAtoms::kernelUnitLength));
460 }
461
462 //----------------------------------------------------------------------
463 // nsSVGElement methods
464
GetNumberInfo()465 nsSVGElement::NumberAttributesInfo nsSVGFELightingElement::GetNumberInfo() {
466 return NumberAttributesInfo(mNumberAttributes, sNumberInfo,
467 ArrayLength(sNumberInfo));
468 }
469
470 nsSVGElement::NumberPairAttributesInfo
GetNumberPairInfo()471 nsSVGFELightingElement::GetNumberPairInfo() {
472 return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
473 ArrayLength(sNumberPairInfo));
474 }
475
GetStringInfo()476 nsSVGElement::StringAttributesInfo nsSVGFELightingElement::GetStringInfo() {
477 return StringAttributesInfo(mStringAttributes, sStringInfo,
478 ArrayLength(sStringInfo));
479 }
480