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/HTMLSourceElement.h"
8 #include "mozilla/dom/HTMLSourceElementBinding.h"
10 #include "mozilla/dom/DocumentInlines.h"
11 #include "mozilla/dom/HTMLImageElement.h"
12 #include "mozilla/dom/HTMLMediaElement.h"
13 #include "mozilla/dom/ResponsiveImageSelector.h"
14 #include "mozilla/dom/MediaList.h"
15 #include "mozilla/dom/MediaSource.h"
17 #include "mozilla/dom/BlobURLProtocolHandler.h"
18 #include "mozilla/AttributeStyles.h"
19 #include "mozilla/MappedDeclarationsBuilder.h"
20 #include "mozilla/Preferences.h"
22 #include "nsGkAtoms.h"
24 NS_IMPL_NS_NEW_HTML_ELEMENT(Source
)
26 namespace mozilla::dom
{
28 HTMLSourceElement::HTMLSourceElement(
29 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
30 : nsGenericHTMLElement(std::move(aNodeInfo
)) {}
32 HTMLSourceElement::~HTMLSourceElement() = default;
34 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLSourceElement
, nsGenericHTMLElement
,
37 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLSourceElement
,
40 NS_IMPL_ELEMENT_CLONE(HTMLSourceElement
)
42 bool HTMLSourceElement::MatchesCurrentMedia() {
44 return mMediaList
->Matches(*OwnerDoc());
52 bool HTMLSourceElement::WouldMatchMediaForDocument(const nsAString
& aMedia
,
53 const Document
* aDocument
) {
54 if (aMedia
.IsEmpty()) {
58 RefPtr
<MediaList
> mediaList
=
59 MediaList::Create(NS_ConvertUTF16toUTF8(aMedia
));
60 return mediaList
->Matches(*aDocument
);
63 void HTMLSourceElement::UpdateMediaList(const nsAttrValue
* aValue
) {
69 NS_ConvertUTF16toUTF8
mediaStr(aValue
->GetStringValue());
70 mMediaList
= MediaList::Create(mediaStr
);
73 bool HTMLSourceElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
74 const nsAString
& aValue
,
75 nsIPrincipal
* aMaybeScriptedPrincipal
,
76 nsAttrValue
& aResult
) {
77 if (aNamespaceID
== kNameSpaceID_None
&&
78 (aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
)) {
79 return aResult
.ParseHTMLDimension(aValue
);
82 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
83 aMaybeScriptedPrincipal
, aResult
);
86 void HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
87 const nsAttrValue
* aValue
,
88 const nsAttrValue
* aOldValue
,
89 nsIPrincipal
* aMaybeScriptedPrincipal
,
91 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::srcset
) {
92 mSrcsetTriggeringPrincipal
= nsContentUtils::GetAttrTriggeringPrincipal(
93 this, aValue
? aValue
->GetStringValue() : EmptyString(),
94 aMaybeScriptedPrincipal
);
96 // If we are associated with a <picture> with a valid <img>, notify it of
97 // responsive parameter changes
98 if (aNameSpaceID
== kNameSpaceID_None
&&
99 (aName
== nsGkAtoms::srcset
|| aName
== nsGkAtoms::sizes
||
100 aName
== nsGkAtoms::media
|| aName
== nsGkAtoms::type
) &&
102 if (aName
== nsGkAtoms::media
) {
103 UpdateMediaList(aValue
);
106 nsString strVal
= aValue
? aValue
->GetStringValue() : EmptyString();
107 // Find all img siblings after this <source> and notify them of the change
108 nsCOMPtr
<nsIContent
> sibling
= AsContent();
109 while ((sibling
= sibling
->GetNextSibling())) {
110 if (auto* img
= HTMLImageElement::FromNode(sibling
)) {
111 if (aName
== nsGkAtoms::srcset
) {
112 img
->PictureSourceSrcsetChanged(this, strVal
, aNotify
);
113 } else if (aName
== nsGkAtoms::sizes
) {
114 img
->PictureSourceSizesChanged(this, strVal
, aNotify
);
115 } else if (aName
== nsGkAtoms::media
|| aName
== nsGkAtoms::type
) {
116 img
->PictureSourceMediaOrTypeChanged(this, aNotify
);
120 } else if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::media
) {
121 UpdateMediaList(aValue
);
122 } else if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::src
) {
123 mSrcTriggeringPrincipal
= nsContentUtils::GetAttrTriggeringPrincipal(
124 this, aValue
? aValue
->GetStringValue() : EmptyString(),
125 aMaybeScriptedPrincipal
);
126 mSrcMediaSource
= nullptr;
128 nsString srcStr
= aValue
->GetStringValue();
129 nsCOMPtr
<nsIURI
> uri
;
130 NewURIFromString(srcStr
, getter_AddRefs(uri
));
131 if (uri
&& IsMediaSourceURI(uri
)) {
132 NS_GetSourceForMediaSourceURI(uri
, getter_AddRefs(mSrcMediaSource
));
135 } else if (aNameSpaceID
== kNameSpaceID_None
&&
136 IsAttributeMappedToImages(aName
) && IsInPicture()) {
137 BuildMappedAttributesForImage();
139 nsCOMPtr
<nsIContent
> sibling
= AsContent();
140 while ((sibling
= sibling
->GetNextSibling())) {
141 if (auto* img
= HTMLImageElement::FromNode(sibling
)) {
142 img
->PictureSourceDimensionChanged(this, aNotify
);
147 return nsGenericHTMLElement::AfterSetAttr(
148 aNameSpaceID
, aName
, aValue
, aOldValue
, aMaybeScriptedPrincipal
, aNotify
);
151 nsresult
HTMLSourceElement::BindToTree(BindContext
& aContext
,
153 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
154 NS_ENSURE_SUCCESS(rv
, rv
);
156 if (auto* media
= HTMLMediaElement::FromNode(aParent
)) {
157 media
->NotifyAddedSource();
160 if (aParent
.IsHTMLElement(nsGkAtoms::picture
)) {
161 BuildMappedAttributesForImage();
163 mMappedAttributesForImage
= nullptr;
169 void HTMLSourceElement::UnbindFromTree(UnbindContext
& aContext
) {
170 mMappedAttributesForImage
= nullptr;
171 nsGenericHTMLElement::UnbindFromTree(aContext
);
174 JSObject
* HTMLSourceElement::WrapNode(JSContext
* aCx
,
175 JS::Handle
<JSObject
*> aGivenProto
) {
176 return HTMLSourceElement_Binding::Wrap(aCx
, this, aGivenProto
);
180 * Helper to map the image source attributes.
181 * Note: This will override the declaration created by the presentation
182 * attributes of HTMLImageElement (i.e. mapped by MapImageSizeAttributeInto).
183 * https://html.spec.whatwg.org/multipage/embedded-content.html#the-source-element
185 void HTMLSourceElement::BuildMappedAttributesForImage() {
186 MOZ_ASSERT(NS_IsMainThread());
188 mMappedAttributesForImage
= nullptr;
190 Document
* document
= GetComposedDoc();
195 const nsAttrValue
* width
= mAttrs
.GetAttr(nsGkAtoms::width
);
196 const nsAttrValue
* height
= mAttrs
.GetAttr(nsGkAtoms::height
);
197 if (!width
&& !height
) {
201 MappedDeclarationsBuilder
builder(*this, *document
);
202 // We should set the missing property values with auto value to make sure it
203 // overrides the declaration created by the presentation attributes of
204 // HTMLImageElement. This can make sure we compute the ratio-dependent axis
205 // size properly by the natural aspect-ratio of the image.
207 // Note: The spec doesn't specify this, so we follow the implementation in
209 // Spec issue: https://github.com/whatwg/html/issues/8178.
211 MapDimensionAttributeInto(builder
, eCSSProperty_width
, *width
);
213 builder
.SetAutoValue(eCSSProperty_width
);
217 MapDimensionAttributeInto(builder
, eCSSProperty_height
, *height
);
219 builder
.SetAutoValue(eCSSProperty_height
);
222 if (width
&& height
) {
223 DoMapAspectRatio(*width
, *height
, builder
);
225 builder
.SetAutoValue(eCSSProperty_aspect_ratio
);
227 mMappedAttributesForImage
= builder
.TakeDeclarationBlock();
230 } // namespace mozilla::dom