SVGFEImageElement.cpp (15258B)
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 "imgIContainer.h" 10 #include "mozilla/RefPtr.h" 11 #include "mozilla/SVGObserverUtils.h" 12 #include "mozilla/dom/BindContext.h" 13 #include "mozilla/dom/Document.h" 14 #include "mozilla/dom/FetchPriority.h" 15 #include "mozilla/dom/SVGFEImageElementBinding.h" 16 #include "mozilla/dom/SVGFilterElement.h" 17 #include "mozilla/dom/UserActivation.h" 18 #include "mozilla/gfx/2D.h" 19 #include "nsContentUtils.h" 20 #include "nsLayoutUtils.h" 21 #include "nsNetUtil.h" 22 23 NS_IMPL_NS_NEW_SVG_ELEMENT(FEImage) 24 25 using namespace mozilla::gfx; 26 27 namespace mozilla::dom { 28 29 JSObject* SVGFEImageElement::WrapNode(JSContext* aCx, 30 JS::Handle<JSObject*> aGivenProto) { 31 return SVGFEImageElement_Binding::Wrap(aCx, this, aGivenProto); 32 } 33 34 SVGElement::StringInfo SVGFEImageElement::sStringInfo[3] = { 35 {nsGkAtoms::result, kNameSpaceID_None, true}, 36 {nsGkAtoms::href, kNameSpaceID_None, true}, 37 {nsGkAtoms::href, kNameSpaceID_XLink, true}}; 38 39 // Cycle collection magic -- based on SVGUseElement 40 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFEImageElement) 41 42 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGFEImageElement, 43 SVGFEImageElementBase) 44 tmp->mImageContentObserver = nullptr; 45 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 46 47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGFEImageElement, 48 SVGFEImageElementBase) 49 SVGObserverUtils::TraverseFEImageObserver(tmp, &cb); 50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 51 52 //---------------------------------------------------------------------- 53 // nsISupports methods 54 55 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(SVGFEImageElement, 56 SVGFEImageElementBase, 57 imgINotificationObserver, 58 nsIImageLoadingContent) 59 60 //---------------------------------------------------------------------- 61 // Implementation 62 63 SVGFEImageElement::SVGFEImageElement( 64 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 65 : SVGFEImageElementBase(std::move(aNodeInfo)) { 66 // We start out broken 67 AddStatesSilently(ElementState::BROKEN); 68 } 69 70 SVGFEImageElement::~SVGFEImageElement() { nsImageLoadingContent::Destroy(); } 71 72 //---------------------------------------------------------------------- 73 74 void SVGFEImageElement::UpdateSrcURI() { 75 nsAutoString href; 76 HrefAsString(href); 77 78 mImageContentObserver = nullptr; 79 mSrcURI = nullptr; 80 if (!href.IsEmpty()) { 81 StringToURI(href, OwnerDoc(), getter_AddRefs(mSrcURI)); 82 } 83 } 84 85 void SVGFEImageElement::LoadSelectedImage(bool aAlwaysLoad, 86 bool aStopLazyLoading) { 87 // Make sure we don't get in a recursive death-spiral 88 bool isEqual; 89 if (mSrcURI && NS_SUCCEEDED(mSrcURI->Equals(GetBaseURI(), &isEqual)) && 90 isEqual) { 91 // Image URI matches our URI exactly! Bail out. 92 return; 93 } 94 95 const bool kNotify = true; 96 97 if (SVGObserverUtils::GetAndObserveFEImageContent(this)) { 98 // We have a local target, don't try to load an image. 99 CancelImageRequests(kNotify); 100 return; 101 } 102 103 nsresult rv = NS_ERROR_FAILURE; 104 if (mSrcURI || (mStringAttributes[HREF].IsExplicitlySet() || 105 mStringAttributes[XLINK_HREF].IsExplicitlySet())) { 106 rv = LoadImage(mSrcURI, /* aForce = */ true, kNotify, eImageLoadType_Normal, 107 LoadFlags(), OwnerDoc()); 108 } 109 110 if (NS_FAILED(rv)) { 111 CancelImageRequests(kNotify); 112 } 113 } 114 115 //---------------------------------------------------------------------- 116 // EventTarget methods: 117 118 void SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) { 119 nsImageLoadingContent::AsyncEventRunning(aEvent); 120 } 121 122 //---------------------------------------------------------------------- 123 // nsIContent methods: 124 125 bool SVGFEImageElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 126 const nsAString& aValue, 127 nsIPrincipal* aMaybeScriptedPrincipal, 128 nsAttrValue& aResult) { 129 if (aNamespaceID == kNameSpaceID_None) { 130 if (aAttribute == nsGkAtoms::crossorigin) { 131 ParseCORSValue(aValue, aResult); 132 return true; 133 } 134 if (aAttribute == nsGkAtoms::fetchpriority) { 135 ParseFetchPriority(aValue, aResult); 136 return true; 137 } 138 } 139 return SVGFEImageElementBase::ParseAttribute( 140 aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult); 141 } 142 143 void SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 144 const nsAttrValue* aValue, 145 const nsAttrValue* aOldValue, 146 nsIPrincipal* aSubjectPrincipal, 147 bool aNotify) { 148 bool forceReload = false; 149 if (aName == nsGkAtoms::href && (aNamespaceID == kNameSpaceID_XLink || 150 aNamespaceID == kNameSpaceID_None)) { 151 if (aNamespaceID == kNameSpaceID_XLink && 152 mStringAttributes[HREF].IsExplicitlySet()) { 153 // href overrides xlink:href 154 return; 155 } 156 UpdateSrcURI(); 157 forceReload = true; 158 } else if (aNamespaceID == kNameSpaceID_None && 159 aName == nsGkAtoms::crossorigin) { 160 forceReload = GetCORSMode() != AttrValueToCORSMode(aOldValue); 161 } 162 163 if (forceReload) { 164 QueueImageTask(mSrcURI, /* aAlwaysLoad = */ true, aNotify); 165 } 166 167 return SVGFEImageElementBase::AfterSetAttr( 168 aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); 169 } 170 171 nsresult SVGFEImageElement::BindToTree(BindContext& aContext, 172 nsINode& aParent) { 173 nsresult rv = SVGFEImageElementBase::BindToTree(aContext, aParent); 174 NS_ENSURE_SUCCESS(rv, rv); 175 176 nsImageLoadingContent::BindToTree(aContext, aParent); 177 178 if (aContext.InComposedDoc()) { 179 aContext.OwnerDoc().SetUseCounter(eUseCounter_custom_feImage); 180 } 181 182 return rv; 183 } 184 185 void SVGFEImageElement::UnbindFromTree(UnbindContext& aContext) { 186 mImageContentObserver = nullptr; 187 nsImageLoadingContent::UnbindFromTree(); 188 SVGFEImageElementBase::UnbindFromTree(aContext); 189 } 190 191 void SVGFEImageElement::DestroyContent() { 192 ClearImageLoadTask(); 193 194 nsImageLoadingContent::Destroy(); 195 SVGFEImageElementBase::DestroyContent(); 196 } 197 198 //---------------------------------------------------------------------- 199 // nsINode methods 200 201 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGFEImageElement) 202 203 void SVGFEImageElement::NodeInfoChanged(Document* aOldDoc) { 204 SVGFEImageElementBase::NodeInfoChanged(aOldDoc); 205 206 // Reparse the URI if needed. Note that we can't check whether we already have 207 // a parsed URI, because it might be null even if we have a valid href 208 // attribute, if we tried to parse with a different base. 209 UpdateSrcURI(); 210 211 QueueImageTask(mSrcURI, /* aAlwaysLoad = */ true, /* aNotify = */ false); 212 } 213 214 //---------------------------------------------------------------------- 215 // nsImageLoadingContent methods: 216 217 CORSMode SVGFEImageElement::GetCORSMode() { 218 return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); 219 } 220 221 void SVGFEImageElement::GetFetchPriority(nsAString& aFetchPriority) const { 222 GetEnumAttr(nsGkAtoms::fetchpriority, kFetchPriorityAttributeValueAuto, 223 aFetchPriority); 224 } 225 226 //---------------------------------------------------------------------- 227 // nsIDOMSVGFEImageElement methods 228 229 FilterPrimitiveDescription SVGFEImageElement::GetPrimitiveDescription( 230 SVGFilterInstance* aInstance, const IntRect& aFilterSubregion, 231 const nsTArray<bool>& aInputsAreTainted, 232 nsTArray<RefPtr<SourceSurface>>& aInputImages) { 233 nsIFrame* frame = GetPrimaryFrame(); 234 if (!frame) { 235 return FilterPrimitiveDescription(); 236 } 237 238 nsCOMPtr<imgIRequest> currentRequest; 239 GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 240 getter_AddRefs(currentRequest)); 241 242 nsCOMPtr<imgIContainer> imageContainer; 243 if (currentRequest) { 244 currentRequest->GetImage(getter_AddRefs(imageContainer)); 245 } 246 247 RefPtr<SourceSurface> image; 248 nsIntSize nativeSize; 249 if (imageContainer) { 250 if (NS_FAILED(imageContainer->GetWidth(&nativeSize.width))) { 251 nativeSize.width = kFallbackIntrinsicWidthInPixels; 252 } 253 if (NS_FAILED(imageContainer->GetHeight(&nativeSize.height))) { 254 nativeSize.height = kFallbackIntrinsicHeightInPixels; 255 } 256 uint32_t flags = 257 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY; 258 image = imageContainer->GetFrameAtSize(nativeSize, 259 imgIContainer::FRAME_CURRENT, flags); 260 } 261 262 if (!image) { 263 return FilterPrimitiveDescription(); 264 } 265 266 Matrix viewBoxTM = SVGContentUtils::GetViewBoxTransform( 267 aFilterSubregion.width, aFilterSubregion.height, 0, 0, nativeSize.width, 268 nativeSize.height, mPreserveAspectRatio); 269 Matrix TM = viewBoxTM; 270 TM.PostTranslate(aFilterSubregion.x, aFilterSubregion.y); 271 272 SamplingFilter samplingFilter = 273 nsLayoutUtils::GetSamplingFilterForFrame(frame); 274 275 ImageAttributes atts; 276 atts.mFilter = (uint32_t)samplingFilter; 277 atts.mTransform = TM; 278 279 // Append the image to aInputImages and store its index in the description. 280 size_t imageIndex = aInputImages.Length(); 281 aInputImages.AppendElement(image); 282 atts.mInputIndex = (uint32_t)imageIndex; 283 return FilterPrimitiveDescription(AsVariant(std::move(atts))); 284 } 285 286 bool SVGFEImageElement::AttributeAffectsRendering(int32_t aNameSpaceID, 287 nsAtom* aAttribute) const { 288 // nsGkAtoms::href is deliberately omitted as the frame has special 289 // handling to load the image 290 return SVGFEImageElementBase::AttributeAffectsRendering(aNameSpaceID, 291 aAttribute) || 292 (aNameSpaceID == kNameSpaceID_None && 293 aAttribute == nsGkAtoms::preserveAspectRatio); 294 } 295 296 bool SVGFEImageElement::OutputIsTainted(const nsTArray<bool>& aInputsAreTainted, 297 nsIPrincipal* aReferencePrincipal) { 298 nsresult rv; 299 nsCOMPtr<imgIRequest> currentRequest; 300 GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 301 getter_AddRefs(currentRequest)); 302 303 if (!currentRequest) { 304 return false; 305 } 306 307 nsCOMPtr<nsIPrincipal> principal; 308 rv = currentRequest->GetImagePrincipal(getter_AddRefs(principal)); 309 if (NS_FAILED(rv) || !principal) { 310 return true; 311 } 312 313 // If CORS was used to load the image, the page is allowed to read from it. 314 if (nsLayoutUtils::ImageRequestUsesCORS(currentRequest)) { 315 return false; 316 } 317 318 if (aReferencePrincipal->Subsumes(principal)) { 319 // The page is allowed to read from the image. 320 return false; 321 } 322 323 return true; 324 } 325 326 //---------------------------------------------------------------------- 327 // SVGElement methods 328 329 already_AddRefed<DOMSVGAnimatedString> SVGFEImageElement::Href() { 330 return mStringAttributes[HREF].IsExplicitlySet() || 331 !mStringAttributes[XLINK_HREF].IsExplicitlySet() 332 ? mStringAttributes[HREF].ToDOMAnimatedString(this) 333 : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); 334 } 335 336 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> 337 SVGFEImageElement::PreserveAspectRatio() { 338 return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); 339 } 340 341 SVGAnimatedPreserveAspectRatio* 342 SVGFEImageElement::GetAnimatedPreserveAspectRatio() { 343 return &mPreserveAspectRatio; 344 } 345 346 SVGElement::StringAttributesInfo SVGFEImageElement::GetStringInfo() { 347 return StringAttributesInfo(mStringAttributes, sStringInfo, 348 std::size(sStringInfo)); 349 } 350 351 //---------------------------------------------------------------------- 352 // nsIImageLoadingContent methods 353 NS_IMETHODIMP_(void) 354 SVGFEImageElement::FrameCreated(nsIFrame* aFrame) { 355 nsImageLoadingContent::FrameCreated(aFrame); 356 357 auto mode = aFrame->PresContext()->ImageAnimationMode(); 358 if (mode == mImageAnimationMode) { 359 return; 360 } 361 362 mImageAnimationMode = mode; 363 364 if (mPendingRequest) { 365 nsCOMPtr<imgIContainer> container; 366 mPendingRequest->GetImage(getter_AddRefs(container)); 367 if (container) { 368 container->SetAnimationMode(mode); 369 } 370 } 371 372 if (mCurrentRequest) { 373 nsCOMPtr<imgIContainer> container; 374 mCurrentRequest->GetImage(getter_AddRefs(container)); 375 if (container) { 376 container->SetAnimationMode(mode); 377 } 378 } 379 } 380 381 //---------------------------------------------------------------------- 382 // imgINotificationObserver methods 383 384 void SVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType, 385 const nsIntRect* aData) { 386 nsImageLoadingContent::Notify(aRequest, aType, aData); 387 388 if (aType == imgINotificationObserver::SIZE_AVAILABLE) { 389 // Request a decode 390 nsCOMPtr<imgIContainer> container; 391 aRequest->GetImage(getter_AddRefs(container)); 392 MOZ_ASSERT(container, "who sent the notification then?"); 393 container->StartDecoding(imgIContainer::FLAG_NONE); 394 container->SetAnimationMode(mImageAnimationMode); 395 } 396 397 if (aType == imgINotificationObserver::LOAD_COMPLETE || 398 aType == imgINotificationObserver::FRAME_UPDATE || 399 aType == imgINotificationObserver::SIZE_AVAILABLE) { 400 if (auto* filter = SVGFilterElement::FromNodeOrNull(GetParent())) { 401 SVGObserverUtils::InvalidateDirectRenderingObservers(filter); 402 } 403 } 404 } 405 406 void SVGFEImageElement::DidAnimateAttribute(int32_t aNameSpaceID, 407 nsAtom* aAttribute) { 408 if ((aNameSpaceID == kNameSpaceID_None || 409 aNameSpaceID == kNameSpaceID_XLink) && 410 aAttribute == nsGkAtoms::href) { 411 UpdateSrcURI(); 412 QueueImageTask(mSrcURI, /* aAlwaysLoad = */ true, /* aNotify */ true); 413 } 414 SVGFEImageElementBase::DidAnimateAttribute(aNameSpaceID, aAttribute); 415 } 416 417 //---------------------------------------------------------------------- 418 // Public helper methods 419 420 void SVGFEImageElement::HrefAsString(nsAString& aHref) { 421 if (mStringAttributes[HREF].IsExplicitlySet()) { 422 mStringAttributes[HREF].GetBaseValue(aHref, this); 423 } else { 424 mStringAttributes[XLINK_HREF].GetBaseValue(aHref, this); 425 } 426 } 427 428 void SVGFEImageElement::NotifyImageContentChanged() { 429 // We don't support rendering fragments yet (bug 455986) 430 } 431 432 void SVGFEImageElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 433 size_t* aNodeSize) const { 434 SVGElement::AddSizeOfExcludingThis(aSizes, aNodeSize); 435 436 // It is okay to include the size of mSrcURI here even though it might have 437 // strong references from elsewhere because the URI was created for this 438 // object, in nsImageLoadingContent::StringToURI(). Only objects that created 439 // their own URI will call nsIURI::SizeOfIncludingThis(). 440 if (mSrcURI) { 441 *aNodeSize += mSrcURI->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf); 442 } 443 } 444 445 } // namespace mozilla::dom