1/*
2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(MEDIA_CONTROLS_SCRIPT)
29
30#include "MediaControlsHost.h"
31
32#include "CaptionUserPreferences.h"
33#include "Element.h"
34#include "HTMLMediaElement.h"
35#include "Logging.h"
36#include "MediaControlElements.h"
37#include "Page.h"
38#include "PageGroup.h"
39#include "RenderTheme.h"
40#include "TextTrack.h"
41#include "TextTrackList.h"
42#include <JavaScriptCore/JSCJSValueInlines.h>
43#include <wtf/UUID.h>
44
45namespace WebCore {
46
47const AtomString& MediaControlsHost::automaticKeyword()
48{
49 static NeverDestroyed<const AtomString> automatic("automatic", AtomString::ConstructFromLiteral);
50 return automatic;
51}
52
53const AtomString& MediaControlsHost::forcedOnlyKeyword()
54{
55 static NeverDestroyed<const AtomString> forcedOn("forced-only", AtomString::ConstructFromLiteral);
56 return forcedOn;
57}
58
59const AtomString& MediaControlsHost::alwaysOnKeyword()
60{
61 static NeverDestroyed<const AtomString> alwaysOn("always-on", AtomString::ConstructFromLiteral);
62 return alwaysOn;
63}
64
65const AtomString& MediaControlsHost::manualKeyword()
66{
67 static NeverDestroyed<const AtomString> alwaysOn("manual", AtomString::ConstructFromLiteral);
68 return alwaysOn;
69}
70
71
72Ref<MediaControlsHost> MediaControlsHost::create(HTMLMediaElement* mediaElement)
73{
74 return adoptRef(*new MediaControlsHost(mediaElement));
75}
76
77MediaControlsHost::MediaControlsHost(HTMLMediaElement* mediaElement)
78 : m_mediaElement(mediaElement)
79{
80 ASSERT(mediaElement);
81}
82
83MediaControlsHost::~MediaControlsHost() = default;
84
85Vector<RefPtr<TextTrack>> MediaControlsHost::sortedTrackListForMenu(TextTrackList& trackList)
86{
87 Page* page = m_mediaElement->document().page();
88 if (!page)
89 return { };
90
91 return page->group().captionPreferences().sortedTrackListForMenu(&trackList);
92}
93
94Vector<RefPtr<AudioTrack>> MediaControlsHost::sortedTrackListForMenu(AudioTrackList& trackList)
95{
96 Page* page = m_mediaElement->document().page();
97 if (!page)
98 return { };
99
100 return page->group().captionPreferences().sortedTrackListForMenu(&trackList);
101}
102
103String MediaControlsHost::displayNameForTrack(const Optional<TextOrAudioTrack>& track)
104{
105 if (!track)
106 return emptyString();
107
108 Page* page = m_mediaElement->document().page();
109 if (!page)
110 return emptyString();
111
112 return WTF::visit([&page](auto& track) {
113 return page->group().captionPreferences().displayNameForTrack(track.get());
114 }, track.value());
115}
116
117TextTrack* MediaControlsHost::captionMenuOffItem()
118{
119 return TextTrack::captionMenuOffItem();
120}
121
122TextTrack* MediaControlsHost::captionMenuAutomaticItem()
123{
124 return TextTrack::captionMenuAutomaticItem();
125}
126
127AtomString MediaControlsHost::captionDisplayMode() const
128{
129 Page* page = m_mediaElement->document().page();
130 if (!page)
131 return emptyAtom();
132
133 switch (page->group().captionPreferences().captionDisplayMode()) {
134 case CaptionUserPreferences::Automatic:
135 return automaticKeyword();
136 case CaptionUserPreferences::ForcedOnly:
137 return forcedOnlyKeyword();
138 case CaptionUserPreferences::AlwaysOn:
139 return alwaysOnKeyword();
140 case CaptionUserPreferences::Manual:
141 return manualKeyword();
142 default:
143 ASSERT_NOT_REACHED();
144 return emptyAtom();
145 }
146}
147
148void MediaControlsHost::setSelectedTextTrack(TextTrack* track)
149{
150 m_mediaElement->setSelectedTextTrack(track);
151}
152
153Element* MediaControlsHost::textTrackContainer()
154{
155 if (!m_textTrackContainer) {
156 m_textTrackContainer = MediaControlTextTrackContainerElement::create(m_mediaElement->document());
157 m_textTrackContainer->setMediaController(m_mediaElement);
158 }
159 return m_textTrackContainer.get();
160}
161
162void MediaControlsHost::updateTextTrackContainer()
163{
164 if (m_textTrackContainer)
165 m_textTrackContainer->updateDisplay();
166}
167
168void MediaControlsHost::enteredFullscreen()
169{
170 if (m_textTrackContainer)
171 m_textTrackContainer->enteredFullscreen();
172}
173
174void MediaControlsHost::exitedFullscreen()
175{
176 if (m_textTrackContainer)
177 m_textTrackContainer->exitedFullscreen();
178}
179
180void MediaControlsHost::updateCaptionDisplaySizes()
181{
182 if (m_textTrackContainer)
183 m_textTrackContainer->updateSizes(true);
184}
185
186bool MediaControlsHost::allowsInlineMediaPlayback() const
187{
188 return !m_mediaElement->mediaSession().requiresFullscreenForVideoPlayback();
189}
190
191bool MediaControlsHost::supportsFullscreen() const
192{
193 return m_mediaElement->supportsFullscreen(HTMLMediaElementEnums::VideoFullscreenModeStandard);
194}
195
196bool MediaControlsHost::isVideoLayerInline() const
197{
198 return m_mediaElement->isVideoLayerInline();
199}
200
201bool MediaControlsHost::isInMediaDocument() const
202{
203 return m_mediaElement->document().isMediaDocument();
204}
205
206void MediaControlsHost::setPreparedToReturnVideoLayerToInline(bool value)
207{
208 m_mediaElement->setPreparedToReturnVideoLayerToInline(value);
209}
210
211bool MediaControlsHost::userGestureRequired() const
212{
213 return !m_mediaElement->mediaSession().playbackPermitted();
214}
215
216bool MediaControlsHost::shouldForceControlsDisplay() const
217{
218 return m_mediaElement->shouldForceControlsDisplay();
219}
220
221String MediaControlsHost::externalDeviceDisplayName() const
222{
223#if ENABLE(WIRELESS_PLAYBACK_TARGET)
224 auto player = m_mediaElement->player();
225 if (!player) {
226 LOG(Media, "MediaControlsHost::externalDeviceDisplayName - returning \"\" because player is NULL");
227 return emptyString();
228 }
229
230 String name = player->wirelessPlaybackTargetName();
231 LOG(Media, "MediaControlsHost::externalDeviceDisplayName - returning \"%s\"", name.utf8().data());
232
233 return name;
234#else
235 return emptyString();
236#endif
237}
238
239auto MediaControlsHost::externalDeviceType() const -> DeviceType
240{
241#if !ENABLE(WIRELESS_PLAYBACK_TARGET)
242 return DeviceType::None;
243#else
244 auto player = m_mediaElement->player();
245 if (!player) {
246 LOG(Media, "MediaControlsHost::externalDeviceType - returning \"none\" because player is NULL");
247 return DeviceType::None;
248 }
249
250 switch (player->wirelessPlaybackTargetType()) {
251 case MediaPlayer::TargetTypeNone:
252 return DeviceType::None;
253 case MediaPlayer::TargetTypeAirPlay:
254 return DeviceType::Airplay;
255 case MediaPlayer::TargetTypeTVOut:
256 return DeviceType::Tvout;
257 }
258
259 ASSERT_NOT_REACHED();
260 return DeviceType::None;
261#endif
262}
263
264bool MediaControlsHost::controlsDependOnPageScaleFactor() const
265{
266 return m_mediaElement->mediaControlsDependOnPageScaleFactor();
267}
268
269void MediaControlsHost::setControlsDependOnPageScaleFactor(bool value)
270{
271 m_mediaElement->setMediaControlsDependOnPageScaleFactor(value);
272}
273
274String MediaControlsHost::generateUUID() const
275{
276 return createCanonicalUUIDString();
277}
278
279String MediaControlsHost::shadowRootCSSText() const
280{
281 return RenderTheme::singleton().modernMediaControlsStyleSheet();
282}
283
284String MediaControlsHost::base64StringForIconNameAndType(const String& iconName, const String& iconType) const
285{
286 return RenderTheme::singleton().mediaControlsBase64StringForIconNameAndType(iconName, iconType);
287}
288
289String MediaControlsHost::formattedStringForDuration(double durationInSeconds) const
290{
291 return RenderTheme::singleton().mediaControlsFormattedStringForDuration(durationInSeconds);
292}
293
294bool MediaControlsHost::compactMode() const
295{
296 if (m_simulateCompactMode)
297 return true;
298
299#if PLATFORM(WATCHOS)
300 return true;
301#else
302 return false;
303#endif
304}
305
306}
307
308#endif
309