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/. */
7 #include "mozilla/dom/SVGFEImageElement.h"
9 #include "mozilla/SVGObserverUtils.h"
10 #include "mozilla/dom/Document.h"
11 #include "mozilla/dom/BindContext.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"
23 NS_IMPL_NS_NEW_SVG_ELEMENT(FEImage
)
25 using namespace mozilla::gfx
;
27 namespace mozilla::dom
{
29 JSObject
* SVGFEImageElement::WrapNode(JSContext
* aCx
,
30 JS::Handle
<JSObject
*> aGivenProto
) {
31 return SVGFEImageElement_Binding::Wrap(aCx
, this, aGivenProto
);
34 SVGElement::StringInfo
SVGFEImageElement::sStringInfo
[3] = {
35 {nsGkAtoms::result
, kNameSpaceID_None
, true},
36 {nsGkAtoms::href
, kNameSpaceID_None
, true},
37 {nsGkAtoms::href
, kNameSpaceID_XLink
, true}};
39 //----------------------------------------------------------------------
40 // nsISupports methods
42 NS_IMPL_ISUPPORTS_INHERITED(SVGFEImageElement
, SVGFEImageElementBase
,
43 imgINotificationObserver
, nsIImageLoadingContent
)
45 //----------------------------------------------------------------------
48 SVGFEImageElement::SVGFEImageElement(
49 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
50 : SVGFEImageElementBase(std::move(aNodeInfo
)), mImageAnimationMode(0) {
51 // We start out broken
52 AddStatesSilently(ElementState::BROKEN
);
55 SVGFEImageElement::~SVGFEImageElement() { nsImageLoadingContent::Destroy(); }
57 //----------------------------------------------------------------------
59 nsresult
SVGFEImageElement::LoadSVGImage(bool aForce
, bool aNotify
) {
60 // resolve href attribute
61 nsIURI
* baseURI
= GetBaseURI();
64 if (mStringAttributes
[HREF
].IsExplicitlySet()) {
65 mStringAttributes
[HREF
].GetAnimValue(href
, this);
67 mStringAttributes
[XLINK_HREF
].GetAnimValue(href
, this);
71 if (baseURI
&& !href
.IsEmpty()) NS_MakeAbsoluteURI(href
, href
, baseURI
);
73 // Make sure we don't get in a recursive death-spiral
74 Document
* doc
= OwnerDoc();
75 nsCOMPtr
<nsIURI
> hrefAsURI
;
76 if (NS_SUCCEEDED(StringToURI(href
, doc
, getter_AddRefs(hrefAsURI
)))) {
78 if (NS_SUCCEEDED(hrefAsURI
->Equals(baseURI
, &isEqual
)) && isEqual
) {
79 // Image URI matches our URI exactly! Bail out.
84 // Mark channel as urgent-start before load image if the image load is
85 // initaiated by a user interaction.
86 mUseUrgentStartForChannel
= UserActivation::IsHandlingUserInput();
87 return LoadImage(href
, aForce
, aNotify
, eImageLoadType_Normal
);
90 bool SVGFEImageElement::ShouldLoadImage() const {
91 return LoadingEnabled() && OwnerDoc()->ShouldLoadImages();
94 //----------------------------------------------------------------------
95 // EventTarget methods:
97 void SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher
* aEvent
) {
98 nsImageLoadingContent::AsyncEventRunning(aEvent
);
101 //----------------------------------------------------------------------
102 // nsIContent methods:
104 bool SVGFEImageElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
105 const nsAString
& aValue
,
106 nsIPrincipal
* aMaybeScriptedPrincipal
,
107 nsAttrValue
& aResult
) {
108 if (aNamespaceID
== kNameSpaceID_None
&&
109 aAttribute
== nsGkAtoms::crossorigin
) {
110 ParseCORSValue(aValue
, aResult
);
113 return SVGFEImageElementBase::ParseAttribute(
114 aNamespaceID
, aAttribute
, aValue
, aMaybeScriptedPrincipal
, aResult
);
117 void SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
118 const nsAttrValue
* aValue
,
119 const nsAttrValue
* aOldValue
,
120 nsIPrincipal
* aSubjectPrincipal
,
122 if (aName
== nsGkAtoms::href
&& (aNamespaceID
== kNameSpaceID_XLink
||
123 aNamespaceID
== kNameSpaceID_None
)) {
124 if (aNamespaceID
== kNameSpaceID_XLink
&&
125 mStringAttributes
[HREF
].IsExplicitlySet()) {
126 // href overrides xlink:href
129 if (aValue
|| (aNamespaceID
== kNameSpaceID_None
&&
130 mStringAttributes
[XLINK_HREF
].IsExplicitlySet())) {
131 if (ShouldLoadImage()) {
132 LoadSVGImage(true, aNotify
);
135 CancelImageRequests(aNotify
);
137 } else if (aNamespaceID
== kNameSpaceID_None
&&
138 aName
== nsGkAtoms::crossorigin
) {
139 if (aNotify
&& GetCORSMode() != AttrValueToCORSMode(aOldValue
) &&
141 ForceReload(aNotify
, IgnoreErrors());
145 return SVGFEImageElementBase::AfterSetAttr(
146 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
149 void SVGFEImageElement::MaybeLoadSVGImage() {
150 if ((mStringAttributes
[HREF
].IsExplicitlySet() ||
151 mStringAttributes
[XLINK_HREF
].IsExplicitlySet()) &&
152 (NS_FAILED(LoadSVGImage(false, true)) || !LoadingEnabled())) {
153 CancelImageRequests(true);
157 nsresult
SVGFEImageElement::BindToTree(BindContext
& aContext
,
159 nsresult rv
= SVGFEImageElementBase::BindToTree(aContext
, aParent
);
160 NS_ENSURE_SUCCESS(rv
, rv
);
162 nsImageLoadingContent::BindToTree(aContext
, aParent
);
164 if ((mStringAttributes
[HREF
].IsExplicitlySet() ||
165 mStringAttributes
[XLINK_HREF
].IsExplicitlySet()) &&
167 nsContentUtils::AddScriptRunner(
168 NewRunnableMethod("dom::SVGFEImageElement::MaybeLoadSVGImage", this,
169 &SVGFEImageElement::MaybeLoadSVGImage
));
172 if (aContext
.InComposedDoc()) {
173 aContext
.OwnerDoc().SetUseCounter(eUseCounter_custom_feImage
);
179 void SVGFEImageElement::UnbindFromTree(UnbindContext
& aContext
) {
180 nsImageLoadingContent::UnbindFromTree();
181 SVGFEImageElementBase::UnbindFromTree(aContext
);
184 void SVGFEImageElement::DestroyContent() {
185 nsImageLoadingContent::Destroy();
186 SVGFEImageElementBase::DestroyContent();
189 //----------------------------------------------------------------------
192 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement
)
194 already_AddRefed
<DOMSVGAnimatedString
> SVGFEImageElement::Href() {
195 return mStringAttributes
[HREF
].IsExplicitlySet()
196 ? mStringAttributes
[HREF
].ToDOMAnimatedString(this)
197 : mStringAttributes
[XLINK_HREF
].ToDOMAnimatedString(this);
200 //----------------------------------------------------------------------
201 // nsImageLoadingContent methods:
203 CORSMode
SVGFEImageElement::GetCORSMode() {
204 return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin
));
207 //----------------------------------------------------------------------
208 // nsIDOMSVGFEImageElement methods
210 FilterPrimitiveDescription
SVGFEImageElement::GetPrimitiveDescription(
211 SVGFilterInstance
* aInstance
, const IntRect
& aFilterSubregion
,
212 const nsTArray
<bool>& aInputsAreTainted
,
213 nsTArray
<RefPtr
<SourceSurface
>>& aInputImages
) {
214 nsIFrame
* frame
= GetPrimaryFrame();
216 return FilterPrimitiveDescription();
219 nsCOMPtr
<imgIRequest
> currentRequest
;
220 GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
221 getter_AddRefs(currentRequest
));
223 nsCOMPtr
<imgIContainer
> imageContainer
;
224 if (currentRequest
) {
225 currentRequest
->GetImage(getter_AddRefs(imageContainer
));
228 RefPtr
<SourceSurface
> image
;
229 nsIntSize nativeSize
;
230 if (imageContainer
) {
231 if (NS_FAILED(imageContainer
->GetWidth(&nativeSize
.width
))) {
232 nativeSize
.width
= kFallbackIntrinsicWidthInPixels
;
234 if (NS_FAILED(imageContainer
->GetHeight(&nativeSize
.height
))) {
235 nativeSize
.height
= kFallbackIntrinsicHeightInPixels
;
238 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
;
239 image
= imageContainer
->GetFrameAtSize(nativeSize
,
240 imgIContainer::FRAME_CURRENT
, flags
);
244 return FilterPrimitiveDescription();
247 Matrix viewBoxTM
= SVGContentUtils::GetViewBoxTransform(
248 aFilterSubregion
.width
, aFilterSubregion
.height
, 0, 0, nativeSize
.width
,
249 nativeSize
.height
, mPreserveAspectRatio
);
250 Matrix TM
= viewBoxTM
;
251 TM
.PostTranslate(aFilterSubregion
.x
, aFilterSubregion
.y
);
253 SamplingFilter samplingFilter
=
254 nsLayoutUtils::GetSamplingFilterForFrame(frame
);
256 ImageAttributes atts
;
257 atts
.mFilter
= (uint32_t)samplingFilter
;
258 atts
.mTransform
= TM
;
260 // Append the image to aInputImages and store its index in the description.
261 size_t imageIndex
= aInputImages
.Length();
262 aInputImages
.AppendElement(image
);
263 atts
.mInputIndex
= (uint32_t)imageIndex
;
264 return FilterPrimitiveDescription(AsVariant(std::move(atts
)));
267 bool SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID
,
268 nsAtom
* aAttribute
) const {
269 // nsGkAtoms::href is deliberately omitted as the frame has special
270 // handling to load the image
271 return SVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID
,
273 (aNameSpaceID
== kNameSpaceID_None
&&
274 aAttribute
== nsGkAtoms::preserveAspectRatio
);
277 bool SVGFEImageElement::OutputIsTainted(const nsTArray
<bool>& aInputsAreTainted
,
278 nsIPrincipal
* aReferencePrincipal
) {
280 nsCOMPtr
<imgIRequest
> currentRequest
;
281 GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
282 getter_AddRefs(currentRequest
));
284 if (!currentRequest
) {
288 nsCOMPtr
<nsIPrincipal
> principal
;
289 rv
= currentRequest
->GetImagePrincipal(getter_AddRefs(principal
));
290 if (NS_FAILED(rv
) || !principal
) {
294 // If CORS was used to load the image, the page is allowed to read from it.
295 if (nsLayoutUtils::ImageRequestUsesCORS(currentRequest
)) {
299 if (aReferencePrincipal
->Subsumes(principal
)) {
300 // The page is allowed to read from the image.
307 //----------------------------------------------------------------------
308 // SVGElement methods
310 already_AddRefed
<DOMSVGAnimatedPreserveAspectRatio
>
311 SVGFEImageElement::PreserveAspectRatio() {
312 return mPreserveAspectRatio
.ToDOMAnimatedPreserveAspectRatio(this);
315 SVGAnimatedPreserveAspectRatio
*
316 SVGFEImageElement::GetAnimatedPreserveAspectRatio() {
317 return &mPreserveAspectRatio
;
320 SVGElement::StringAttributesInfo
SVGFEImageElement::GetStringInfo() {
321 return StringAttributesInfo(mStringAttributes
, sStringInfo
,
322 std::size(sStringInfo
));
325 //----------------------------------------------------------------------
326 // nsIImageLoadingContent methods
328 SVGFEImageElement::FrameCreated(nsIFrame
* aFrame
) {
329 nsImageLoadingContent::FrameCreated(aFrame
);
331 uint64_t mode
= aFrame
->PresContext()->ImageAnimationMode();
332 if (mode
== mImageAnimationMode
) {
336 mImageAnimationMode
= mode
;
338 if (mPendingRequest
) {
339 nsCOMPtr
<imgIContainer
> container
;
340 mPendingRequest
->GetImage(getter_AddRefs(container
));
342 container
->SetAnimationMode(mode
);
346 if (mCurrentRequest
) {
347 nsCOMPtr
<imgIContainer
> container
;
348 mCurrentRequest
->GetImage(getter_AddRefs(container
));
350 container
->SetAnimationMode(mode
);
355 //----------------------------------------------------------------------
356 // imgINotificationObserver methods
358 void SVGFEImageElement::Notify(imgIRequest
* aRequest
, int32_t aType
,
359 const nsIntRect
* aData
) {
360 nsImageLoadingContent::Notify(aRequest
, aType
, aData
);
362 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
364 nsCOMPtr
<imgIContainer
> container
;
365 aRequest
->GetImage(getter_AddRefs(container
));
366 MOZ_ASSERT(container
, "who sent the notification then?");
367 container
->StartDecoding(imgIContainer::FLAG_NONE
);
368 container
->SetAnimationMode(mImageAnimationMode
);
371 if (aType
== imgINotificationObserver::LOAD_COMPLETE
||
372 aType
== imgINotificationObserver::FRAME_UPDATE
||
373 aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
374 if (auto* filter
= SVGFilterElement::FromNodeOrNull(GetParent())) {
375 SVGObserverUtils::InvalidateDirectRenderingObservers(filter
);
380 void SVGFEImageElement::DidAnimateAttribute(int32_t aNameSpaceID
,
381 nsAtom
* aAttribute
) {
382 if ((aNameSpaceID
== kNameSpaceID_None
||
383 aNameSpaceID
== kNameSpaceID_XLink
) &&
384 aAttribute
== nsGkAtoms::href
) {
386 mStringAttributes
[SVGFEImageElement::HREF
].IsExplicitlySet() ||
387 mStringAttributes
[SVGFEImageElement::XLINK_HREF
].IsExplicitlySet();
389 LoadSVGImage(true, true);
391 CancelImageRequests(true);
394 SVGFEImageElementBase::DidAnimateAttribute(aNameSpaceID
, aAttribute
);
397 } // namespace mozilla::dom