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