HTMLSourceElement.cpp (8163B)
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/HTMLSourceElement.h" 8 9 #include "mozilla/AttributeStyles.h" 10 #include "mozilla/MappedDeclarationsBuilder.h" 11 #include "mozilla/Preferences.h" 12 #include "mozilla/dom/BlobURLProtocolHandler.h" 13 #include "mozilla/dom/DocumentInlines.h" 14 #include "mozilla/dom/HTMLImageElement.h" 15 #include "mozilla/dom/HTMLMediaElement.h" 16 #include "mozilla/dom/HTMLSourceElementBinding.h" 17 #include "mozilla/dom/MediaList.h" 18 #include "mozilla/dom/MediaSource.h" 19 #include "mozilla/dom/ResponsiveImageSelector.h" 20 #include "nsAttrValueOrString.h" 21 #include "nsGkAtoms.h" 22 23 NS_IMPL_NS_NEW_HTML_ELEMENT(Source) 24 25 namespace mozilla::dom { 26 27 HTMLSourceElement::HTMLSourceElement( 28 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 29 : nsGenericHTMLElement(std::move(aNodeInfo)) {} 30 31 HTMLSourceElement::~HTMLSourceElement() = default; 32 33 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLSourceElement, nsGenericHTMLElement, 34 mSrcMediaSource) 35 36 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLSourceElement, 37 nsGenericHTMLElement) 38 39 NS_IMPL_ELEMENT_CLONE(HTMLSourceElement) 40 41 bool HTMLSourceElement::MatchesCurrentMedia() { 42 if (mMediaList) { 43 return mMediaList->Matches(*OwnerDoc()); 44 } 45 46 // No media specified 47 return true; 48 } 49 50 /* static */ 51 bool HTMLSourceElement::WouldMatchMediaForDocument(const nsAString& aMedia, 52 const Document* aDocument) { 53 if (aMedia.IsEmpty()) { 54 return true; 55 } 56 57 RefPtr<MediaList> mediaList = 58 MediaList::Create(NS_ConvertUTF16toUTF8(aMedia)); 59 return mediaList->Matches(*aDocument); 60 } 61 62 void HTMLSourceElement::UpdateMediaList(const nsAttrValue* aValue) { 63 mMediaList = nullptr; 64 if (!aValue) { 65 return; 66 } 67 68 NS_ConvertUTF16toUTF8 mediaStr(nsAttrValueOrString(aValue).String()); 69 mMediaList = MediaList::Create(mediaStr); 70 } 71 72 bool HTMLSourceElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 73 const nsAString& aValue, 74 nsIPrincipal* aMaybeScriptedPrincipal, 75 nsAttrValue& aResult) { 76 if (aNamespaceID == kNameSpaceID_None && 77 (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height)) { 78 return aResult.ParseHTMLDimension(aValue); 79 } 80 81 return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, 82 aMaybeScriptedPrincipal, aResult); 83 } 84 85 void HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, 86 const nsAttrValue* aValue, 87 const nsAttrValue* aOldValue, 88 nsIPrincipal* aMaybeScriptedPrincipal, 89 bool aNotify) { 90 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::srcset) { 91 mSrcsetTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal( 92 this, nsAttrValueOrString(aValue).String(), aMaybeScriptedPrincipal); 93 } 94 // If we are associated with a <picture> with a valid <img>, notify it of 95 // responsive parameter changes 96 if (aNameSpaceID == kNameSpaceID_None && 97 (aName == nsGkAtoms::srcset || aName == nsGkAtoms::sizes || 98 aName == nsGkAtoms::media || aName == nsGkAtoms::type) && 99 IsInPicture()) { 100 if (aName == nsGkAtoms::media) { 101 UpdateMediaList(aValue); 102 } 103 104 nsAttrValueOrString value(aValue); 105 // Find all img siblings after this <source> and notify them of the change 106 nsCOMPtr<nsIContent> sibling = AsContent(); 107 while ((sibling = sibling->GetNextSibling())) { 108 if (auto* img = HTMLImageElement::FromNode(sibling)) { 109 if (aName == nsGkAtoms::srcset) { 110 img->PictureSourceSrcsetChanged(this, value.String(), aNotify); 111 } else if (aName == nsGkAtoms::sizes) { 112 img->PictureSourceSizesChanged(this, value.String(), aNotify); 113 } else if (aName == nsGkAtoms::media || aName == nsGkAtoms::type) { 114 img->PictureSourceMediaOrTypeChanged(this, aNotify); 115 } 116 } 117 } 118 } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::media) { 119 UpdateMediaList(aValue); 120 } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { 121 nsAttrValueOrString srcValue(aValue); 122 mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal( 123 this, srcValue.String(), aMaybeScriptedPrincipal); 124 mSrcMediaSource = nullptr; 125 if (aValue) { 126 nsCOMPtr<nsIURI> uri; 127 NewURIFromString(srcValue.String(), getter_AddRefs(uri)); 128 if (uri && IsMediaSourceURI(uri)) { 129 NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource)); 130 } 131 } 132 } else if (aNameSpaceID == kNameSpaceID_None && 133 IsAttributeMappedToImages(aName) && IsInPicture()) { 134 BuildMappedAttributesForImage(); 135 136 nsCOMPtr<nsIContent> sibling = AsContent(); 137 while ((sibling = sibling->GetNextSibling())) { 138 if (auto* img = HTMLImageElement::FromNode(sibling)) { 139 img->PictureSourceDimensionChanged(this, aNotify); 140 } 141 } 142 } 143 144 return nsGenericHTMLElement::AfterSetAttr( 145 aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify); 146 } 147 148 nsresult HTMLSourceElement::BindToTree(BindContext& aContext, 149 nsINode& aParent) { 150 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent); 151 NS_ENSURE_SUCCESS(rv, rv); 152 153 if (auto* media = HTMLMediaElement::FromNode(aParent)) { 154 media->NotifyAddedSource(); 155 } 156 157 if (aParent.IsHTMLElement(nsGkAtoms::picture)) { 158 BuildMappedAttributesForImage(); 159 } else { 160 mMappedAttributesForImage = nullptr; 161 } 162 163 return NS_OK; 164 } 165 166 void HTMLSourceElement::UnbindFromTree(UnbindContext& aContext) { 167 mMappedAttributesForImage = nullptr; 168 nsGenericHTMLElement::UnbindFromTree(aContext); 169 } 170 171 JSObject* HTMLSourceElement::WrapNode(JSContext* aCx, 172 JS::Handle<JSObject*> aGivenProto) { 173 return HTMLSourceElement_Binding::Wrap(aCx, this, aGivenProto); 174 } 175 176 /** 177 * Helper to map the image source attributes. 178 * Note: This will override the declaration created by the presentation 179 * attributes of HTMLImageElement (i.e. mapped by MapImageSizeAttributeInto). 180 * https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element 181 */ 182 void HTMLSourceElement::BuildMappedAttributesForImage() { 183 MOZ_ASSERT(NS_IsMainThread()); 184 185 mMappedAttributesForImage = nullptr; 186 187 Document* document = GetComposedDoc(); 188 if (!document) { 189 return; 190 } 191 192 const nsAttrValue* width = mAttrs.GetAttr(nsGkAtoms::width); 193 const nsAttrValue* height = mAttrs.GetAttr(nsGkAtoms::height); 194 if (!width && !height) { 195 return; 196 } 197 198 MappedDeclarationsBuilder builder(*this, *document); 199 // We should set the missing property values with auto value to make sure it 200 // overrides the declaration created by the presentation attributes of 201 // HTMLImageElement. This can make sure we compute the ratio-dependent axis 202 // size properly by the natural aspect-ratio of the image. 203 // 204 // Note: The spec doesn't specify this, so we follow the implementation in 205 // other browsers. 206 // Spec issue: https://github.com/whatwg/html/issues/8178. 207 if (width) { 208 MapDimensionAttributeInto(builder, eCSSProperty_width, *width); 209 } else { 210 builder.SetAutoValue(eCSSProperty_width); 211 } 212 213 if (height) { 214 MapDimensionAttributeInto(builder, eCSSProperty_height, *height); 215 } else { 216 builder.SetAutoValue(eCSSProperty_height); 217 } 218 219 if (width && height) { 220 DoMapAspectRatio(*width, *height, builder); 221 } else { 222 builder.SetAutoValue(eCSSProperty_aspect_ratio); 223 } 224 mMappedAttributesForImage = builder.TakeDeclarationBlock(); 225 } 226 227 } // namespace mozilla::dom