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