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