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, &amplitude, &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