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/dom/SVGFEImageElement.h"
8 
9 #include "mozilla/EventStates.h"
10 #include "mozilla/SVGObserverUtils.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/SVGFEImageElementBinding.h"
13 #include "mozilla/dom/SVGFilterElement.h"
14 #include "mozilla/dom/UserActivation.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/RefPtr.h"
17 #include "nsContentUtils.h"
18 #include "nsLayoutUtils.h"
19 #include "nsNetUtil.h"
20 #include "imgIContainer.h"
21 #include "gfx2DGlue.h"
22 
23 NS_IMPL_NS_NEW_SVG_ELEMENT(FEImage)
24 
25 using namespace mozilla::gfx;
26 
27 namespace mozilla {
28 namespace dom {
29 
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)30 JSObject* SVGFEImageElement::WrapNode(JSContext* aCx,
31                                       JS::Handle<JSObject*> aGivenProto) {
32   return SVGFEImageElement_Binding::Wrap(aCx, this, aGivenProto);
33 }
34 
35 SVGElement::StringInfo SVGFEImageElement::sStringInfo[3] = {
36     {nsGkAtoms::result, kNameSpaceID_None, true},
37     {nsGkAtoms::href, kNameSpaceID_None, true},
38     {nsGkAtoms::href, kNameSpaceID_XLink, true}};
39 
40 //----------------------------------------------------------------------
41 // nsISupports methods
42 
NS_IMPL_ISUPPORTS_INHERITED(SVGFEImageElement,SVGFEImageElementBase,imgINotificationObserver,nsIImageLoadingContent)43 NS_IMPL_ISUPPORTS_INHERITED(SVGFEImageElement, SVGFEImageElementBase,
44                             imgINotificationObserver, nsIImageLoadingContent)
45 
46 //----------------------------------------------------------------------
47 // Implementation
48 
49 SVGFEImageElement::SVGFEImageElement(
50     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
51     : SVGFEImageElementBase(std::move(aNodeInfo)), mImageAnimationMode(0) {
52   // We start out broken
53   AddStatesSilently(NS_EVENT_STATE_BROKEN);
54 }
55 
~SVGFEImageElement()56 SVGFEImageElement::~SVGFEImageElement() { nsImageLoadingContent::Destroy(); }
57 
58 //----------------------------------------------------------------------
59 
LoadSVGImage(bool aForce,bool aNotify)60 nsresult SVGFEImageElement::LoadSVGImage(bool aForce, bool aNotify) {
61   // resolve href attribute
62   nsIURI* baseURI = GetBaseURI();
63 
64   nsAutoString href;
65   if (mStringAttributes[HREF].IsExplicitlySet()) {
66     mStringAttributes[HREF].GetAnimValue(href, this);
67   } else {
68     mStringAttributes[XLINK_HREF].GetAnimValue(href, this);
69   }
70   href.Trim(" \t\n\r");
71 
72   if (baseURI && !href.IsEmpty()) NS_MakeAbsoluteURI(href, href, baseURI);
73 
74   // Make sure we don't get in a recursive death-spiral
75   Document* doc = OwnerDoc();
76   nsCOMPtr<nsIURI> hrefAsURI;
77   if (NS_SUCCEEDED(StringToURI(href, doc, getter_AddRefs(hrefAsURI)))) {
78     bool isEqual;
79     if (NS_SUCCEEDED(hrefAsURI->Equals(baseURI, &isEqual)) && isEqual) {
80       // Image URI matches our URI exactly! Bail out.
81       return NS_OK;
82     }
83   }
84 
85   // Mark channel as urgent-start before load image if the image load is
86   // initaiated by a user interaction.
87   mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput();
88   return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
89 }
90 
ShouldLoadImage() const91 bool SVGFEImageElement::ShouldLoadImage() const {
92   return LoadingEnabled() && OwnerDoc()->ShouldLoadImages();
93 }
94 
95 //----------------------------------------------------------------------
96 // EventTarget methods:
97 
AsyncEventRunning(AsyncEventDispatcher * aEvent)98 void SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
99   nsImageLoadingContent::AsyncEventRunning(aEvent);
100 }
101 
102 //----------------------------------------------------------------------
103 // nsIContent methods:
104 
NS_IMETHODIMP_(bool)105 NS_IMETHODIMP_(bool)
106 SVGFEImageElement::IsAttributeMapped(const nsAtom* name) const {
107   static const MappedAttributeEntry* const map[] = {sGraphicsMap};
108 
109   return FindAttributeDependence(name, map) ||
110          SVGFEImageElementBase::IsAttributeMapped(name);
111 }
112 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)113 nsresult SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
114                                          const nsAttrValue* aValue,
115                                          const nsAttrValue* aOldValue,
116                                          nsIPrincipal* aSubjectPrincipal,
117                                          bool aNotify) {
118   if (aName == nsGkAtoms::href && (aNamespaceID == kNameSpaceID_XLink ||
119                                    aNamespaceID == kNameSpaceID_None)) {
120     if (aValue) {
121       if (ShouldLoadImage()) {
122         LoadSVGImage(true, aNotify);
123       }
124     } else {
125       CancelImageRequests(aNotify);
126     }
127   }
128 
129   return SVGFEImageElementBase::AfterSetAttr(
130       aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
131 }
132 
MaybeLoadSVGImage()133 void SVGFEImageElement::MaybeLoadSVGImage() {
134   if ((mStringAttributes[HREF].IsExplicitlySet() ||
135        mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
136       (NS_FAILED(LoadSVGImage(false, true)) || !LoadingEnabled())) {
137     CancelImageRequests(true);
138   }
139 }
140 
BindToTree(BindContext & aContext,nsINode & aParent)141 nsresult SVGFEImageElement::BindToTree(BindContext& aContext,
142                                        nsINode& aParent) {
143   nsresult rv = SVGFEImageElementBase::BindToTree(aContext, aParent);
144   NS_ENSURE_SUCCESS(rv, rv);
145 
146   nsImageLoadingContent::BindToTree(aContext, aParent);
147 
148   if ((mStringAttributes[HREF].IsExplicitlySet() ||
149        mStringAttributes[XLINK_HREF].IsExplicitlySet()) &&
150       ShouldLoadImage()) {
151     nsContentUtils::AddScriptRunner(
152         NewRunnableMethod("dom::SVGFEImageElement::MaybeLoadSVGImage", this,
153                           &SVGFEImageElement::MaybeLoadSVGImage));
154   }
155 
156   return rv;
157 }
158 
UnbindFromTree(bool aNullParent)159 void SVGFEImageElement::UnbindFromTree(bool aNullParent) {
160   nsImageLoadingContent::UnbindFromTree(aNullParent);
161   SVGFEImageElementBase::UnbindFromTree(aNullParent);
162 }
163 
IntrinsicState() const164 EventStates SVGFEImageElement::IntrinsicState() const {
165   return SVGFEImageElementBase::IntrinsicState() |
166          nsImageLoadingContent::ImageState();
167 }
168 
DestroyContent()169 void SVGFEImageElement::DestroyContent() {
170   nsImageLoadingContent::Destroy();
171   SVGFEImageElementBase::DestroyContent();
172 }
173 
174 //----------------------------------------------------------------------
175 // nsINode methods
176 
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement)177 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement)
178 
179 already_AddRefed<DOMSVGAnimatedString> SVGFEImageElement::Href() {
180   return mStringAttributes[HREF].IsExplicitlySet()
181              ? mStringAttributes[HREF].ToDOMAnimatedString(this)
182              : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
183 }
184 
185 //----------------------------------------------------------------------
186 // nsIDOMSVGFEImageElement methods
187 
GetPrimitiveDescription(SVGFilterInstance * aInstance,const IntRect & aFilterSubregion,const nsTArray<bool> & aInputsAreTainted,nsTArray<RefPtr<SourceSurface>> & aInputImages)188 FilterPrimitiveDescription SVGFEImageElement::GetPrimitiveDescription(
189     SVGFilterInstance* aInstance, const IntRect& aFilterSubregion,
190     const nsTArray<bool>& aInputsAreTainted,
191     nsTArray<RefPtr<SourceSurface>>& aInputImages) {
192   nsIFrame* frame = GetPrimaryFrame();
193   if (!frame) {
194     return FilterPrimitiveDescription();
195   }
196 
197   nsCOMPtr<imgIRequest> currentRequest;
198   GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
199              getter_AddRefs(currentRequest));
200 
201   nsCOMPtr<imgIContainer> imageContainer;
202   if (currentRequest) {
203     currentRequest->GetImage(getter_AddRefs(imageContainer));
204   }
205 
206   RefPtr<SourceSurface> image;
207   if (imageContainer) {
208     uint32_t flags =
209         imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
210     image = imageContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags);
211   }
212 
213   if (!image) {
214     return FilterPrimitiveDescription();
215   }
216 
217   IntSize nativeSize;
218   imageContainer->GetWidth(&nativeSize.width);
219   imageContainer->GetHeight(&nativeSize.height);
220 
221   Matrix viewBoxTM = SVGContentUtils::GetViewBoxTransform(
222       aFilterSubregion.width, aFilterSubregion.height, 0, 0, nativeSize.width,
223       nativeSize.height, mPreserveAspectRatio);
224   Matrix TM = viewBoxTM;
225   TM.PostTranslate(aFilterSubregion.x, aFilterSubregion.y);
226 
227   SamplingFilter samplingFilter =
228       nsLayoutUtils::GetSamplingFilterForFrame(frame);
229 
230   ImageAttributes atts;
231   atts.mFilter = (uint32_t)samplingFilter;
232   atts.mTransform = TM;
233 
234   // Append the image to aInputImages and store its index in the description.
235   size_t imageIndex = aInputImages.Length();
236   aInputImages.AppendElement(image);
237   atts.mInputIndex = (uint32_t)imageIndex;
238   return FilterPrimitiveDescription(AsVariant(std::move(atts)));
239 }
240 
AttributeAffectsRendering(int32_t aNameSpaceID,nsAtom * aAttribute) const241 bool SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID,
242                                                   nsAtom* aAttribute) const {
243   // nsGkAtoms::href is deliberately omitted as the frame has special
244   // handling to load the image
245   return SVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID,
246                                                           aAttribute) ||
247          (aNameSpaceID == kNameSpaceID_None &&
248           aAttribute == nsGkAtoms::preserveAspectRatio);
249 }
250 
OutputIsTainted(const nsTArray<bool> & aInputsAreTainted,nsIPrincipal * aReferencePrincipal)251 bool SVGFEImageElement::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted,
252                                         nsIPrincipal* aReferencePrincipal) {
253   nsresult rv;
254   nsCOMPtr<imgIRequest> currentRequest;
255   GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
256              getter_AddRefs(currentRequest));
257 
258   if (!currentRequest) {
259     return false;
260   }
261 
262   nsCOMPtr<nsIPrincipal> principal;
263   rv = currentRequest->GetImagePrincipal(getter_AddRefs(principal));
264   if (NS_FAILED(rv) || !principal) {
265     return true;
266   }
267 
268   int32_t corsmode;
269   if (NS_SUCCEEDED(currentRequest->GetCORSMode(&corsmode)) &&
270       corsmode != CORS_NONE) {
271     // If CORS was used to load the image, the page is allowed to read from it.
272     return false;
273   }
274 
275   if (aReferencePrincipal->Subsumes(principal)) {
276     // The page is allowed to read from the image.
277     return false;
278   }
279 
280   return true;
281 }
282 
283 //----------------------------------------------------------------------
284 // SVGElement methods
285 
286 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
PreserveAspectRatio()287 SVGFEImageElement::PreserveAspectRatio() {
288   return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
289 }
290 
291 SVGAnimatedPreserveAspectRatio*
GetAnimatedPreserveAspectRatio()292 SVGFEImageElement::GetAnimatedPreserveAspectRatio() {
293   return &mPreserveAspectRatio;
294 }
295 
GetStringInfo()296 SVGElement::StringAttributesInfo SVGFEImageElement::GetStringInfo() {
297   return StringAttributesInfo(mStringAttributes, sStringInfo,
298                               ArrayLength(sStringInfo));
299 }
300 
301 //----------------------------------------------------------------------
302 // nsIImageLoadingContent methods
NS_IMETHODIMP_(void)303 NS_IMETHODIMP_(void)
304 SVGFEImageElement::FrameCreated(nsIFrame* aFrame) {
305   nsImageLoadingContent::FrameCreated(aFrame);
306 
307   uint64_t mode = aFrame->PresContext()->ImageAnimationMode();
308   if (mode == mImageAnimationMode) {
309     return;
310   }
311 
312   mImageAnimationMode = mode;
313 
314   if (mPendingRequest) {
315     nsCOMPtr<imgIContainer> container;
316     mPendingRequest->GetImage(getter_AddRefs(container));
317     if (container) {
318       container->SetAnimationMode(mode);
319     }
320   }
321 
322   if (mCurrentRequest) {
323     nsCOMPtr<imgIContainer> container;
324     mCurrentRequest->GetImage(getter_AddRefs(container));
325     if (container) {
326       container->SetAnimationMode(mode);
327     }
328   }
329 }
330 
331 //----------------------------------------------------------------------
332 // imgINotificationObserver methods
333 
Notify(imgIRequest * aRequest,int32_t aType,const nsIntRect * aData)334 void SVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType,
335                                const nsIntRect* aData) {
336   nsImageLoadingContent::Notify(aRequest, aType, aData);
337 
338   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
339     // Request a decode
340     nsCOMPtr<imgIContainer> container;
341     aRequest->GetImage(getter_AddRefs(container));
342     MOZ_ASSERT(container, "who sent the notification then?");
343     container->StartDecoding(imgIContainer::FLAG_NONE);
344     container->SetAnimationMode(mImageAnimationMode);
345   }
346 
347   if (aType == imgINotificationObserver::LOAD_COMPLETE ||
348       aType == imgINotificationObserver::FRAME_UPDATE ||
349       aType == imgINotificationObserver::SIZE_AVAILABLE) {
350     if (GetParent() && GetParent()->IsSVGElement(nsGkAtoms::filter)) {
351       SVGObserverUtils::InvalidateDirectRenderingObservers(
352           static_cast<SVGFilterElement*>(GetParent()));
353     }
354   }
355 }
356 
357 }  // namespace dom
358 }  // namespace mozilla
359