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