Enable picture-in-picture from inline element on suspend.
[WebKit-https.git] / Source / WebCore / platform / ios / WebVideoFullscreenControllerAVKit.mm
1 /*
2  * Copyright (C) 2013 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
30 #import "WebVideoFullscreenControllerAVKit.h"
31
32 #import "Logging.h"
33 #import "MediaSelectionOption.h"
34 #import "PlaybackSessionInterfaceAVKit.h"
35 #import "PlaybackSessionModelMediaElement.h"
36 #import "TimeRanges.h"
37 #import "VideoFullscreenChangeObserver.h"
38 #import "VideoFullscreenInterfaceAVKit.h"
39 #import "VideoFullscreenModelVideoElement.h"
40 #import <QuartzCore/CoreAnimation.h>
41 #import <UIKit/UIView.h>
42 #import <WebCore/FrameView.h>
43 #import <WebCore/HTMLVideoElement.h>
44 #import <WebCore/RenderVideo.h>
45 #import <WebCore/WebCoreThreadRun.h>
46 #import <pal/spi/cocoa/QuartzCoreSPI.h>
47 #import <wtf/SoftLinking.h>
48
49 SOFT_LINK_FRAMEWORK(UIKit)
50 SOFT_LINK_CLASS(UIKit, UIView)
51
52 using namespace WebCore;
53
54 #if !HAVE(AVKIT)
55
56 @implementation WebVideoFullscreenController
57 - (void)setVideoElement:(WebCore::HTMLVideoElement*)videoElement
58 {
59     UNUSED_PARAM(videoElement);
60 }
61
62 - (WebCore::HTMLVideoElement*)videoElement
63 {
64     return nullptr;
65 }
66
67 - (void)enterFullscreen:(UIView *)view mode:(WebCore::HTMLMediaElementEnums::VideoFullscreenMode)mode
68 {
69     UNUSED_PARAM(view);
70     UNUSED_PARAM(mode);
71 }
72
73 - (void)requestHideAndExitFullscreen
74 {
75 }
76
77 - (void)exitFullscreen
78 {
79 }
80 @end
81
82 #else
83
84 static IntRect elementRectInWindow(HTMLVideoElement* videoElement)
85 {
86     if (!videoElement)
87         return { };
88     auto* renderer = videoElement->renderer();
89     auto* view = videoElement->document().view();
90     if (!renderer || !view)
91         return { };
92     return view->convertToContainingWindow(renderer->absoluteBoundingBoxRect());
93 }
94
95 class VideoFullscreenControllerContext;
96
97 @interface WebVideoFullscreenController (delegate)
98 -(void)didFinishFullscreen:(VideoFullscreenControllerContext*)context;
99 @end
100
101 class VideoFullscreenControllerContext final
102     : private VideoFullscreenModel
103     , private VideoFullscreenModelClient
104     , private VideoFullscreenChangeObserver
105     , private PlaybackSessionModel
106     , private PlaybackSessionModelClient
107     , public ThreadSafeRefCounted<VideoFullscreenControllerContext> {
108
109 public:
110     static Ref<VideoFullscreenControllerContext> create()
111     {
112         return adoptRef(*new VideoFullscreenControllerContext);
113     }
114
115     void setController(WebVideoFullscreenController* controller) { m_controller = controller; }
116     void setUpFullscreen(HTMLVideoElement&, UIView *, HTMLMediaElementEnums::VideoFullscreenMode);
117     void exitFullscreen();
118     void requestHideAndExitFullscreen();
119     void invalidate();
120
121 private:
122     VideoFullscreenControllerContext() { }
123
124     // VideoFullscreenChangeObserver
125     void requestUpdateInlineRect() override;
126     void requestVideoContentLayer() override;
127     void returnVideoContentLayer() override;
128     void didSetupFullscreen() override;
129     void didEnterFullscreen() override { }
130     void didExitFullscreen() override;
131     void didCleanupFullscreen() override;
132     void fullscreenMayReturnToInline() override;
133
134     // VideoFullscreenModelClient
135     void hasVideoChanged(bool) override;
136     void videoDimensionsChanged(const FloatSize&) override;
137
138     // PlaybackSessionModel
139     void addClient(PlaybackSessionModelClient&) override;
140     void removeClient(PlaybackSessionModelClient&) override;
141     void play() override;
142     void pause() override;
143     void togglePlayState() override;
144     void beginScrubbing() override;
145     void endScrubbing() override;
146     void seekToTime(double, double, double) override;
147     void fastSeek(double time) override;
148     void beginScanningForward() override;
149     void beginScanningBackward() override;
150     void endScanning() override;
151     void selectAudioMediaOption(uint64_t) override;
152     void selectLegibleMediaOption(uint64_t) override;
153     double duration() const override;
154     double playbackStartedTime() const override { return 0; }
155     double currentTime() const override;
156     double bufferedTime() const override;
157     bool isPlaying() const override;
158     bool isScrubbing() const override { return false; }
159     float playbackRate() const override;
160     Ref<TimeRanges> seekableRanges() const override;
161     double seekableTimeRangesLastModifiedTime() const override;
162     double liveUpdateInterval() const override;
163     bool canPlayFastReverse() const override;
164     Vector<MediaSelectionOption> audioMediaSelectionOptions() const override;
165     uint64_t audioMediaSelectedIndex() const override;
166     Vector<MediaSelectionOption> legibleMediaSelectionOptions() const override;
167     uint64_t legibleMediaSelectedIndex() const override;
168     bool externalPlaybackEnabled() const override;
169     ExternalPlaybackTargetType externalPlaybackTargetType() const override;
170     String externalPlaybackLocalizedDeviceName() const override;
171     bool wirelessVideoPlaybackDisabled() const override;
172     void togglePictureInPicture() override { }
173     void toggleMuted() override;
174     void setMuted(bool) final;
175
176     // PlaybackSessionModelClient
177     void durationChanged(double) override;
178     void currentTimeChanged(double currentTime, double anchorTime) override;
179     void bufferedTimeChanged(double) override;
180     void rateChanged(bool isPlaying, float playbackRate) override;
181     void seekableRangesChanged(const TimeRanges&, double lastModifiedTime, double liveUpdateInterval) override;
182     void canPlayFastReverseChanged(bool) override;
183     void audioMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex) override;
184     void legibleMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex) override;
185     void externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType, const String& localizedDeviceName) override;
186     void wirelessVideoPlaybackDisabledChanged(bool) override;
187     void mutedChanged(bool) override;
188
189     // VideoFullscreenModel
190     void addClient(VideoFullscreenModelClient&) override;
191     void removeClient(VideoFullscreenModelClient&) override;
192     void requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode, bool finishedWithMedia = false) override;
193     void setVideoLayerFrame(FloatRect) override;
194     void setVideoLayerGravity(VideoFullscreenModel::VideoGravity) override;
195     void fullscreenModeChanged(HTMLMediaElementEnums::VideoFullscreenMode) override;
196     bool isVisible() const override;
197     bool hasVideo() const override;
198     FloatSize videoDimensions() const override;
199     bool isMuted() const override;
200
201     HashSet<PlaybackSessionModelClient*> m_playbackClients;
202     HashSet<VideoFullscreenModelClient*> m_fullscreenClients;
203     RefPtr<VideoFullscreenInterfaceAVKit> m_interface;
204     RefPtr<VideoFullscreenModelVideoElement> m_fullscreenModel;
205     RefPtr<PlaybackSessionModelMediaElement> m_playbackModel;
206     RefPtr<HTMLVideoElement> m_videoElement;
207     RetainPtr<UIView> m_videoFullscreenView;
208     RetainPtr<WebVideoFullscreenController> m_controller;
209 };
210
211 #pragma mark VideoFullscreenChangeObserver
212
213 void VideoFullscreenControllerContext::requestUpdateInlineRect()
214 {
215 #if PLATFORM(IOS) && ENABLE(FULLSCREEN_API)
216     ASSERT(isUIThread());
217     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
218         IntRect clientRect = elementRectInWindow(m_videoElement.get());
219         dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this, clientRect] {
220             m_interface->setInlineRect(clientRect, clientRect != IntRect(0, 0, 0, 0));
221         });
222     });
223 #else
224     ASSERT_NOT_REACHED();
225 #endif
226 }
227
228 void VideoFullscreenControllerContext::requestVideoContentLayer()
229 {
230 #if PLATFORM(IOS) && ENABLE(FULLSCREEN_API)
231     ASSERT(isUIThread());
232     WebThreadRun([protectedThis = makeRefPtr(this), this, videoFullscreenLayer = retainPtr([m_videoFullscreenView layer])] () mutable {
233         [videoFullscreenLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent)];
234         m_fullscreenModel->setVideoFullscreenLayer(videoFullscreenLayer.get(), [protectedThis = WTFMove(protectedThis), this] () mutable {
235             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
236                 m_interface->setHasVideoContentLayer(true);
237             });
238         });
239     });
240 #else
241     ASSERT_NOT_REACHED();
242 #endif
243 }
244
245 void VideoFullscreenControllerContext::returnVideoContentLayer()
246 {
247 #if PLATFORM(IOS) && ENABLE(FULLSCREEN_API)
248     ASSERT(isUIThread());
249     WebThreadRun([protectedThis = makeRefPtr(this), this, videoFullscreenLayer = retainPtr([m_videoFullscreenView layer])] () mutable {
250         [videoFullscreenLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent)];
251         m_fullscreenModel->setVideoFullscreenLayer(nil, [protectedThis = WTFMove(protectedThis), this] () mutable {
252             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
253                 m_interface->setHasVideoContentLayer(false);
254             });
255         });
256     });
257 #else
258     ASSERT_NOT_REACHED();
259 #endif
260 }
261
262 void VideoFullscreenControllerContext::didSetupFullscreen()
263 {
264     ASSERT(isUIThread());
265 #if PLATFORM(IOS) && ENABLE(FULLSCREEN_API)
266     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
267         m_interface->enterFullscreen();
268     });
269 #else
270     WebThreadRun([protectedThis = makeRefPtr(this), this, videoFullscreenLayer = retainPtr([m_videoFullscreenView layer])] () mutable {
271         [videoFullscreenLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent)];
272         m_fullscreenModel->setVideoFullscreenLayer(videoFullscreenLayer.get(), [protectedThis = WTFMove(protectedThis), this] () mutable {
273             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
274                 m_interface->enterFullscreen();
275             });
276         });
277     });
278 #endif
279 }
280
281 void VideoFullscreenControllerContext::didExitFullscreen()
282 {
283     ASSERT(isUIThread());
284 #if PLATFORM(IOS) && ENABLE(FULLSCREEN_API)
285     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
286         m_interface->cleanupFullscreen();
287     });
288 #else
289     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
290         m_fullscreenModel->setVideoFullscreenLayer(nil, [protectedThis = WTFMove(protectedThis), this] () mutable {
291             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
292                 m_interface->cleanupFullscreen();
293             });
294         });
295     });
296 #endif
297 }
298
299 void VideoFullscreenControllerContext::didCleanupFullscreen()
300 {
301     ASSERT(isUIThread());
302     m_interface->setVideoFullscreenModel(nullptr);
303     m_interface->setVideoFullscreenChangeObserver(nullptr);
304     m_interface = nullptr;
305     m_videoFullscreenView = nil;
306
307     WebThreadRun([protectedThis = makeRefPtr(this), this] {
308         m_fullscreenModel->setVideoFullscreenLayer(nil);
309         m_fullscreenModel->setVideoElement(nullptr);
310         m_playbackModel->setMediaElement(nullptr);
311         m_fullscreenModel->removeClient(*this);
312         m_fullscreenModel = nullptr;
313         m_videoElement = nullptr;
314
315         [m_controller didFinishFullscreen:this];
316     });
317 }
318
319 void VideoFullscreenControllerContext::fullscreenMayReturnToInline()
320 {
321     ASSERT(isUIThread());
322     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
323         IntRect clientRect = elementRectInWindow(m_videoElement.get());
324         dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this, clientRect] {
325             m_interface->preparedToReturnToInline(true, clientRect);
326         });
327     });
328 }
329
330 #pragma mark PlaybackSessionModelClient
331
332 void VideoFullscreenControllerContext::durationChanged(double duration)
333 {
334     if (WebThreadIsCurrent()) {
335         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), duration] {
336             protectedThis->durationChanged(duration);
337         });
338         return;
339     }
340
341     for (auto& client : m_playbackClients)
342         client->durationChanged(duration);
343 }
344
345 void VideoFullscreenControllerContext::currentTimeChanged(double currentTime, double anchorTime)
346 {
347     if (WebThreadIsCurrent()) {
348         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), currentTime, anchorTime] {
349             protectedThis->currentTimeChanged(currentTime, anchorTime);
350         });
351         return;
352     }
353
354     for (auto& client : m_playbackClients)
355         client->currentTimeChanged(currentTime, anchorTime);
356 }
357
358 void VideoFullscreenControllerContext::bufferedTimeChanged(double bufferedTime)
359 {
360     if (WebThreadIsCurrent()) {
361         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), bufferedTime] {
362             protectedThis->bufferedTimeChanged(bufferedTime);
363         });
364         return;
365     }
366
367     for (auto& client : m_playbackClients)
368         client->bufferedTimeChanged(bufferedTime);
369 }
370
371 void VideoFullscreenControllerContext::rateChanged(bool isPlaying, float playbackRate)
372 {
373     if (WebThreadIsCurrent()) {
374         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), isPlaying, playbackRate] {
375             protectedThis->rateChanged(isPlaying, playbackRate);
376         });
377         return;
378     }
379
380     for (auto& client : m_playbackClients)
381         client->rateChanged(isPlaying, playbackRate);
382 }
383
384 void VideoFullscreenControllerContext::hasVideoChanged(bool hasVideo)
385 {
386     if (WebThreadIsCurrent()) {
387         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), hasVideo] {
388             protectedThis->hasVideoChanged(hasVideo);
389         });
390         return;
391     }
392
393     for (auto& client : m_fullscreenClients)
394         client->hasVideoChanged(hasVideo);
395 }
396
397 void VideoFullscreenControllerContext::videoDimensionsChanged(const FloatSize& videoDimensions)
398 {
399     if (WebThreadIsCurrent()) {
400         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), videoDimensions = videoDimensions] {
401             protectedThis->videoDimensionsChanged(videoDimensions);
402         });
403         return;
404     }
405
406     for (auto& client : m_fullscreenClients)
407         client->videoDimensionsChanged(videoDimensions);
408 }
409
410 void VideoFullscreenControllerContext::seekableRangesChanged(const TimeRanges& timeRanges, double lastModifiedTime, double liveUpdateInterval)
411 {
412     if (WebThreadIsCurrent()) {
413         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), platformTimeRanges = timeRanges.ranges(), lastModifiedTime, liveUpdateInterval] {
414             protectedThis->seekableRangesChanged(TimeRanges::create(platformTimeRanges), lastModifiedTime, liveUpdateInterval);
415         });
416         return;
417     }
418
419     for (auto &client : m_playbackClients)
420         client->seekableRangesChanged(timeRanges, lastModifiedTime, liveUpdateInterval);
421 }
422
423 void VideoFullscreenControllerContext::canPlayFastReverseChanged(bool canPlayFastReverse)
424 {
425     if (WebThreadIsCurrent()) {
426         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), canPlayFastReverse] {
427             protectedThis->canPlayFastReverseChanged(canPlayFastReverse);
428         });
429         return;
430     }
431
432     for (auto &client : m_playbackClients)
433         client->canPlayFastReverseChanged(canPlayFastReverse);
434 }
435
436 static Vector<MediaSelectionOption> isolatedCopy(const Vector<MediaSelectionOption>& options)
437 {
438     Vector<MediaSelectionOption> optionsCopy;
439     optionsCopy.reserveInitialCapacity(options.size());
440     for (auto& option : options)
441         optionsCopy.uncheckedAppend({ option.displayName.isolatedCopy(), option.type });
442     return optionsCopy;
443 }
444
445 void VideoFullscreenControllerContext::audioMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
446 {
447     if (WebThreadIsCurrent()) {
448         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), options = isolatedCopy(options), selectedIndex] {
449             protectedThis->audioMediaSelectionOptionsChanged(options, selectedIndex);
450         });
451         return;
452     }
453
454     for (auto& client : m_playbackClients)
455         client->audioMediaSelectionOptionsChanged(options, selectedIndex);
456 }
457
458 void VideoFullscreenControllerContext::legibleMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
459 {
460     if (WebThreadIsCurrent()) {
461         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), options = isolatedCopy(options), selectedIndex] {
462             protectedThis->legibleMediaSelectionOptionsChanged(options, selectedIndex);
463         });
464         return;
465     }
466
467     for (auto& client : m_playbackClients)
468         client->legibleMediaSelectionOptionsChanged(options, selectedIndex);
469 }
470
471 void VideoFullscreenControllerContext::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType type, const String& localizedDeviceName)
472 {
473     if (WebThreadIsCurrent()) {
474         callOnMainThread([protectedThis = makeRef(*this), this, enabled, type, localizedDeviceName = localizedDeviceName.isolatedCopy()] {
475             for (auto& client : m_playbackClients)
476                 client->externalPlaybackChanged(enabled, type, localizedDeviceName);
477         });
478         return;
479     }
480
481     for (auto& client : m_playbackClients)
482         client->externalPlaybackChanged(enabled, type, localizedDeviceName);
483 }
484
485 void VideoFullscreenControllerContext::wirelessVideoPlaybackDisabledChanged(bool disabled)
486 {
487     if (WebThreadIsCurrent()) {
488         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), disabled] {
489             protectedThis->wirelessVideoPlaybackDisabledChanged(disabled);
490         });
491         return;
492     }
493
494     for (auto& client : m_playbackClients)
495         client->wirelessVideoPlaybackDisabledChanged(disabled);
496 }
497
498 void VideoFullscreenControllerContext::mutedChanged(bool muted)
499 {
500     if (WebThreadIsCurrent()) {
501         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), muted] {
502             protectedThis->mutedChanged(muted);
503         });
504         return;
505     }
506
507     for (auto& client : m_playbackClients)
508         client->mutedChanged(muted);
509 }
510
511 #pragma mark VideoFullscreenModel
512
513 void VideoFullscreenControllerContext::addClient(VideoFullscreenModelClient& client)
514 {
515     ASSERT(!m_fullscreenClients.contains(&client));
516     m_fullscreenClients.add(&client);
517 }
518
519 void VideoFullscreenControllerContext::removeClient(VideoFullscreenModelClient& client)
520 {
521     ASSERT(m_fullscreenClients.contains(&client));
522     m_fullscreenClients.remove(&client);
523 }
524
525 void VideoFullscreenControllerContext::requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode mode, bool finishedWithMedia)
526 {
527     ASSERT(isUIThread());
528     WebThreadRun([protectedThis = makeRefPtr(this), this, mode, finishedWithMedia] {
529         if (m_fullscreenModel)
530             m_fullscreenModel->requestFullscreenMode(mode, finishedWithMedia);
531     });
532 }
533
534 void VideoFullscreenControllerContext::setVideoLayerFrame(FloatRect frame)
535 {
536     ASSERT(isUIThread());
537     RetainPtr<CALayer> videoFullscreenLayer = [m_videoFullscreenView layer];
538     [videoFullscreenLayer setSublayerTransform:[videoFullscreenLayer transform]];
539
540     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this, frame, videoFullscreenLayer = WTFMove(videoFullscreenLayer)] () mutable {
541         WebThreadRun([protectedThis = WTFMove(protectedThis), this, frame, videoFullscreenLayer = WTFMove(videoFullscreenLayer)] {
542             [CATransaction begin];
543             [CATransaction setDisableActions:YES];
544             [CATransaction setAnimationDuration:0];
545             
546             [videoFullscreenLayer setSublayerTransform:CATransform3DIdentity];
547             
548             if (m_fullscreenModel)
549                 m_fullscreenModel->setVideoLayerFrame(frame);
550             [CATransaction commit];
551         });
552     });
553 }
554
555 void VideoFullscreenControllerContext::setVideoLayerGravity(VideoFullscreenModel::VideoGravity videoGravity)
556 {
557     ASSERT(isUIThread());
558     WebThreadRun([protectedThis = makeRefPtr(this), this, videoGravity] {
559         if (m_fullscreenModel)
560             m_fullscreenModel->setVideoLayerGravity(videoGravity);
561     });
562 }
563
564 void VideoFullscreenControllerContext::fullscreenModeChanged(HTMLMediaElementEnums::VideoFullscreenMode mode)
565 {
566     ASSERT(isUIThread());
567     WebThreadRun([protectedThis = makeRefPtr(this), this, mode] {
568         if (m_fullscreenModel)
569             m_fullscreenModel->fullscreenModeChanged(mode);
570     });
571 }
572
573 bool VideoFullscreenControllerContext::isVisible() const
574 {
575     ASSERT(isUIThread());
576     return m_fullscreenModel ? m_fullscreenModel->isVisible() : false;
577 }
578
579 bool VideoFullscreenControllerContext::hasVideo() const
580 {
581     ASSERT(isUIThread());
582     return m_fullscreenModel ? m_fullscreenModel->hasVideo() : false;
583 }
584
585 bool VideoFullscreenControllerContext::isMuted() const
586 {
587     ASSERT(isUIThread());
588     return m_playbackModel ? m_playbackModel->isMuted() : false;
589 }
590
591 FloatSize VideoFullscreenControllerContext::videoDimensions() const
592 {
593     ASSERT(isUIThread());
594     return m_fullscreenModel ? m_fullscreenModel->videoDimensions() : FloatSize();
595 }
596
597 #pragma mark - PlaybackSessionModel
598
599 void VideoFullscreenControllerContext::addClient(PlaybackSessionModelClient& client)
600 {
601     ASSERT(!m_playbackClients.contains(&client));
602     m_playbackClients.add(&client);
603 }
604
605 void VideoFullscreenControllerContext::removeClient(PlaybackSessionModelClient& client)
606 {
607     ASSERT(m_playbackClients.contains(&client));
608     m_playbackClients.remove(&client);
609 }
610
611 void VideoFullscreenControllerContext::play()
612 {
613     ASSERT(isUIThread());
614     WebThreadRun([protectedThis = makeRefPtr(this), this] {
615         if (m_playbackModel)
616             m_playbackModel->play();
617     });
618 }
619
620 void VideoFullscreenControllerContext::pause()
621 {
622     ASSERT(isUIThread());
623     WebThreadRun([protectedThis = makeRefPtr(this), this] {
624         if (m_playbackModel)
625             m_playbackModel->pause();
626     });
627 }
628
629 void VideoFullscreenControllerContext::togglePlayState()
630 {
631     ASSERT(isUIThread());
632     WebThreadRun([protectedThis = makeRefPtr(this), this] {
633         if (m_playbackModel)
634             m_playbackModel->togglePlayState();
635     });
636 }
637
638 void VideoFullscreenControllerContext::toggleMuted()
639 {
640     ASSERT(isUIThread());
641     WebThreadRun([protectedThis = makeRefPtr(this), this] {
642         if (m_playbackModel)
643             m_playbackModel->toggleMuted();
644     });
645 }
646
647 void VideoFullscreenControllerContext::setMuted(bool muted)
648 {
649     ASSERT(isUIThread());
650     WebThreadRun([protectedThis = makeRefPtr(this), this, muted] {
651         if (m_playbackModel)
652             m_playbackModel->setMuted(muted);
653     });
654 }
655
656 void VideoFullscreenControllerContext::beginScrubbing()
657 {
658     ASSERT(isUIThread());
659     WebThreadRun([protectedThis = makeRefPtr(this), this] {
660         if (m_playbackModel)
661             m_playbackModel->beginScrubbing();
662     });
663 }
664
665 void VideoFullscreenControllerContext::endScrubbing()
666 {
667     ASSERT(isUIThread());
668     WebThreadRun([protectedThis = makeRefPtr(this), this] {
669         if (m_playbackModel)
670             m_playbackModel->endScrubbing();
671     });
672 }
673
674 void VideoFullscreenControllerContext::seekToTime(double time, double toleranceBefore, double toleranceAfter)
675 {
676     ASSERT(isUIThread());
677     WebThreadRun([protectedThis = makeRefPtr(this), this, time, toleranceBefore, toleranceAfter] {
678         if (m_playbackModel)
679             m_playbackModel->seekToTime(time, toleranceBefore, toleranceAfter);
680     });
681 }
682
683 void VideoFullscreenControllerContext::fastSeek(double time)
684 {
685     ASSERT(isUIThread());
686     WebThreadRun([protectedThis = makeRefPtr(this), this, time] {
687         if (m_playbackModel)
688             m_playbackModel->fastSeek(time);
689     });
690 }
691
692 void VideoFullscreenControllerContext::beginScanningForward()
693 {
694     ASSERT(isUIThread());
695     WebThreadRun([protectedThis = makeRefPtr(this), this] {
696         if (m_playbackModel)
697             m_playbackModel->beginScanningForward();
698     });
699 }
700
701 void VideoFullscreenControllerContext::beginScanningBackward()
702 {
703     ASSERT(isUIThread());
704     WebThreadRun([protectedThis = makeRefPtr(this), this] {
705         if (m_playbackModel)
706             m_playbackModel->beginScanningBackward();
707     });
708 }
709
710 void VideoFullscreenControllerContext::endScanning()
711 {
712     ASSERT(isUIThread());
713     WebThreadRun([protectedThis = makeRefPtr(this), this] {
714         if (m_playbackModel)
715             m_playbackModel->endScanning();
716     });
717 }
718
719 void VideoFullscreenControllerContext::selectAudioMediaOption(uint64_t index)
720 {
721     ASSERT(isUIThread());
722     WebThreadRun([protectedThis = makeRefPtr(this), this, index] {
723         if (m_playbackModel)
724             m_playbackModel->selectAudioMediaOption(index);
725     });
726 }
727
728 void VideoFullscreenControllerContext::selectLegibleMediaOption(uint64_t index)
729 {
730     ASSERT(isUIThread());
731     WebThreadRun([protectedThis = makeRefPtr(this), this, index] {
732         if (m_playbackModel)
733             m_playbackModel->selectLegibleMediaOption(index);
734     });
735 }
736
737 double VideoFullscreenControllerContext::duration() const
738 {
739     ASSERT(isUIThread());
740     return m_playbackModel ? m_playbackModel->duration() : 0;
741 }
742
743 double VideoFullscreenControllerContext::currentTime() const
744 {
745     ASSERT(isUIThread());
746     return m_playbackModel ? m_playbackModel->currentTime() : 0;
747 }
748
749 double VideoFullscreenControllerContext::bufferedTime() const
750 {
751     ASSERT(isUIThread());
752     return m_playbackModel ? m_playbackModel->bufferedTime() : 0;
753 }
754
755 bool VideoFullscreenControllerContext::isPlaying() const
756 {
757     ASSERT(isUIThread());
758     return m_playbackModel ? m_playbackModel->isPlaying() : false;
759 }
760
761 float VideoFullscreenControllerContext::playbackRate() const
762 {
763     ASSERT(isUIThread());
764     return m_playbackModel ? m_playbackModel->playbackRate() : 0;
765 }
766
767 Ref<TimeRanges> VideoFullscreenControllerContext::seekableRanges() const
768 {
769     ASSERT(isUIThread());
770     return m_playbackModel ? m_playbackModel->seekableRanges() : TimeRanges::create();
771 }
772
773 double VideoFullscreenControllerContext::seekableTimeRangesLastModifiedTime() const
774 {
775     ASSERT(isUIThread());
776     return m_playbackModel ? m_playbackModel->seekableTimeRangesLastModifiedTime() : 0;
777 }
778
779 double VideoFullscreenControllerContext::liveUpdateInterval() const
780 {
781     ASSERT(isUIThread());
782     return m_playbackModel ? m_playbackModel->liveUpdateInterval() : 0;
783 }
784
785 bool VideoFullscreenControllerContext::canPlayFastReverse() const
786 {
787     ASSERT(isUIThread());
788     return m_playbackModel ? m_playbackModel->canPlayFastReverse() : false;
789 }
790
791 Vector<MediaSelectionOption> VideoFullscreenControllerContext::audioMediaSelectionOptions() const
792 {
793     ASSERT(isUIThread());
794     if (m_playbackModel)
795         return m_playbackModel->audioMediaSelectionOptions();
796     return { };
797 }
798
799 uint64_t VideoFullscreenControllerContext::audioMediaSelectedIndex() const
800 {
801     ASSERT(isUIThread());
802     return m_playbackModel ? m_playbackModel->audioMediaSelectedIndex() : -1;
803 }
804
805 Vector<MediaSelectionOption> VideoFullscreenControllerContext::legibleMediaSelectionOptions() const
806 {
807     ASSERT(isUIThread());
808     if (m_playbackModel)
809         return m_playbackModel->legibleMediaSelectionOptions();
810     return { };
811 }
812
813 uint64_t VideoFullscreenControllerContext::legibleMediaSelectedIndex() const
814 {
815     ASSERT(isUIThread());
816     return m_playbackModel ? m_playbackModel->legibleMediaSelectedIndex() : -1;
817 }
818
819 bool VideoFullscreenControllerContext::externalPlaybackEnabled() const
820 {
821     ASSERT(isUIThread());
822     return m_playbackModel ? m_playbackModel->externalPlaybackEnabled() : false;
823 }
824
825 PlaybackSessionModel::ExternalPlaybackTargetType VideoFullscreenControllerContext::externalPlaybackTargetType() const
826 {
827     ASSERT(isUIThread());
828     return m_playbackModel ? m_playbackModel->externalPlaybackTargetType() : TargetTypeNone;
829 }
830
831 String VideoFullscreenControllerContext::externalPlaybackLocalizedDeviceName() const
832 {
833     ASSERT(isUIThread());
834     return m_playbackModel ? m_playbackModel->externalPlaybackLocalizedDeviceName() : String();
835 }
836
837 bool VideoFullscreenControllerContext::wirelessVideoPlaybackDisabled() const
838 {
839     ASSERT(isUIThread());
840     return m_playbackModel ? m_playbackModel->wirelessVideoPlaybackDisabled() : true;
841 }
842
843 #pragma mark Other
844
845 void VideoFullscreenControllerContext::setUpFullscreen(HTMLVideoElement& videoElement, UIView *view, HTMLMediaElementEnums::VideoFullscreenMode mode)
846 {
847     ASSERT(isMainThread());
848     RetainPtr<UIView> viewRef = view;
849     m_videoElement = &videoElement;
850     m_playbackModel = PlaybackSessionModelMediaElement::create();
851     m_playbackModel->addClient(*this);
852     m_playbackModel->setMediaElement(m_videoElement.get());
853
854     m_fullscreenModel = VideoFullscreenModelVideoElement::create();
855     m_fullscreenModel->addClient(*this);
856     m_fullscreenModel->setVideoElement(m_videoElement.get());
857
858     bool allowsPictureInPicture = m_videoElement->webkitSupportsPresentationMode(HTMLVideoElement::VideoPresentationMode::PictureInPicture);
859
860     IntRect videoElementClientRect = elementRectInWindow(m_videoElement.get());
861     FloatRect videoLayerFrame = FloatRect(FloatPoint(), videoElementClientRect.size());
862     m_fullscreenModel->setVideoLayerFrame(videoLayerFrame);
863
864     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this, videoElementClientRect, viewRef, mode, allowsPictureInPicture] {
865         ASSERT(isUIThread());
866
867         Ref<PlaybackSessionInterfaceAVKit> sessionInterface = PlaybackSessionInterfaceAVKit::create(*this);
868         m_interface = VideoFullscreenInterfaceAVKit::create(sessionInterface.get());
869         m_interface->setVideoFullscreenChangeObserver(this);
870         m_interface->setVideoFullscreenModel(this);
871         m_interface->setVideoFullscreenChangeObserver(this);
872
873         m_videoFullscreenView = adoptNS([allocUIViewInstance() init]);
874         
875         m_interface->setupFullscreen(*m_videoFullscreenView.get(), videoElementClientRect, viewRef.get(), mode, allowsPictureInPicture, false);
876     });
877 }
878
879 void VideoFullscreenControllerContext::exitFullscreen()
880 {
881     ASSERT(WebThreadIsCurrent() || isMainThread());
882     IntRect clientRect = elementRectInWindow(m_videoElement.get());
883     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this, clientRect] {
884         ASSERT(isUIThread());
885         m_interface->exitFullscreen(clientRect);
886     });
887 }
888
889 void VideoFullscreenControllerContext::requestHideAndExitFullscreen()
890 {
891     ASSERT(isUIThread());
892     m_interface->requestHideAndExitFullscreen();
893 }
894
895 @implementation WebVideoFullscreenController {
896     RefPtr<VideoFullscreenControllerContext> _context;
897     RefPtr<HTMLVideoElement> _videoElement;
898 }
899
900 - (instancetype)init
901 {
902     if (!(self = [super init]))
903         return nil;
904     
905     return self;
906 }
907
908 - (void)setVideoElement:(HTMLVideoElement*)videoElement
909 {
910     _videoElement = videoElement;
911 }
912
913 - (HTMLVideoElement*)videoElement
914 {
915     return _videoElement.get();
916 }
917
918 - (void)enterFullscreen:(UIView *)view mode:(HTMLMediaElementEnums::VideoFullscreenMode)mode
919 {
920     ASSERT(isMainThread());
921     _context = VideoFullscreenControllerContext::create();
922     _context->setController(self);
923     _context->setUpFullscreen(*_videoElement.get(), view, mode);
924 }
925
926 - (void)exitFullscreen
927 {
928     ASSERT(WebThreadIsCurrent() || isMainThread());
929     _context->exitFullscreen();
930 }
931
932 - (void)requestHideAndExitFullscreen
933 {
934     ASSERT(isUIThread());
935     if (_context)
936         _context->requestHideAndExitFullscreen();
937 }
938
939 - (void)didFinishFullscreen:(VideoFullscreenControllerContext*)context
940 {
941     ASSERT(WebThreadIsCurrent());
942     ASSERT_UNUSED(context, context == _context);
943     [[self retain] autorelease]; // retain self before breaking a retain cycle.
944     _context->setController(nil);
945     _context = nullptr;
946     _videoElement = nullptr;
947 }
948
949 @end
950
951 #endif // !HAVE(AVKIT)
952
953 #endif // PLATFORM(IOS)