09fb8296d12e97c409531d9f487946a829b4fbb0
[WebKit-https.git] / Source / WebCore / platform / ios / WebVideoFullscreenModelVideoElement.mm
1 /*
2  * Copyright (C) 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 #import "config.h"
27
28 #if PLATFORM(IOS)
29 #import "WebVideoFullscreenModelVideoElement.h"
30
31 #import "DOMEventInternal.h"
32 #import "Logging.h"
33 #import "MediaControlsHost.h"
34 #import "WebVideoFullscreenInterface.h"
35 #import <QuartzCore/CoreAnimation.h>
36 #import <WebCore/DOMEventListener.h>
37 #import <WebCore/Event.h>
38 #import <WebCore/EventListener.h>
39 #import <WebCore/EventNames.h>
40 #import <WebCore/HTMLElement.h>
41 #import <WebCore/HTMLVideoElement.h>
42 #import <WebCore/Page.h>
43 #import <WebCore/PageGroup.h>
44 #import <WebCore/SoftLinking.h>
45 #import <WebCore/TextTrackList.h>
46 #import <WebCore/TimeRanges.h>
47 #import <WebCore/WebCoreThreadRun.h>
48 #import <wtf/NeverDestroyed.h>
49 #import <wtf/RetainPtr.h>
50
51
52 using namespace WebCore;
53
54 WebVideoFullscreenModelVideoElement::WebVideoFullscreenModelVideoElement()
55     : EventListener(EventListener::CPPEventListenerType)
56 {
57 }
58
59 WebVideoFullscreenModelVideoElement::~WebVideoFullscreenModelVideoElement()
60 {
61 }
62
63 void WebVideoFullscreenModelVideoElement::setWebVideoFullscreenInterface(WebVideoFullscreenInterface* interface)
64 {
65     if (interface == m_videoFullscreenInterface)
66         return;
67
68     m_videoFullscreenInterface = interface;
69
70     if (m_videoFullscreenInterface) {
71         m_videoFullscreenInterface->resetMediaState();
72         if (m_videoElement) {
73             m_videoFullscreenInterface->setVideoDimensions(true, m_videoElement->videoWidth(), m_videoElement->videoHeight());
74             m_videoFullscreenInterface->setWirelessVideoPlaybackDisabled(m_videoElement->mediaSession().wirelessVideoPlaybackDisabled(*m_videoElement));
75         }
76     }
77 }
78
79 void WebVideoFullscreenModelVideoElement::setVideoElement(HTMLVideoElement* videoElement)
80 {
81     if (m_videoElement == videoElement)
82         return;
83
84     if (m_videoFullscreenInterface)
85         m_videoFullscreenInterface->resetMediaState();
86
87     if (m_videoElement && m_videoElement->videoFullscreenLayer())
88         m_videoElement->setVideoFullscreenLayer(nullptr);
89
90     if (m_videoElement && m_isListening) {
91         for (auto& eventName : observedEventNames())
92             m_videoElement->removeEventListener(eventName, this, false);
93     }
94     m_isListening = false;
95
96     m_videoElement = videoElement;
97
98     if (!m_videoElement)
99         return;
100
101     for (auto& eventName : observedEventNames())
102         m_videoElement->addEventListener(eventName, this, false);
103     m_isListening = true;
104
105     updateForEventName(eventNameAll());
106
107     if (m_videoFullscreenInterface) {
108         m_videoFullscreenInterface->setVideoDimensions(true, videoElement->videoWidth(), videoElement->videoHeight());
109         m_videoFullscreenInterface->setWirelessVideoPlaybackDisabled(m_videoElement->mediaSession().wirelessVideoPlaybackDisabled(*m_videoElement));
110     }
111 }
112
113 void WebVideoFullscreenModelVideoElement::handleEvent(WebCore::ScriptExecutionContext*, WebCore::Event* event)
114 {
115     updateForEventName(event->type());
116 }
117
118 void WebVideoFullscreenModelVideoElement::updateForEventName(const WTF::AtomicString& eventName)
119 {
120     if (!m_videoElement || !m_videoFullscreenInterface)
121         return;
122     
123     bool all = eventName == eventNameAll();
124
125     if (all
126         || eventName == eventNames().durationchangeEvent) {
127         m_videoFullscreenInterface->setDuration(m_videoElement->duration());
128         // These is no standard event for minFastReverseRateChange; duration change is a reasonable proxy for it.
129         // It happens every time a new item becomes ready to play.
130         m_videoFullscreenInterface->setCanPlayFastReverse(m_videoElement->minFastReverseRate() < 0.0);
131     }
132
133     if (all
134         || eventName == eventNames().pauseEvent
135         || eventName == eventNames().playEvent
136         || eventName == eventNames().ratechangeEvent)
137         m_videoFullscreenInterface->setRate(!m_videoElement->paused(), m_videoElement->playbackRate());
138
139     if (all
140         || eventName == eventNames().timeupdateEvent) {
141         m_videoFullscreenInterface->setCurrentTime(m_videoElement->currentTime(), [[NSProcessInfo processInfo] systemUptime]);
142         m_videoFullscreenInterface->setBufferedTime(m_videoElement->maxBufferedTime());
143         // FIXME: 130788 - find a better event to update seekable ranges from.
144         m_videoFullscreenInterface->setSeekableRanges(*m_videoElement->seekable());
145     }
146
147     if (all
148         || eventName == eventNames().addtrackEvent
149         || eventName == eventNames().removetrackEvent)
150         updateLegibleOptions();
151
152     if (all
153         || eventName == eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent) {
154         bool enabled = m_videoElement->webkitCurrentPlaybackTargetIsWireless();
155         WebVideoFullscreenInterface::ExternalPlaybackTargetType targetType = WebVideoFullscreenInterface::TargetTypeNone;
156         String localizedDeviceName;
157
158         if (m_videoElement->mediaControlsHost()) {
159             DEPRECATED_DEFINE_STATIC_LOCAL(String, airplay, (ASCIILiteral("airplay")));
160             DEPRECATED_DEFINE_STATIC_LOCAL(String, tvout, (ASCIILiteral("tvout")));
161             
162             String type = m_videoElement->mediaControlsHost()->externalDeviceType();
163             if (type == airplay)
164                 targetType = WebVideoFullscreenInterface::TargetTypeAirPlay;
165             else if (type == tvout)
166                 targetType = WebVideoFullscreenInterface::TargetTypeTVOut;
167             localizedDeviceName = m_videoElement->mediaControlsHost()->externalDeviceDisplayName();
168         }
169         m_videoFullscreenInterface->setExternalPlayback(enabled, targetType, localizedDeviceName);
170         m_videoFullscreenInterface->setWirelessVideoPlaybackDisabled(m_videoElement->mediaSession().wirelessVideoPlaybackDisabled(*m_videoElement));
171     }
172 }
173
174 void WebVideoFullscreenModelVideoElement::setVideoFullscreenLayer(PlatformLayer* videoLayer)
175 {
176     if (m_videoFullscreenLayer == videoLayer)
177         return;
178     
179     m_videoFullscreenLayer = videoLayer;
180     [m_videoFullscreenLayer setAnchorPoint:CGPointMake(0.5, 0.5)];
181     [m_videoFullscreenLayer setBounds:m_videoFrame];
182     
183     if (m_videoElement)
184         m_videoElement->setVideoFullscreenLayer(m_videoFullscreenLayer.get());
185 }
186
187 void WebVideoFullscreenModelVideoElement::play()
188 {
189     if (m_videoElement)
190         m_videoElement->play();
191 }
192
193 void WebVideoFullscreenModelVideoElement::pause()
194 {
195     if (m_videoElement)
196         m_videoElement->pause();
197 }
198
199 void WebVideoFullscreenModelVideoElement::togglePlayState()
200 {
201     if (m_videoElement)
202         m_videoElement->togglePlayState();
203 }
204
205 void WebVideoFullscreenModelVideoElement::beginScrubbing()
206 {
207     if (m_videoElement)
208         m_videoElement->beginScrubbing();
209 }
210
211 void WebVideoFullscreenModelVideoElement::endScrubbing()
212 {
213     if (m_videoElement)
214         m_videoElement->endScrubbing();
215 }
216
217 void WebVideoFullscreenModelVideoElement::seekToTime(double time)
218 {
219     if (m_videoElement)
220         m_videoElement->setCurrentTime(time);
221 }
222
223 void WebVideoFullscreenModelVideoElement::fastSeek(double time)
224 {
225     if (m_videoElement)
226         m_videoElement->fastSeek(time);
227 }
228
229 void WebVideoFullscreenModelVideoElement::beginScanningForward()
230 {
231     if (m_videoElement)
232         m_videoElement->beginScanning(MediaControllerInterface::Forward);
233 }
234
235 void WebVideoFullscreenModelVideoElement::beginScanningBackward()
236 {
237     if (m_videoElement)
238         m_videoElement->beginScanning(MediaControllerInterface::Backward);
239 }
240
241 void WebVideoFullscreenModelVideoElement::endScanning()
242 {
243     if (m_videoElement)
244         m_videoElement->endScanning();
245 }
246
247 void WebVideoFullscreenModelVideoElement::requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
248 {
249     if (m_videoElement && m_videoElement->fullscreenMode() != mode)
250         m_videoElement->setFullscreenMode(mode);
251 }
252
253 void WebVideoFullscreenModelVideoElement::setVideoLayerFrame(FloatRect rect)
254 {
255     m_videoFrame = rect;
256     [m_videoFullscreenLayer setBounds:CGRect(rect)];
257     if (m_videoElement)
258         m_videoElement->setVideoFullscreenFrame(rect);
259 }
260
261 void WebVideoFullscreenModelVideoElement::setVideoLayerGravity(WebVideoFullscreenModel::VideoGravity gravity)
262 {
263     MediaPlayer::VideoGravity videoGravity = MediaPlayer::VideoGravityResizeAspect;
264     if (gravity == WebVideoFullscreenModel::VideoGravityResize)
265         videoGravity = MediaPlayer::VideoGravityResize;
266     else if (gravity == WebVideoFullscreenModel::VideoGravityResizeAspect)
267         videoGravity = MediaPlayer::VideoGravityResizeAspect;
268     else if (gravity == WebVideoFullscreenModel::VideoGravityResizeAspectFill)
269         videoGravity = MediaPlayer::VideoGravityResizeAspectFill;
270     else
271         ASSERT_NOT_REACHED();
272     
273     m_videoElement->setVideoFullscreenGravity(videoGravity);
274 }
275
276 void WebVideoFullscreenModelVideoElement::selectAudioMediaOption(uint64_t selectedAudioIndex)
277 {
278     AudioTrack* selectedAudioTrack = nullptr;
279
280     for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
281         auto& audioTrack = m_audioTracksForMenu[index];
282         audioTrack->setEnabled(index == static_cast<size_t>(selectedAudioIndex));
283         if (audioTrack->enabled())
284             selectedAudioTrack = audioTrack.get();
285     }
286
287     m_videoElement->audioTrackEnabledChanged(selectedAudioTrack);
288 }
289
290 void WebVideoFullscreenModelVideoElement::selectLegibleMediaOption(uint64_t index)
291 {
292     TextTrack* textTrack = nullptr;
293     
294     if (index < m_legibleTracksForMenu.size())
295         textTrack = m_legibleTracksForMenu[static_cast<size_t>(index)].get();
296     else
297         textTrack = TextTrack::captionMenuOffItem();
298     
299     m_videoElement->setSelectedTextTrack(textTrack);
300 }
301
302 void WebVideoFullscreenModelVideoElement::updateLegibleOptions()
303 {
304     AudioTrackList* audioTrackList = m_videoElement->audioTracks();
305     TextTrackList* trackList = m_videoElement->textTracks();
306
307     if ((!trackList && !audioTrackList) || !m_videoElement->document().page() || !m_videoElement->mediaControlsHost())
308         return;
309     
310     WTF::AtomicString displayMode = m_videoElement->mediaControlsHost()->captionDisplayMode();
311     TextTrack* offItem = m_videoElement->mediaControlsHost()->captionMenuOffItem();
312     TextTrack* automaticItem = m_videoElement->mediaControlsHost()->captionMenuAutomaticItem();
313     CaptionUserPreferences& captionPreferences = *m_videoElement->document().page()->group().captionPreferences();
314     m_legibleTracksForMenu = captionPreferences.sortedTrackListForMenu(trackList);
315
316     m_audioTracksForMenu = captionPreferences.sortedTrackListForMenu(audioTrackList);
317     
318     Vector<String> audioTrackDisplayNames;
319     uint64_t selectedAudioIndex = 0;
320     
321     for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
322         auto& track = m_audioTracksForMenu[index];
323         audioTrackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
324         
325         if (track->enabled())
326             selectedAudioIndex = index;
327     }
328     
329     m_videoFullscreenInterface->setAudioMediaSelectionOptions(audioTrackDisplayNames, selectedAudioIndex);
330
331     Vector<String> trackDisplayNames;
332     uint64_t selectedIndex = 0;
333     uint64_t offIndex = 0;
334     bool trackMenuItemSelected = false;
335     
336     for (size_t index = 0; index < m_legibleTracksForMenu.size(); index++) {
337         auto& track = m_legibleTracksForMenu[index];
338         trackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
339         
340         if (track == offItem)
341             offIndex = index;
342         
343         if (track == automaticItem && displayMode == MediaControlsHost::automaticKeyword()) {
344             selectedIndex = index;
345             trackMenuItemSelected = true;
346         }
347         
348         if (displayMode != MediaControlsHost::automaticKeyword() && track->mode() == TextTrack::showingKeyword()) {
349             selectedIndex = index;
350             trackMenuItemSelected = true;
351         }
352     }
353     
354     if (offIndex && !trackMenuItemSelected && displayMode == MediaControlsHost::forcedOnlyKeyword()) {
355         selectedIndex = offIndex;
356         trackMenuItemSelected = true;
357     }
358     
359     m_videoFullscreenInterface->setLegibleMediaSelectionOptions(trackDisplayNames, selectedIndex);
360 }
361
362 const Vector<AtomicString>& WebVideoFullscreenModelVideoElement::observedEventNames()
363 {
364     static NeverDestroyed<Vector<AtomicString>> sEventNames;
365
366     if (!sEventNames.get().size()) {
367         sEventNames.get().append(eventNames().durationchangeEvent);
368         sEventNames.get().append(eventNames().pauseEvent);
369         sEventNames.get().append(eventNames().playEvent);
370         sEventNames.get().append(eventNames().ratechangeEvent);
371         sEventNames.get().append(eventNames().timeupdateEvent);
372         sEventNames.get().append(eventNames().addtrackEvent);
373         sEventNames.get().append(eventNames().removetrackEvent);
374         sEventNames.get().append(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent);
375     }
376     return sEventNames.get();
377 }
378
379 const AtomicString& WebVideoFullscreenModelVideoElement::eventNameAll()
380 {
381     static NeverDestroyed<AtomicString> sEventNameAll = "allEvents";
382     return sEventNameAll;
383 }
384
385 void WebVideoFullscreenModelVideoElement::fullscreenModeChanged(HTMLMediaElementEnums::VideoFullscreenMode videoFullscreenMode)
386 {
387     if (m_videoElement)
388         m_videoElement->fullscreenModeChanged(videoFullscreenMode);
389 }
390
391 bool WebVideoFullscreenModelVideoElement::isVisible() const
392 {
393     if (!m_videoElement)
394         return false;
395
396     if (Page* page = m_videoElement->document().page())
397         return page->isVisible();
398
399     return false;
400 }
401
402 #endif