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