SVGImageElement.cpp (12304B)
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/SVGImageElement.h" 8 9 #include "SVGGeometryProperty.h" 10 #include "imgINotificationObserver.h" 11 #include "mozilla/dom/Document.h" 12 #include "mozilla/dom/FetchPriority.h" 13 #include "mozilla/dom/SVGImageElementBinding.h" 14 #include "mozilla/dom/SVGLengthBinding.h" 15 #include "mozilla/dom/UserActivation.h" 16 #include "mozilla/gfx/2D.h" 17 #include "nsCOMPtr.h" 18 #include "nsContentUtils.h" 19 #include "nsNetUtil.h" 20 21 NS_IMPL_NS_NEW_SVG_ELEMENT(Image) 22 23 using namespace mozilla::gfx; 24 25 namespace mozilla::dom { 26 27 JSObject* SVGImageElement::WrapNode(JSContext* aCx, 28 JS::Handle<JSObject*> aGivenProto) { 29 return SVGImageElement_Binding::Wrap(aCx, this, aGivenProto); 30 } 31 32 SVGElement::LengthInfo SVGImageElement::sLengthInfo[4] = { 33 {nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, 34 SVGContentUtils::X}, 35 {nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, 36 SVGContentUtils::Y}, 37 {nsGkAtoms::width, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, 38 SVGContentUtils::X}, 39 {nsGkAtoms::height, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, 40 SVGContentUtils::Y}, 41 }; 42 43 SVGElement::StringInfo SVGImageElement::sStringInfo[2] = { 44 {nsGkAtoms::href, kNameSpaceID_None, true}, 45 {nsGkAtoms::href, kNameSpaceID_XLink, true}}; 46 47 //---------------------------------------------------------------------- 48 // nsISupports methods 49 50 NS_IMPL_ISUPPORTS_INHERITED(SVGImageElement, SVGImageElementBase, 51 imgINotificationObserver, nsIImageLoadingContent) 52 53 //---------------------------------------------------------------------- 54 // Implementation 55 56 namespace SVGT = SVGGeometryProperty::Tags; 57 58 SVGImageElement::SVGImageElement( 59 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 60 : SVGImageElementBase(std::move(aNodeInfo)) { 61 // We start out broken 62 AddStatesSilently(ElementState::BROKEN); 63 } 64 65 SVGImageElement::~SVGImageElement() { nsImageLoadingContent::Destroy(); } 66 67 NonCustomCSSPropertyId SVGImageElement::GetCSSPropertyIdForAttrEnum( 68 uint8_t aAttrEnum) { 69 switch (aAttrEnum) { 70 case ATTR_X: 71 return eCSSProperty_x; 72 case ATTR_Y: 73 return eCSSProperty_y; 74 case ATTR_WIDTH: 75 return eCSSProperty_width; 76 case ATTR_HEIGHT: 77 return eCSSProperty_height; 78 default: 79 MOZ_ASSERT_UNREACHABLE("Unknown attr enum"); 80 return eCSSProperty_UNKNOWN; 81 } 82 } 83 //---------------------------------------------------------------------- 84 // nsINode methods 85 86 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGImageElement) 87 88 void SVGImageElement::NodeInfoChanged(Document* aOldDoc) { 89 SVGImageElementBase::NodeInfoChanged(aOldDoc); 90 91 // Reparse the URI if needed. Note that we can't check whether we already have 92 // a parsed URI, because it might be null even if we have a valid href 93 // attribute, if we tried to parse with a different base. 94 UpdateSrcURI(); 95 96 QueueImageTask(mSrcURI, /* aAlwaysLoad = */ true, /* aNotify = */ false); 97 } 98 99 //---------------------------------------------------------------------- 100 101 already_AddRefed<DOMSVGAnimatedLength> SVGImageElement::X() { 102 return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this); 103 } 104 105 already_AddRefed<DOMSVGAnimatedLength> SVGImageElement::Y() { 106 return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this); 107 } 108 109 already_AddRefed<DOMSVGAnimatedLength> SVGImageElement::Width() { 110 return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this); 111 } 112 113 already_AddRefed<DOMSVGAnimatedLength> SVGImageElement::Height() { 114 return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this); 115 } 116 117 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> 118 SVGImageElement::PreserveAspectRatio() { 119 return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); 120 } 121 122 already_AddRefed<DOMSVGAnimatedString> SVGImageElement::Href() { 123 return mStringAttributes[HREF].IsExplicitlySet() || 124 !mStringAttributes[XLINK_HREF].IsExplicitlySet() 125 ? mStringAttributes[HREF].ToDOMAnimatedString(this) 126 : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this); 127 } 128 129 void SVGImageElement::GetDecoding(nsAString& aValue) { 130 GetEnumAttr(nsGkAtoms::decoding, kDecodingTableDefault->tag, aValue); 131 } 132 133 already_AddRefed<Promise> SVGImageElement::Decode(ErrorResult& aRv) { 134 return nsImageLoadingContent::QueueDecodeAsync(aRv); 135 } 136 137 //---------------------------------------------------------------------- 138 139 void SVGImageElement::UpdateSrcURI() { 140 nsAutoString href; 141 if (mStringAttributes[HREF].IsExplicitlySet()) { 142 mStringAttributes[HREF].GetAnimValue(href, this); 143 } else { 144 mStringAttributes[XLINK_HREF].GetAnimValue(href, this); 145 } 146 147 mSrcURI = nullptr; 148 if (!href.IsEmpty()) { 149 StringToURI(href, OwnerDoc(), getter_AddRefs(mSrcURI)); 150 } 151 } 152 153 void SVGImageElement::LoadSelectedImage(bool aAlwaysLoad, 154 bool aStopLazyLoading) { 155 nsresult rv = NS_ERROR_FAILURE; 156 157 const bool kNotify = true; 158 if (mSrcURI || (mStringAttributes[HREF].IsExplicitlySet() || 159 mStringAttributes[XLINK_HREF].IsExplicitlySet())) { 160 rv = LoadImage(mSrcURI, /* aForce = */ true, kNotify, eImageLoadType_Normal, 161 LoadFlags(), OwnerDoc()); 162 } 163 164 if (NS_FAILED(rv)) { 165 CancelImageRequests(kNotify); 166 } 167 } 168 169 Rect SVGImageElement::GeometryBounds(const Matrix& aToBoundsSpace) { 170 Rect rect; 171 172 DebugOnly<bool> ok = 173 SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width, 174 SVGT::Height>(this, &rect.x, &rect.y, 175 &rect.width, &rect.height); 176 MOZ_ASSERT(ok, "SVGGeometryProperty::ResolveAll failed"); 177 178 if (rect.IsEmpty()) { 179 // Rendering of the element disabled 180 rect.SetEmpty(); // Make sure width/height are zero and not negative 181 } 182 183 return aToBoundsSpace.TransformBounds(rect); 184 } 185 186 //---------------------------------------------------------------------- 187 // EventTarget methods: 188 189 void SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) { 190 nsImageLoadingContent::AsyncEventRunning(aEvent); 191 } 192 193 //---------------------------------------------------------------------- 194 // nsImageLoadingContent methods: 195 196 CORSMode SVGImageElement::GetCORSMode() { 197 return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin)); 198 } 199 200 void SVGImageElement::GetFetchPriority(nsAString& aFetchPriority) const { 201 GetEnumAttr(nsGkAtoms::fetchpriority, kFetchPriorityAttributeValueAuto, 202 aFetchPriority); 203 } 204 205 //---------------------------------------------------------------------- 206 // nsIContent methods: 207 208 bool SVGImageElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 209 const nsAString& aValue, 210 nsIPrincipal* aMaybeScriptedPrincipal, 211 nsAttrValue& aResult) { 212 if (aNamespaceID == kNameSpaceID_None) { 213 if (aAttribute == nsGkAtoms::crossorigin) { 214 ParseCORSValue(aValue, aResult); 215 return true; 216 } 217 if (aAttribute == nsGkAtoms::decoding) { 218 return aResult.ParseEnumValue(aValue, kDecodingTable, false, 219 kDecodingTableDefault); 220 } 221 if (aAttribute == nsGkAtoms::fetchpriority) { 222 ParseFetchPriority(aValue, aResult); 223 return true; 224 } 225 } 226 227 return SVGImageElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue, 228 aMaybeScriptedPrincipal, aResult); 229 } 230 231 void SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 232 const nsAttrValue* aValue, 233 const nsAttrValue* aOldValue, 234 nsIPrincipal* aSubjectPrincipal, 235 bool aNotify) { 236 bool forceReload = false; 237 if (aName == nsGkAtoms::href && (aNamespaceID == kNameSpaceID_None || 238 aNamespaceID == kNameSpaceID_XLink)) { 239 if (aNamespaceID == kNameSpaceID_XLink && 240 mStringAttributes[HREF].IsExplicitlySet()) { 241 // href overrides xlink:href 242 return; 243 } 244 UpdateSrcURI(); 245 forceReload = true; 246 } else if (aNamespaceID == kNameSpaceID_None) { 247 if (aName == nsGkAtoms::decoding) { 248 // Request sync or async image decoding. 249 SetSyncDecodingHint( 250 aValue && static_cast<ImageDecodingType>(aValue->GetEnumValue()) == 251 ImageDecodingType::Sync); 252 } else if (aName == nsGkAtoms::crossorigin) { 253 forceReload = GetCORSMode() != AttrValueToCORSMode(aOldValue); 254 } 255 } 256 257 if (forceReload) { 258 mUseUrgentStartForChannel = UserActivation::IsHandlingUserInput(); 259 QueueImageTask(mSrcURI, /* aAlwaysLoad = */ true, aNotify); 260 } 261 262 return SVGImageElementBase::AfterSetAttr( 263 aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); 264 } 265 266 nsresult SVGImageElement::BindToTree(BindContext& aContext, nsINode& aParent) { 267 nsresult rv = SVGImageElementBase::BindToTree(aContext, aParent); 268 NS_ENSURE_SUCCESS(rv, rv); 269 270 nsImageLoadingContent::BindToTree(aContext, aParent); 271 272 return rv; 273 } 274 275 void SVGImageElement::UnbindFromTree(UnbindContext& aContext) { 276 nsImageLoadingContent::UnbindFromTree(); 277 SVGImageElementBase::UnbindFromTree(aContext); 278 } 279 280 void SVGImageElement::DestroyContent() { 281 ClearImageLoadTask(); 282 283 nsImageLoadingContent::Destroy(); 284 SVGImageElementBase::DestroyContent(); 285 } 286 287 NS_IMETHODIMP_(bool) 288 SVGImageElement::IsAttributeMapped(const nsAtom* name) const { 289 return IsInLengthInfo(name, sLengthInfo) || 290 SVGImageElementBase::IsAttributeMapped(name); 291 } 292 293 //---------------------------------------------------------------------- 294 // SVGElement methods 295 296 /* virtual */ 297 bool SVGImageElement::HasValidDimensions() const { 298 float width, height; 299 300 if (SVGGeometryProperty::ResolveAll<SVGT::Width, SVGT::Height>(this, &width, 301 &height)) { 302 return width > 0 && height > 0; 303 } 304 // This function might be called for an element in display:none subtree 305 // (e.g. SMIL animateMotion), we fall back to use SVG attributes. 306 return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() || 307 mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) && 308 (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() || 309 mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0); 310 } 311 312 SVGElement::LengthAttributesInfo SVGImageElement::GetLengthInfo() { 313 return LengthAttributesInfo(mLengthAttributes, sLengthInfo, 314 std::size(sLengthInfo)); 315 } 316 317 SVGAnimatedPreserveAspectRatio* 318 SVGImageElement::GetAnimatedPreserveAspectRatio() { 319 return &mPreserveAspectRatio; 320 } 321 322 SVGElement::StringAttributesInfo SVGImageElement::GetStringInfo() { 323 return StringAttributesInfo(mStringAttributes, sStringInfo, 324 std::size(sStringInfo)); 325 } 326 327 void SVGImageElement::DidAnimateAttribute(int32_t aNameSpaceID, 328 nsAtom* aAttribute) { 329 if ((aNameSpaceID == kNameSpaceID_None || 330 aNameSpaceID == kNameSpaceID_XLink) && 331 aAttribute == nsGkAtoms::href) { 332 UpdateSrcURI(); 333 mUseUrgentStartForChannel = false; 334 QueueImageTask(mSrcURI, /* aAlwaysLoad = */ true, /* aNotify = */ true); 335 } 336 SVGImageElementBase::DidAnimateAttribute(aNameSpaceID, aAttribute); 337 } 338 339 void SVGImageElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 340 size_t* aNodeSize) const { 341 SVGElement::AddSizeOfExcludingThis(aSizes, aNodeSize); 342 343 // It is okay to include the size of mSrcURI here even though it might have 344 // strong references from elsewhere because the URI was created for this 345 // object, in nsImageLoadingContent::StringToURI(). Only objects that created 346 // their own URI will call nsIURI::SizeOfIncludingThis(). 347 if (mSrcURI) { 348 *aNodeSize += mSrcURI->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf); 349 } 350 } 351 352 } // namespace mozilla::dom