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