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 #include "mozilla/dom/HTMLSourceElementBinding.h"
9
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"
16
17 #include "nsGkAtoms.h"
18
19 #include "mozilla/dom/BlobURLProtocolHandler.h"
20 #include "mozilla/Preferences.h"
21
22 NS_IMPL_NS_NEW_HTML_ELEMENT(Source)
23
24 namespace mozilla::dom {
25
HTMLSourceElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)26 HTMLSourceElement::HTMLSourceElement(
27 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
28 : nsGenericHTMLElement(std::move(aNodeInfo)) {}
29
30 HTMLSourceElement::~HTMLSourceElement() = default;
31
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLSourceElement,nsGenericHTMLElement,mSrcMediaSource)32 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLSourceElement, nsGenericHTMLElement,
33 mSrcMediaSource)
34
35 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLSourceElement,
36 nsGenericHTMLElement)
37
38 NS_IMPL_ELEMENT_CLONE(HTMLSourceElement)
39
40 bool HTMLSourceElement::MatchesCurrentMedia() {
41 if (mMediaList) {
42 return mMediaList->Matches(*OwnerDoc());
43 }
44
45 // No media specified
46 return true;
47 }
48
49 /* static */
WouldMatchMediaForDocument(const nsAString & aMedia,const Document * aDocument)50 bool HTMLSourceElement::WouldMatchMediaForDocument(const nsAString& aMedia,
51 const Document* aDocument) {
52 if (aMedia.IsEmpty()) {
53 return true;
54 }
55
56 RefPtr<MediaList> mediaList =
57 MediaList::Create(NS_ConvertUTF16toUTF8(aMedia));
58 return mediaList->Matches(*aDocument);
59 }
60
UpdateMediaList(const nsAttrValue * aValue)61 void HTMLSourceElement::UpdateMediaList(const nsAttrValue* aValue) {
62 mMediaList = nullptr;
63 if (!aValue) {
64 return;
65 }
66
67 NS_ConvertUTF16toUTF8 mediaStr(aValue->GetStringValue());
68 mMediaList = MediaList::Create(mediaStr);
69 }
70
AfterSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aMaybeScriptedPrincipal,bool aNotify)71 nsresult HTMLSourceElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
72 const nsAttrValue* aValue,
73 const nsAttrValue* aOldValue,
74 nsIPrincipal* aMaybeScriptedPrincipal,
75 bool aNotify) {
76 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::srcset) {
77 mSrcsetTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
78 this, aValue ? aValue->GetStringValue() : EmptyString(),
79 aMaybeScriptedPrincipal);
80 }
81 // If we are associated with a <picture> with a valid <img>, notify it of
82 // responsive parameter changes
83 Element* parent = nsINode::GetParentElement();
84 if (aNameSpaceID == kNameSpaceID_None &&
85 (aName == nsGkAtoms::srcset || aName == nsGkAtoms::sizes ||
86 aName == nsGkAtoms::media || aName == nsGkAtoms::type) &&
87 parent && parent->IsHTMLElement(nsGkAtoms::picture)) {
88 if (aName == nsGkAtoms::media) {
89 UpdateMediaList(aValue);
90 }
91
92 nsString strVal = aValue ? aValue->GetStringValue() : EmptyString();
93 // Find all img siblings after this <source> and notify them of the change
94 nsCOMPtr<nsIContent> sibling = AsContent();
95 while ((sibling = sibling->GetNextSibling())) {
96 if (auto* img = HTMLImageElement::FromNode(sibling)) {
97 if (aName == nsGkAtoms::srcset) {
98 img->PictureSourceSrcsetChanged(this, strVal, aNotify);
99 } else if (aName == nsGkAtoms::sizes) {
100 img->PictureSourceSizesChanged(this, strVal, aNotify);
101 } else if (aName == nsGkAtoms::media || aName == nsGkAtoms::type) {
102 img->PictureSourceMediaOrTypeChanged(this, aNotify);
103 }
104 }
105 }
106 } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::media) {
107 UpdateMediaList(aValue);
108 } else if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
109 mSrcTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
110 this, aValue ? aValue->GetStringValue() : EmptyString(),
111 aMaybeScriptedPrincipal);
112 mSrcMediaSource = nullptr;
113 if (aValue) {
114 nsString srcStr = aValue->GetStringValue();
115 nsCOMPtr<nsIURI> uri;
116 NewURIFromString(srcStr, getter_AddRefs(uri));
117 if (uri && IsMediaSourceURI(uri)) {
118 NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource));
119 }
120 }
121 }
122
123 return nsGenericHTMLElement::AfterSetAttr(
124 aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
125 }
126
BindToTree(BindContext & aContext,nsINode & aParent)127 nsresult HTMLSourceElement::BindToTree(BindContext& aContext,
128 nsINode& aParent) {
129 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
130 NS_ENSURE_SUCCESS(rv, rv);
131
132 if (auto* media = HTMLMediaElement::FromNode(aParent)) {
133 media->NotifyAddedSource();
134 }
135
136 return NS_OK;
137 }
138
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)139 JSObject* HTMLSourceElement::WrapNode(JSContext* aCx,
140 JS::Handle<JSObject*> aGivenProto) {
141 return HTMLSourceElement_Binding::Wrap(aCx, this, aGivenProto);
142 }
143
144 } // namespace mozilla::dom
145