[iOS] When simultaneously exiting-and-entering fullscreen, WebVideoFullscreenManager...
[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     }
75 }
76
77 void WebVideoFullscreenModelVideoElement::setVideoElement(HTMLVideoElement* videoElement)
78 {
79     if (m_videoElement == videoElement)
80         return;
81
82     if (m_videoFullscreenInterface)
83         m_videoFullscreenInterface->resetMediaState();
84
85     if (m_videoElement && m_videoElement->fullscreenMode())
86         m_videoElement->fullscreenModeChanged(HTMLMediaElement::VideoFullscreenModeNone);
87
88     if (m_videoElement && m_videoElement->videoFullscreenLayer())
89         m_videoElement->setVideoFullscreenLayer(nullptr);
90
91     if (m_videoElement && m_isListening) {
92         for (auto eventName : observedEventNames())
93             m_videoElement->removeEventListener(eventName, this, false);
94     }
95     m_isListening = false;
96
97     m_videoElement = videoElement;
98
99     if (!m_videoElement)
100         return;
101
102     for (auto eventName : observedEventNames())
103         m_videoElement->addEventListener(eventName, this, false);
104     m_isListening = true;
105
106     updateForEventName(eventNameAll());
107
108     if (m_videoFullscreenInterface)
109         m_videoFullscreenInterface->setVideoDimensions(true, videoElement->videoWidth(), videoElement->videoHeight());
110 }
111
112 void WebVideoFullscreenModelVideoElement::handleEvent(WebCore::ScriptExecutionContext*, WebCore::Event* event)
113 {
114     LOG(Media, "handleEvent %s", event->type().characters8());
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->mediaSession().currentPlaybackTargetIsWireless(*m_videoElement);
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     }
171 }
172
173 void WebVideoFullscreenModelVideoElement::setVideoFullscreenLayer(PlatformLayer* videoLayer)
174 {
175     if (m_videoFullscreenLayer == videoLayer)
176         return;
177     
178     m_videoFullscreenLayer = videoLayer;
179     [m_videoFullscreenLayer setAnchorPoint:CGPointMake(0.5, 0.5)];
180     [m_videoFullscreenLayer setBounds:m_videoFrame];
181     
182     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
183     WebThreadRun(^{
184         if (m_videoElement)
185             m_videoElement->setVideoFullscreenLayer(m_videoFullscreenLayer.get());
186         protect.clear();
187     });
188 }
189
190 void WebVideoFullscreenModelVideoElement::play()
191 {
192     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
193     WebThreadRun(^{
194         if (m_videoElement)
195             m_videoElement->play();
196         protect.clear();
197     });
198 }
199
200 void WebVideoFullscreenModelVideoElement::pause()
201 {
202     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
203     WebThreadRun(^{
204         if (m_videoElement)
205             m_videoElement->pause();
206         protect.clear();
207     });
208 }
209
210 void WebVideoFullscreenModelVideoElement::togglePlayState()
211 {
212     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
213     WebThreadRun(^{
214         if (m_videoElement)
215             m_videoElement->togglePlayState();
216         protect.clear();
217     });
218 }
219
220 void WebVideoFullscreenModelVideoElement::beginScrubbing()
221 {
222     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
223     WebThreadRun(^{
224         if (m_videoElement)
225             m_videoElement->beginScrubbing();
226         protect.clear();
227     });
228 }
229
230 void WebVideoFullscreenModelVideoElement::endScrubbing()
231 {
232     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
233     WebThreadRun(^{
234         if (m_videoElement)
235             m_videoElement->endScrubbing();
236         protect.clear();
237     });
238 }
239
240 void WebVideoFullscreenModelVideoElement::seekToTime(double time)
241 {
242     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
243     WebThreadRun(^{
244         if (m_videoElement)
245             m_videoElement->setCurrentTime(time);
246         protect.clear();
247     });
248 }
249
250 void WebVideoFullscreenModelVideoElement::fastSeek(double time)
251 {
252     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
253     WebThreadRun(^{
254         if (m_videoElement)
255             m_videoElement->fastSeek(time);
256         protect.clear();
257     });
258 }
259
260 void WebVideoFullscreenModelVideoElement::beginScanningForward()
261 {
262     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
263     WebThreadRun(^{
264         if (m_videoElement)
265             m_videoElement->beginScanning(MediaControllerInterface::Forward);
266         protect.clear();
267     });
268 }
269
270 void WebVideoFullscreenModelVideoElement::beginScanningBackward()
271 {
272     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
273     WebThreadRun(^{
274         if (m_videoElement)
275             m_videoElement->beginScanning(MediaControllerInterface::Backward);
276         protect.clear();
277     });
278 }
279
280 void WebVideoFullscreenModelVideoElement::endScanning()
281 {
282     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
283     WebThreadRun(^{
284         if (m_videoElement)
285             m_videoElement->endScanning();
286         protect.clear();
287     });
288 }
289
290 void WebVideoFullscreenModelVideoElement::requestExitFullscreen()
291 {
292     if (!m_videoElement)
293         return;
294
295     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
296     WebThreadRun(^{
297         if (m_videoElement && m_videoElement->isFullscreen())
298             m_videoElement->exitFullscreen();
299         protect.clear();
300     });
301 }
302
303 void WebVideoFullscreenModelVideoElement::setVideoLayerFrame(FloatRect rect)
304 {
305     m_videoFrame = rect;
306     [m_videoFullscreenLayer setBounds:CGRect(rect)];
307     m_videoElement->setVideoFullscreenFrame(rect);
308 }
309
310 FloatRect WebVideoFullscreenModelVideoElement::videoLayerFrame() const
311 {
312     return m_videoFrame;
313 }
314
315 void WebVideoFullscreenModelVideoElement::setVideoLayerGravity(WebVideoFullscreenModel::VideoGravity gravity)
316 {
317     MediaPlayer::VideoGravity videoGravity = MediaPlayer::VideoGravityResizeAspect;
318     if (gravity == WebVideoFullscreenModel::VideoGravityResize)
319         videoGravity = MediaPlayer::VideoGravityResize;
320     else if (gravity == WebVideoFullscreenModel::VideoGravityResizeAspect)
321         videoGravity = MediaPlayer::VideoGravityResizeAspect;
322     else if (gravity == WebVideoFullscreenModel::VideoGravityResizeAspectFill)
323         videoGravity = MediaPlayer::VideoGravityResizeAspectFill;
324     else
325         ASSERT_NOT_REACHED();
326     
327     m_videoElement->setVideoFullscreenGravity(videoGravity);
328 }
329
330 WebVideoFullscreenModel::VideoGravity WebVideoFullscreenModelVideoElement::videoLayerGravity() const
331 {
332     switch (m_videoElement->videoFullscreenGravity()) {
333     case MediaPlayer::VideoGravityResize:
334         return VideoGravityResize;
335     case MediaPlayer::VideoGravityResizeAspect:
336         return VideoGravityResizeAspect;
337     case MediaPlayer::VideoGravityResizeAspectFill:
338         return VideoGravityResizeAspectFill;
339     }
340
341     ASSERT_NOT_REACHED();
342     return VideoGravityResize;
343 }
344
345 void WebVideoFullscreenModelVideoElement::selectAudioMediaOption(uint64_t selectedAudioIndex)
346 {
347     AudioTrack* selectedAudioTrack = nullptr;
348
349     for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
350         auto& audioTrack = m_audioTracksForMenu[index];
351         audioTrack->setEnabled(index == static_cast<size_t>(selectedAudioIndex));
352         if (audioTrack->enabled())
353             selectedAudioTrack = audioTrack.get();
354     }
355
356     m_videoElement->audioTrackEnabledChanged(selectedAudioTrack);
357 }
358
359 void WebVideoFullscreenModelVideoElement::selectLegibleMediaOption(uint64_t index)
360 {
361     TextTrack* textTrack = nullptr;
362     
363     if (index < m_legibleTracksForMenu.size())
364         textTrack = m_legibleTracksForMenu[static_cast<size_t>(index)].get();
365     else
366         textTrack = TextTrack::captionMenuOffItem();
367     
368     m_videoElement->setSelectedTextTrack(textTrack);
369 }
370
371 void WebVideoFullscreenModelVideoElement::updateLegibleOptions()
372 {
373     AudioTrackList* audioTrackList = m_videoElement->audioTracks();
374     TextTrackList* trackList = m_videoElement->textTracks();
375
376     if ((!trackList && !audioTrackList) || !m_videoElement->document().page() || !m_videoElement->mediaControlsHost())
377         return;
378     
379     WTF::AtomicString displayMode = m_videoElement->mediaControlsHost()->captionDisplayMode();
380     TextTrack* offItem = m_videoElement->mediaControlsHost()->captionMenuOffItem();
381     TextTrack* automaticItem = m_videoElement->mediaControlsHost()->captionMenuAutomaticItem();
382     CaptionUserPreferences& captionPreferences = *m_videoElement->document().page()->group().captionPreferences();
383     m_legibleTracksForMenu = captionPreferences.sortedTrackListForMenu(trackList);
384
385     m_audioTracksForMenu = captionPreferences.sortedTrackListForMenu(audioTrackList);
386     
387     Vector<String> audioTrackDisplayNames;
388     uint64_t selectedAudioIndex = 0;
389     
390     for (size_t index = 0; index < m_audioTracksForMenu.size(); ++index) {
391         auto& track = m_audioTracksForMenu[index];
392         audioTrackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
393         
394         if (track->enabled())
395             selectedAudioIndex = index;
396     }
397     
398     m_videoFullscreenInterface->setAudioMediaSelectionOptions(audioTrackDisplayNames, selectedAudioIndex);
399
400     Vector<String> trackDisplayNames;
401     uint64_t selectedIndex = 0;
402     uint64_t offIndex = 0;
403     bool trackMenuItemSelected = false;
404     
405     for (size_t index = 0; index < m_legibleTracksForMenu.size(); index++) {
406         auto& track = m_legibleTracksForMenu[index];
407         trackDisplayNames.append(captionPreferences.displayNameForTrack(track.get()));
408         
409         if (track == offItem)
410             offIndex = index;
411         
412         if (track == automaticItem && displayMode == MediaControlsHost::automaticKeyword()) {
413             selectedIndex = index;
414             trackMenuItemSelected = true;
415         }
416         
417         if (displayMode != MediaControlsHost::automaticKeyword() && track->mode() == TextTrack::showingKeyword()) {
418             selectedIndex = index;
419             trackMenuItemSelected = true;
420         }
421     }
422     
423     if (offIndex && !trackMenuItemSelected && displayMode == MediaControlsHost::forcedOnlyKeyword()) {
424         selectedIndex = offIndex;
425         trackMenuItemSelected = true;
426     }
427     
428     m_videoFullscreenInterface->setLegibleMediaSelectionOptions(trackDisplayNames, selectedIndex);
429 }
430
431 const Vector<AtomicString>& WebVideoFullscreenModelVideoElement::observedEventNames()
432 {
433     static NeverDestroyed<Vector<AtomicString>> sEventNames;
434
435     if (!sEventNames.get().size()) {
436         sEventNames.get().append(eventNames().durationchangeEvent);
437         sEventNames.get().append(eventNames().pauseEvent);
438         sEventNames.get().append(eventNames().playEvent);
439         sEventNames.get().append(eventNames().ratechangeEvent);
440         sEventNames.get().append(eventNames().timeupdateEvent);
441         sEventNames.get().append(eventNames().addtrackEvent);
442         sEventNames.get().append(eventNames().removetrackEvent);
443         sEventNames.get().append(eventNames().webkitcurrentplaybacktargetiswirelesschangedEvent);
444     }
445     return sEventNames.get();
446 }
447
448 const AtomicString& WebVideoFullscreenModelVideoElement::eventNameAll()
449 {
450     static NeverDestroyed<AtomicString> sEventNameAll = "allEvents";
451     return sEventNameAll;
452 }
453
454 void WebVideoFullscreenModelVideoElement::fullscreenModeChanged(HTMLMediaElement::VideoFullscreenMode videoFullscreenMode)
455 {
456     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
457     WebThreadRun(^{
458         if (m_videoElement)
459             m_videoElement->fullscreenModeChanged(videoFullscreenMode);
460         protect.clear();
461     });
462 }
463
464 #endif