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
)) {
125 if (ShouldLoadImage()) {
126 LoadSVGImage(true, aNotify
);
129 CancelImageRequests(aNotify
);
131 } else if (aNamespaceID
== kNameSpaceID_None
&&
132 aName
== nsGkAtoms::crossorigin
) {
133 if (aNotify
&& GetCORSMode() != AttrValueToCORSMode(aOldValue
) &&
135 ForceReload(aNotify
, IgnoreErrors());
139 return SVGFEImageElementBase::AfterSetAttr(
140 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
143 void SVGFEImageElement::MaybeLoadSVGImage() {
144 if ((mStringAttributes
[HREF
].IsExplicitlySet() ||
145 mStringAttributes
[XLINK_HREF
].IsExplicitlySet()) &&
146 (NS_FAILED(LoadSVGImage(false, true)) || !LoadingEnabled())) {
147 CancelImageRequests(true);
151 nsresult
SVGFEImageElement::BindToTree(BindContext
& aContext
,
153 nsresult rv
= SVGFEImageElementBase::BindToTree(aContext
, aParent
);
154 NS_ENSURE_SUCCESS(rv
, rv
);
156 nsImageLoadingContent::BindToTree(aContext
, aParent
);
158 if ((mStringAttributes
[HREF
].IsExplicitlySet() ||
159 mStringAttributes
[XLINK_HREF
].IsExplicitlySet()) &&
161 nsContentUtils::AddScriptRunner(
162 NewRunnableMethod("dom::SVGFEImageElement::MaybeLoadSVGImage", this,
163 &SVGFEImageElement::MaybeLoadSVGImage
));
166 if (aContext
.InComposedDoc()) {
167 aContext
.OwnerDoc().SetUseCounter(eUseCounter_custom_feImage
);
173 void SVGFEImageElement::UnbindFromTree(bool aNullParent
) {
174 nsImageLoadingContent::UnbindFromTree(aNullParent
);
175 SVGFEImageElementBase::UnbindFromTree(aNullParent
);
178 ElementState
SVGFEImageElement::IntrinsicState() const {
179 return SVGFEImageElementBase::IntrinsicState() |
180 nsImageLoadingContent::ImageState();
183 void SVGFEImageElement::DestroyContent() {
184 nsImageLoadingContent::Destroy();
185 SVGFEImageElementBase::DestroyContent();
188 //----------------------------------------------------------------------
191 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement
)
193 already_AddRefed
<DOMSVGAnimatedString
> SVGFEImageElement::Href() {
194 return mStringAttributes
[HREF
].IsExplicitlySet()
195 ? mStringAttributes
[HREF
].ToDOMAnimatedString(this)
196 : mStringAttributes
[XLINK_HREF
].ToDOMAnimatedString(this);
199 //----------------------------------------------------------------------
200 // nsImageLoadingContent methods:
202 CORSMode
SVGFEImageElement::GetCORSMode() {
203 return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin
));
206 //----------------------------------------------------------------------
207 // nsIDOMSVGFEImageElement methods
209 FilterPrimitiveDescription
SVGFEImageElement::GetPrimitiveDescription(
210 SVGFilterInstance
* aInstance
, const IntRect
& aFilterSubregion
,
211 const nsTArray
<bool>& aInputsAreTainted
,
212 nsTArray
<RefPtr
<SourceSurface
>>& aInputImages
) {
213 nsIFrame
* frame
= GetPrimaryFrame();
215 return FilterPrimitiveDescription();
218 nsCOMPtr
<imgIRequest
> currentRequest
;
219 GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
220 getter_AddRefs(currentRequest
));
222 nsCOMPtr
<imgIContainer
> imageContainer
;
223 if (currentRequest
) {
224 currentRequest
->GetImage(getter_AddRefs(imageContainer
));
227 RefPtr
<SourceSurface
> image
;
228 if (imageContainer
) {
230 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
;
231 image
= imageContainer
->GetFrame(imgIContainer::FRAME_CURRENT
, flags
);
235 return FilterPrimitiveDescription();
239 imageContainer
->GetWidth(&nativeSize
.width
);
240 imageContainer
->GetHeight(&nativeSize
.height
);
242 Matrix viewBoxTM
= SVGContentUtils::GetViewBoxTransform(
243 aFilterSubregion
.width
, aFilterSubregion
.height
, 0, 0, nativeSize
.width
,
244 nativeSize
.height
, mPreserveAspectRatio
);
245 Matrix TM
= viewBoxTM
;
246 TM
.PostTranslate(aFilterSubregion
.x
, aFilterSubregion
.y
);
248 SamplingFilter samplingFilter
=
249 nsLayoutUtils::GetSamplingFilterForFrame(frame
);
251 ImageAttributes atts
;
252 atts
.mFilter
= (uint32_t)samplingFilter
;
253 atts
.mTransform
= TM
;
255 // Append the image to aInputImages and store its index in the description.
256 size_t imageIndex
= aInputImages
.Length();
257 aInputImages
.AppendElement(image
);
258 atts
.mInputIndex
= (uint32_t)imageIndex
;
259 return FilterPrimitiveDescription(AsVariant(std::move(atts
)));
262 bool SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID
,
263 nsAtom
* aAttribute
) const {
264 // nsGkAtoms::href is deliberately omitted as the frame has special
265 // handling to load the image
266 return SVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID
,
268 (aNameSpaceID
== kNameSpaceID_None
&&
269 aAttribute
== nsGkAtoms::preserveAspectRatio
);
272 bool SVGFEImageElement::OutputIsTainted(const nsTArray
<bool>& aInputsAreTainted
,
273 nsIPrincipal
* aReferencePrincipal
) {
275 nsCOMPtr
<imgIRequest
> currentRequest
;
276 GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
277 getter_AddRefs(currentRequest
));
279 if (!currentRequest
) {
283 nsCOMPtr
<nsIPrincipal
> principal
;
284 rv
= currentRequest
->GetImagePrincipal(getter_AddRefs(principal
));
285 if (NS_FAILED(rv
) || !principal
) {
289 // If CORS was used to load the image, the page is allowed to read from it.
290 if (nsLayoutUtils::ImageRequestUsesCORS(currentRequest
)) {
294 if (aReferencePrincipal
->Subsumes(principal
)) {
295 // The page is allowed to read from the image.
302 //----------------------------------------------------------------------
303 // SVGElement methods
305 already_AddRefed
<DOMSVGAnimatedPreserveAspectRatio
>
306 SVGFEImageElement::PreserveAspectRatio() {
307 return mPreserveAspectRatio
.ToDOMAnimatedPreserveAspectRatio(this);
310 SVGAnimatedPreserveAspectRatio
*
311 SVGFEImageElement::GetAnimatedPreserveAspectRatio() {
312 return &mPreserveAspectRatio
;
315 SVGElement::StringAttributesInfo
SVGFEImageElement::GetStringInfo() {
316 return StringAttributesInfo(mStringAttributes
, sStringInfo
,
317 ArrayLength(sStringInfo
));
320 //----------------------------------------------------------------------
321 // nsIImageLoadingContent methods
323 SVGFEImageElement::FrameCreated(nsIFrame
* aFrame
) {
324 nsImageLoadingContent::FrameCreated(aFrame
);
326 uint64_t mode
= aFrame
->PresContext()->ImageAnimationMode();
327 if (mode
== mImageAnimationMode
) {
331 mImageAnimationMode
= mode
;
333 if (mPendingRequest
) {
334 nsCOMPtr
<imgIContainer
> container
;
335 mPendingRequest
->GetImage(getter_AddRefs(container
));
337 container
->SetAnimationMode(mode
);
341 if (mCurrentRequest
) {
342 nsCOMPtr
<imgIContainer
> container
;
343 mCurrentRequest
->GetImage(getter_AddRefs(container
));
345 container
->SetAnimationMode(mode
);
350 //----------------------------------------------------------------------
351 // imgINotificationObserver methods
353 void SVGFEImageElement::Notify(imgIRequest
* aRequest
, int32_t aType
,
354 const nsIntRect
* aData
) {
355 nsImageLoadingContent::Notify(aRequest
, aType
, aData
);
357 if (aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
359 nsCOMPtr
<imgIContainer
> container
;
360 aRequest
->GetImage(getter_AddRefs(container
));
361 MOZ_ASSERT(container
, "who sent the notification then?");
362 container
->StartDecoding(imgIContainer::FLAG_NONE
);
363 container
->SetAnimationMode(mImageAnimationMode
);
366 if (aType
== imgINotificationObserver::LOAD_COMPLETE
||
367 aType
== imgINotificationObserver::FRAME_UPDATE
||
368 aType
== imgINotificationObserver::SIZE_AVAILABLE
) {
369 if (auto* filter
= SVGFilterElement::FromNodeOrNull(GetParent())) {
370 SVGObserverUtils::InvalidateDirectRenderingObservers(filter
);
375 } // namespace mozilla::dom