Clean up USE(WEB_THREAD)
[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 #if USE(WEB_THREAD)
225     ASSERT(isUIThread());
226     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
227 #endif
228         IntRect clientRect = elementRectInWindow(m_videoElement.get());
229         dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this, clientRect] {
230             m_interface->setInlineRect(clientRect, clientRect != IntRect(0, 0, 0, 0));
231         });
232 #if USE(WEB_THREAD)
233     });
234 #endif
235 #else
236     ASSERT_NOT_REACHED();
237 #endif
238 }
239
240 void VideoFullscreenControllerContext::requestVideoContentLayer()
241 {
242 #if PLATFORM(IOS_FAMILY)
243 #if USE(WEB_THREAD)
244     ASSERT(isUIThread());
245     WebThreadRun([protectedThis = makeRefPtr(this), this, videoFullscreenLayer = retainPtr([m_videoFullscreenView layer])] () mutable {
246 #endif
247         [videoFullscreenLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent)];
248         m_fullscreenModel->setVideoFullscreenLayer(videoFullscreenLayer.get(), [protectedThis = WTFMove(protectedThis), this] () mutable {
249             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
250                 m_interface->setHasVideoContentLayer(true);
251             });
252         });
253 #if USE(WEB_THREAD)
254     });
255 #endif
256 #else
257     ASSERT_NOT_REACHED();
258 #endif
259 }
260
261 void VideoFullscreenControllerContext::returnVideoContentLayer()
262 {
263 #if PLATFORM(IOS_FAMILY)
264 #if USE(WEB_THREAD)
265     ASSERT(isUIThread());
266     WebThreadRun([protectedThis = makeRefPtr(this), this, videoFullscreenLayer = retainPtr([m_videoFullscreenView layer])] () mutable {
267 #endif
268         [videoFullscreenLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent)];
269         m_fullscreenModel->setVideoFullscreenLayer(nil, [protectedThis = WTFMove(protectedThis), this] () mutable {
270             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
271                 m_interface->setHasVideoContentLayer(false);
272             });
273         });
274 #if USE(WEB_THREAD)
275     });
276 #endif
277 #else
278     ASSERT_NOT_REACHED();
279 #endif
280 }
281
282 void VideoFullscreenControllerContext::didSetupFullscreen()
283 {
284 #if USE(WEB_THREAD)
285     ASSERT(isUIThread());
286 #endif
287 #if PLATFORM(IOS_FAMILY)
288     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
289         m_interface->enterFullscreen();
290     });
291 #else
292 #if USE(WEB_THREAD)
293     WebThreadRun([protectedThis = makeRefPtr(this), this, videoFullscreenLayer = retainPtr([m_videoFullscreenView layer])] () mutable {
294 #endif
295         [videoFullscreenLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent)];
296         m_fullscreenModel->setVideoFullscreenLayer(videoFullscreenLayer.get(), [protectedThis = WTFMove(protectedThis), this] () mutable {
297             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
298                 m_interface->enterFullscreen();
299             });
300         });
301 #if USE(WEB_THREAD)
302     });
303 #endif
304 #endif
305 }
306
307 void VideoFullscreenControllerContext::willExitFullscreen()
308 {
309 #if PLATFORM(WATCHOS)
310 #if USE(WEB_THREAD)
311     ASSERT(isUIThread());
312     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
313 #endif
314         m_fullscreenModel->willExitFullscreen();
315 #if USE(WEB_THREAD)
316     });
317 #endif
318 #endif
319 }
320
321 void VideoFullscreenControllerContext::didExitFullscreen()
322 {
323     ASSERT(isUIThread());
324 #if PLATFORM(IOS_FAMILY)
325     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
326         m_interface->cleanupFullscreen();
327     });
328 #else
329 #if USE(WEB_THREAD)
330     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
331 #endif
332         m_fullscreenModel->setVideoFullscreenLayer(nil, [protectedThis = WTFMove(protectedThis), this] () mutable {
333             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this] {
334                 m_interface->cleanupFullscreen();
335             });
336         });
337 #if USE(WEB_THREAD)
338     });
339 #endif
340 #endif
341 }
342
343 void VideoFullscreenControllerContext::didCleanupFullscreen()
344 {
345 #if USE(WEB_THREAD)
346     ASSERT(isUIThread());
347 #endif
348     m_interface->setVideoFullscreenModel(nullptr);
349     m_interface->setVideoFullscreenChangeObserver(nullptr);
350     m_interface = nullptr;
351     m_videoFullscreenView = nil;
352
353 #if USE(WEB_THREAD)
354     WebThreadRun([protectedThis = makeRefPtr(this), this] {
355 #endif
356         m_fullscreenModel->setVideoFullscreenLayer(nil);
357         m_fullscreenModel->setVideoElement(nullptr);
358         m_playbackModel->setMediaElement(nullptr);
359         m_playbackModel->removeClient(*this);
360         m_fullscreenModel->removeClient(*this);
361         m_fullscreenModel = nullptr;
362         m_videoElement = nullptr;
363
364         [m_controller didFinishFullscreen:this];
365 #if USE(WEB_THREAD)
366     });
367 #endif
368 }
369
370 void VideoFullscreenControllerContext::fullscreenMayReturnToInline()
371 {
372 #if USE(WEB_THREAD)
373     ASSERT(isUIThread());
374     WebThreadRun([protectedThis = makeRefPtr(this), this] () mutable {
375 #endif
376         IntRect clientRect = elementRectInWindow(m_videoElement.get());
377         dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this, clientRect] {
378             m_interface->preparedToReturnToInline(true, clientRect);
379         });
380 #if USE(WEB_THREAD)
381     });
382 #endif
383 }
384
385 #pragma mark PlaybackSessionModelClient
386
387 void VideoFullscreenControllerContext::durationChanged(double duration)
388 {
389     if (WebThreadIsCurrent()) {
390         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), duration] {
391             protectedThis->durationChanged(duration);
392         });
393         return;
394     }
395
396     for (auto& client : m_playbackClients)
397         client->durationChanged(duration);
398 }
399
400 void VideoFullscreenControllerContext::currentTimeChanged(double currentTime, double anchorTime)
401 {
402     if (WebThreadIsCurrent()) {
403         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), currentTime, anchorTime] {
404             protectedThis->currentTimeChanged(currentTime, anchorTime);
405         });
406         return;
407     }
408
409     for (auto& client : m_playbackClients)
410         client->currentTimeChanged(currentTime, anchorTime);
411 }
412
413 void VideoFullscreenControllerContext::bufferedTimeChanged(double bufferedTime)
414 {
415     if (WebThreadIsCurrent()) {
416         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), bufferedTime] {
417             protectedThis->bufferedTimeChanged(bufferedTime);
418         });
419         return;
420     }
421
422     for (auto& client : m_playbackClients)
423         client->bufferedTimeChanged(bufferedTime);
424 }
425
426 void VideoFullscreenControllerContext::rateChanged(bool isPlaying, float playbackRate)
427 {
428     if (WebThreadIsCurrent()) {
429         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), isPlaying, playbackRate] {
430             protectedThis->rateChanged(isPlaying, playbackRate);
431         });
432         return;
433     }
434
435     for (auto& client : m_playbackClients)
436         client->rateChanged(isPlaying, playbackRate);
437 }
438
439 void VideoFullscreenControllerContext::hasVideoChanged(bool hasVideo)
440 {
441     if (WebThreadIsCurrent()) {
442         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), hasVideo] {
443             protectedThis->hasVideoChanged(hasVideo);
444         });
445         return;
446     }
447
448     for (auto& client : m_fullscreenClients)
449         client->hasVideoChanged(hasVideo);
450 }
451
452 void VideoFullscreenControllerContext::videoDimensionsChanged(const FloatSize& videoDimensions)
453 {
454     if (WebThreadIsCurrent()) {
455         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), videoDimensions = videoDimensions] {
456             protectedThis->videoDimensionsChanged(videoDimensions);
457         });
458         return;
459     }
460
461     for (auto& client : m_fullscreenClients)
462         client->videoDimensionsChanged(videoDimensions);
463 }
464
465 void VideoFullscreenControllerContext::seekableRangesChanged(const TimeRanges& timeRanges, double lastModifiedTime, double liveUpdateInterval)
466 {
467     if (WebThreadIsCurrent()) {
468         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), platformTimeRanges = timeRanges.ranges(), lastModifiedTime, liveUpdateInterval] {
469             protectedThis->seekableRangesChanged(TimeRanges::create(platformTimeRanges), lastModifiedTime, liveUpdateInterval);
470         });
471         return;
472     }
473
474     for (auto &client : m_playbackClients)
475         client->seekableRangesChanged(timeRanges, lastModifiedTime, liveUpdateInterval);
476 }
477
478 void VideoFullscreenControllerContext::canPlayFastReverseChanged(bool canPlayFastReverse)
479 {
480     if (WebThreadIsCurrent()) {
481         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), canPlayFastReverse] {
482             protectedThis->canPlayFastReverseChanged(canPlayFastReverse);
483         });
484         return;
485     }
486
487     for (auto &client : m_playbackClients)
488         client->canPlayFastReverseChanged(canPlayFastReverse);
489 }
490
491 static Vector<MediaSelectionOption> isolatedCopy(const Vector<MediaSelectionOption>& options)
492 {
493     Vector<MediaSelectionOption> optionsCopy;
494     optionsCopy.reserveInitialCapacity(options.size());
495     for (auto& option : options)
496         optionsCopy.uncheckedAppend({ option.displayName.isolatedCopy(), option.type });
497     return optionsCopy;
498 }
499
500 void VideoFullscreenControllerContext::audioMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
501 {
502     if (WebThreadIsCurrent()) {
503         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), options = isolatedCopy(options), selectedIndex] {
504             protectedThis->audioMediaSelectionOptionsChanged(options, selectedIndex);
505         });
506         return;
507     }
508
509     for (auto& client : m_playbackClients)
510         client->audioMediaSelectionOptionsChanged(options, selectedIndex);
511 }
512
513 void VideoFullscreenControllerContext::legibleMediaSelectionOptionsChanged(const Vector<MediaSelectionOption>& options, uint64_t selectedIndex)
514 {
515     if (WebThreadIsCurrent()) {
516         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), options = isolatedCopy(options), selectedIndex] {
517             protectedThis->legibleMediaSelectionOptionsChanged(options, selectedIndex);
518         });
519         return;
520     }
521
522     for (auto& client : m_playbackClients)
523         client->legibleMediaSelectionOptionsChanged(options, selectedIndex);
524 }
525
526 void VideoFullscreenControllerContext::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType type, const String& localizedDeviceName)
527 {
528     if (WebThreadIsCurrent()) {
529         callOnMainThread([protectedThis = makeRef(*this), this, enabled, type, localizedDeviceName = localizedDeviceName.isolatedCopy()] {
530             for (auto& client : m_playbackClients)
531                 client->externalPlaybackChanged(enabled, type, localizedDeviceName);
532         });
533         return;
534     }
535
536     for (auto& client : m_playbackClients)
537         client->externalPlaybackChanged(enabled, type, localizedDeviceName);
538 }
539
540 void VideoFullscreenControllerContext::wirelessVideoPlaybackDisabledChanged(bool disabled)
541 {
542     if (WebThreadIsCurrent()) {
543         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), disabled] {
544             protectedThis->wirelessVideoPlaybackDisabledChanged(disabled);
545         });
546         return;
547     }
548
549     for (auto& client : m_playbackClients)
550         client->wirelessVideoPlaybackDisabledChanged(disabled);
551 }
552
553 void VideoFullscreenControllerContext::mutedChanged(bool muted)
554 {
555     if (WebThreadIsCurrent()) {
556         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), muted] {
557             protectedThis->mutedChanged(muted);
558         });
559         return;
560     }
561
562     for (auto& client : m_playbackClients)
563         client->mutedChanged(muted);
564 }
565
566 void VideoFullscreenControllerContext::volumeChanged(double volume)
567 {
568     if (WebThreadIsCurrent()) {
569         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), volume] {
570             protectedThis->volumeChanged(volume);
571         });
572         return;
573     }
574
575     for (auto& client : m_playbackClients)
576         client->volumeChanged(volume);
577 }
578 #pragma mark VideoFullscreenModel
579
580 void VideoFullscreenControllerContext::addClient(VideoFullscreenModelClient& client)
581 {
582     ASSERT(!m_fullscreenClients.contains(&client));
583     m_fullscreenClients.add(&client);
584 }
585
586 void VideoFullscreenControllerContext::removeClient(VideoFullscreenModelClient& client)
587 {
588     ASSERT(m_fullscreenClients.contains(&client));
589     m_fullscreenClients.remove(&client);
590 }
591
592 void VideoFullscreenControllerContext::requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode mode, bool finishedWithMedia)
593 {
594 #if USE(WEB_THREAD)
595     ASSERT(isUIThread());
596     WebThreadRun([protectedThis = makeRefPtr(this), this, mode, finishedWithMedia] {
597 #endif
598         if (m_fullscreenModel)
599             m_fullscreenModel->requestFullscreenMode(mode, finishedWithMedia);
600 #if USE(WEB_THREAD)
601     });
602 #endif
603 }
604
605 void VideoFullscreenControllerContext::setVideoLayerFrame(FloatRect frame)
606 {
607 #if USE(WEB_THREAD)
608     ASSERT(isUIThread());
609 #endif
610     RetainPtr<CALayer> videoFullscreenLayer = [m_videoFullscreenView layer];
611     [videoFullscreenLayer setSublayerTransform:[videoFullscreenLayer transform]];
612
613     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this, frame, videoFullscreenLayer = WTFMove(videoFullscreenLayer)] () mutable {
614 #if USE(WEB_THREAD)
615         WebThreadRun([protectedThis = WTFMove(protectedThis), this, frame, videoFullscreenLayer = WTFMove(videoFullscreenLayer)] {
616 #endif
617             [CATransaction begin];
618             [CATransaction setDisableActions:YES];
619             [CATransaction setAnimationDuration:0];
620             
621             [videoFullscreenLayer setSublayerTransform:CATransform3DIdentity];
622             
623             if (m_fullscreenModel)
624                 m_fullscreenModel->setVideoLayerFrame(frame);
625             [CATransaction commit];
626 #if USE(WEB_THREAD)
627         });
628 #endif
629     });
630 }
631
632 void VideoFullscreenControllerContext::setVideoLayerGravity(MediaPlayerEnums::VideoGravity videoGravity)
633 {
634 #if USE(WEB_THREAD)
635     ASSERT(isUIThread());
636     WebThreadRun([protectedThis = makeRefPtr(this), this, videoGravity] {
637 #endif
638         if (m_fullscreenModel)
639             m_fullscreenModel->setVideoLayerGravity(videoGravity);
640 #if USE(WEB_THREAD)
641     });
642 #endif
643 }
644
645 void VideoFullscreenControllerContext::fullscreenModeChanged(HTMLMediaElementEnums::VideoFullscreenMode mode)
646 {
647 #if USE(WEB_THREAD)
648     ASSERT(isUIThread());
649     WebThreadRun([protectedThis = makeRefPtr(this), this, mode] {
650 #endif
651         if (m_fullscreenModel)
652             m_fullscreenModel->fullscreenModeChanged(mode);
653 #if USE(WEB_THREAD)
654     });
655 #endif
656 }
657
658 bool VideoFullscreenControllerContext::hasVideo() const
659 {
660     ASSERT(isUIThread());
661     return m_fullscreenModel ? m_fullscreenModel->hasVideo() : false;
662 }
663
664 bool VideoFullscreenControllerContext::isMuted() const
665 {
666     ASSERT(isUIThread());
667     return m_playbackModel ? m_playbackModel->isMuted() : false;
668 }
669
670 double VideoFullscreenControllerContext::volume() const
671 {
672     ASSERT(isUIThread());
673     return m_playbackModel ? m_playbackModel->volume() : 0;
674 }
675
676 bool VideoFullscreenControllerContext::isPictureInPictureActive() const
677 {
678     ASSERT(isUIThread());
679     return m_playbackModel ? m_playbackModel->isPictureInPictureActive() : false;
680 }
681
682 bool VideoFullscreenControllerContext::isPictureInPictureSupported() const
683 {
684     ASSERT(isUIThread());
685     return m_playbackModel ? m_playbackModel->isPictureInPictureSupported() : false;
686 }
687
688 void VideoFullscreenControllerContext::willEnterPictureInPicture()
689 {
690     ASSERT(isUIThread());
691     for (auto* client : m_fullscreenClients)
692         client->willEnterPictureInPicture();
693 }
694
695 void VideoFullscreenControllerContext::didEnterPictureInPicture()
696 {
697     ASSERT(isUIThread());
698     for (auto* client : m_fullscreenClients)
699         client->didEnterPictureInPicture();
700 }
701
702 void VideoFullscreenControllerContext::failedToEnterPictureInPicture()
703 {
704     ASSERT(isUIThread());
705     for (auto* client : m_fullscreenClients)
706         client->failedToEnterPictureInPicture();
707 }
708
709 void VideoFullscreenControllerContext::willExitPictureInPicture()
710 {
711     ASSERT(isUIThread());
712     for (auto* client : m_fullscreenClients)
713         client->willExitPictureInPicture();
714 }
715
716 void VideoFullscreenControllerContext::didExitPictureInPicture()
717 {
718     ASSERT(isUIThread());
719     for (auto* client : m_fullscreenClients)
720         client->didExitPictureInPicture();
721 }
722
723 FloatSize VideoFullscreenControllerContext::videoDimensions() const
724 {
725     ASSERT(isUIThread());
726     return m_fullscreenModel ? m_fullscreenModel->videoDimensions() : FloatSize();
727 }
728
729 #pragma mark - PlaybackSessionModel
730
731 void VideoFullscreenControllerContext::addClient(PlaybackSessionModelClient& client)
732 {
733     ASSERT(!m_playbackClients.contains(&client));
734     m_playbackClients.add(&client);
735 }
736
737 void VideoFullscreenControllerContext::removeClient(PlaybackSessionModelClient& client)
738 {
739     ASSERT(m_playbackClients.contains(&client));
740     m_playbackClients.remove(&client);
741 }
742
743 void VideoFullscreenControllerContext::play()
744 {
745 #if USE(WEB_THREAD)
746     ASSERT(isUIThread());
747     WebThreadRun([protectedThis = makeRefPtr(this), this] {
748 #endif
749         if (m_playbackModel)
750             m_playbackModel->play();
751 #if USE(WEB_THREAD)
752     });
753 #endif
754 }
755
756 void VideoFullscreenControllerContext::pause()
757 {
758 #if USE(WEB_THREAD)
759     ASSERT(isUIThread());
760     WebThreadRun([protectedThis = makeRefPtr(this), this] {
761 #endif
762         if (m_playbackModel)
763             m_playbackModel->pause();
764 #if USE(WEB_THREAD)
765     });
766 #endif
767 }
768
769 void VideoFullscreenControllerContext::togglePlayState()
770 {
771 #if USE(WEB_THREAD)
772     ASSERT(isUIThread());
773     WebThreadRun([protectedThis = makeRefPtr(this), this] {
774 #endif
775         if (m_playbackModel)
776             m_playbackModel->togglePlayState();
777 #if USE(WEB_THREAD)
778     });
779 #endif
780 }
781
782 void VideoFullscreenControllerContext::toggleMuted()
783 {
784 #if USE(WEB_THREAD)
785     ASSERT(isUIThread());
786     WebThreadRun([protectedThis = makeRefPtr(this), this] {
787 #endif
788         if (m_playbackModel)
789             m_playbackModel->toggleMuted();
790 #if USE(WEB_THREAD)
791     });
792 #endif
793 }
794
795 void VideoFullscreenControllerContext::setMuted(bool muted)
796 {
797 #if USE(WEB_THREAD)
798     ASSERT(isUIThread());
799     WebThreadRun([protectedThis = makeRefPtr(this), this, muted] {
800 #endif
801         if (m_playbackModel)
802             m_playbackModel->setMuted(muted);
803 #if USE(WEB_THREAD)
804     });
805 #endif
806 }
807
808 void VideoFullscreenControllerContext::setVolume(double volume)
809 {
810 #if USE(WEB_THREAD)
811     ASSERT(isUIThread());
812     WebThreadRun([protectedThis = makeRefPtr(this), this, volume] {
813 #endif
814         if (m_playbackModel)
815             m_playbackModel->setVolume(volume);
816 #if USE(WEB_THREAD)
817     });
818 #endif
819 }
820
821 void VideoFullscreenControllerContext::setPlayingOnSecondScreen(bool value)
822 {
823 #if USE(WEB_THREAD)
824     ASSERT(isUIThread());
825     WebThreadRun([protectedThis = makeRefPtr(this), this, value] {
826 #endif
827         if (m_playbackModel)
828             m_playbackModel->setPlayingOnSecondScreen(value);
829 #if USE(WEB_THREAD)
830     });
831 #endif
832 }
833
834 void VideoFullscreenControllerContext::beginScrubbing()
835 {
836 #if USE(WEB_THREAD)
837     ASSERT(isUIThread());
838     WebThreadRun([protectedThis = makeRefPtr(this), this] {
839 #endif
840         if (m_playbackModel)
841             m_playbackModel->beginScrubbing();
842 #if USE(WEB_THREAD)
843     });
844 #endif
845 }
846
847 void VideoFullscreenControllerContext::endScrubbing()
848 {
849 #if USE(WEB_THREAD)
850     ASSERT(isUIThread());
851     WebThreadRun([protectedThis = makeRefPtr(this), this] {
852 #endif
853         if (m_playbackModel)
854             m_playbackModel->endScrubbing();
855 #if USE(WEB_THREAD)
856     });
857 #endif
858 }
859
860 void VideoFullscreenControllerContext::seekToTime(double time, double toleranceBefore, double toleranceAfter)
861 {
862 #if USE(WEB_THREAD)
863     ASSERT(isUIThread());
864     WebThreadRun([protectedThis = makeRefPtr(this), this, time, toleranceBefore, toleranceAfter] {
865 #endif
866         if (m_playbackModel)
867             m_playbackModel->seekToTime(time, toleranceBefore, toleranceAfter);
868 #if USE(WEB_THREAD)
869     });
870 #endif
871 }
872
873 void VideoFullscreenControllerContext::fastSeek(double time)
874 {
875 #if USE(WEB_THREAD)
876     ASSERT(isUIThread());
877     WebThreadRun([protectedThis = makeRefPtr(this), this, time] {
878 #endif
879         if (m_playbackModel)
880             m_playbackModel->fastSeek(time);
881 #if USE(WEB_THREAD)
882     });
883 #endif
884 }
885
886 void VideoFullscreenControllerContext::beginScanningForward()
887 {
888 #if USE(WEB_THREAD)
889     ASSERT(isUIThread());
890     WebThreadRun([protectedThis = makeRefPtr(this), this] {
891 #endif
892         if (m_playbackModel)
893             m_playbackModel->beginScanningForward();
894 #if USE(WEB_THREAD)
895     });
896 #endif
897 }
898
899 void VideoFullscreenControllerContext::beginScanningBackward()
900 {
901 #if USE(WEB_THREAD)
902     ASSERT(isUIThread());
903     WebThreadRun([protectedThis = makeRefPtr(this), this] {
904 #endif
905         if (m_playbackModel)
906             m_playbackModel->beginScanningBackward();
907 #if USE(WEB_THREAD)
908     });
909 #endif
910 }
911
912 void VideoFullscreenControllerContext::endScanning()
913 {
914 #if USE(WEB_THREAD)
915     ASSERT(isUIThread());
916     WebThreadRun([protectedThis = makeRefPtr(this), this] {
917 #endif
918         if (m_playbackModel)
919             m_playbackModel->endScanning();
920 #if USE(WEB_THREAD)
921     });
922 #endif
923 }
924
925 void VideoFullscreenControllerContext::selectAudioMediaOption(uint64_t index)
926 {
927 #if USE(WEB_THREAD)
928     ASSERT(isUIThread());
929     WebThreadRun([protectedThis = makeRefPtr(this), this, index] {
930 #endif
931         if (m_playbackModel)
932             m_playbackModel->selectAudioMediaOption(index);
933 #if USE(WEB_THREAD)
934     });
935 #endif
936 }
937
938 void VideoFullscreenControllerContext::selectLegibleMediaOption(uint64_t index)
939 {
940 #if USE(WEB_THREAD)
941     ASSERT(isUIThread());
942     WebThreadRun([protectedThis = makeRefPtr(this), this, index] {
943 #endif
944         if (m_playbackModel)
945             m_playbackModel->selectLegibleMediaOption(index);
946 #if USE(WEB_THREAD)
947     });
948 #endif
949 }
950
951 double VideoFullscreenControllerContext::duration() const
952 {
953 #if USE(WEB_THREAD)
954     ASSERT(isUIThread());
955 #endif
956     return m_playbackModel ? m_playbackModel->duration() : 0;
957 }
958
959 double VideoFullscreenControllerContext::currentTime() const
960 {
961 #if USE(WEB_THREAD)
962     ASSERT(isUIThread());
963 #endif
964     return m_playbackModel ? m_playbackModel->currentTime() : 0;
965 }
966
967 double VideoFullscreenControllerContext::bufferedTime() const
968 {
969 #if USE(WEB_THREAD)
970     ASSERT(isUIThread());
971 #endif
972     return m_playbackModel ? m_playbackModel->bufferedTime() : 0;
973 }
974
975 bool VideoFullscreenControllerContext::isPlaying() const
976 {
977 #if USE(WEB_THREAD)
978     ASSERT(isUIThread());
979 #endif
980     return m_playbackModel ? m_playbackModel->isPlaying() : false;
981 }
982
983 float VideoFullscreenControllerContext::playbackRate() const
984 {
985 #if USE(WEB_THREAD)
986     ASSERT(isUIThread());
987 #endif
988     return m_playbackModel ? m_playbackModel->playbackRate() : 0;
989 }
990
991 Ref<TimeRanges> VideoFullscreenControllerContext::seekableRanges() const
992 {
993 #if USE(WEB_THREAD)
994     ASSERT(isUIThread());
995 #endif
996     return m_playbackModel ? m_playbackModel->seekableRanges() : TimeRanges::create();
997 }
998
999 double VideoFullscreenControllerContext::seekableTimeRangesLastModifiedTime() const
1000 {
1001 #if USE(WEB_THREAD)
1002     ASSERT(isUIThread());
1003 #endif
1004     return m_playbackModel ? m_playbackModel->seekableTimeRangesLastModifiedTime() : 0;
1005 }
1006
1007 double VideoFullscreenControllerContext::liveUpdateInterval() const
1008 {
1009 #if USE(WEB_THREAD)
1010     ASSERT(isUIThread());
1011 #endif
1012     return m_playbackModel ? m_playbackModel->liveUpdateInterval() : 0;
1013 }
1014
1015 bool VideoFullscreenControllerContext::canPlayFastReverse() const
1016 {
1017 #if USE(WEB_THREAD)
1018     ASSERT(isUIThread());
1019 #endif
1020     return m_playbackModel ? m_playbackModel->canPlayFastReverse() : false;
1021 }
1022
1023 Vector<MediaSelectionOption> VideoFullscreenControllerContext::audioMediaSelectionOptions() const
1024 {
1025 #if USE(WEB_THREAD)
1026     ASSERT(isUIThread());
1027 #endif
1028     if (m_playbackModel)
1029         return m_playbackModel->audioMediaSelectionOptions();
1030     return { };
1031 }
1032
1033 uint64_t VideoFullscreenControllerContext::audioMediaSelectedIndex() const
1034 {
1035 #if USE(WEB_THREAD)
1036     ASSERT(isUIThread());
1037 #endif
1038     return m_playbackModel ? m_playbackModel->audioMediaSelectedIndex() : -1;
1039 }
1040
1041 Vector<MediaSelectionOption> VideoFullscreenControllerContext::legibleMediaSelectionOptions() const
1042 {
1043 #if USE(WEB_THREAD)
1044     ASSERT(isUIThread());
1045 #endif
1046     if (m_playbackModel)
1047         return m_playbackModel->legibleMediaSelectionOptions();
1048     return { };
1049 }
1050
1051 uint64_t VideoFullscreenControllerContext::legibleMediaSelectedIndex() const
1052 {
1053 #if USE(WEB_THREAD)
1054     ASSERT(isUIThread());
1055 #endif
1056     return m_playbackModel ? m_playbackModel->legibleMediaSelectedIndex() : -1;
1057 }
1058
1059 bool VideoFullscreenControllerContext::externalPlaybackEnabled() const
1060 {
1061 #if USE(WEB_THREAD)
1062     ASSERT(isUIThread());
1063 #endif
1064     return m_playbackModel ? m_playbackModel->externalPlaybackEnabled() : false;
1065 }
1066
1067 PlaybackSessionModel::ExternalPlaybackTargetType VideoFullscreenControllerContext::externalPlaybackTargetType() const
1068 {
1069 #if USE(WEB_THREAD)
1070     ASSERT(isUIThread());
1071 #endif
1072     return m_playbackModel ? m_playbackModel->externalPlaybackTargetType() : TargetTypeNone;
1073 }
1074
1075 String VideoFullscreenControllerContext::externalPlaybackLocalizedDeviceName() const
1076 {
1077 #if USE(WEB_THREAD)
1078     ASSERT(isUIThread());
1079 #endif
1080     return m_playbackModel ? m_playbackModel->externalPlaybackLocalizedDeviceName() : String();
1081 }
1082
1083 bool VideoFullscreenControllerContext::wirelessVideoPlaybackDisabled() const
1084 {
1085 #if USE(WEB_THREAD)
1086     ASSERT(isUIThread());
1087 #endif
1088     return m_playbackModel ? m_playbackModel->wirelessVideoPlaybackDisabled() : true;
1089 }
1090
1091 #pragma mark Other
1092
1093 void VideoFullscreenControllerContext::setUpFullscreen(HTMLVideoElement& videoElement, UIView *view, HTMLMediaElementEnums::VideoFullscreenMode mode)
1094 {
1095 #if USE(WEB_THREAD)
1096     ASSERT(isMainThread());
1097 #endif
1098     RetainPtr<UIView> viewRef = view;
1099     m_videoElement = &videoElement;
1100     m_playbackModel = PlaybackSessionModelMediaElement::create();
1101     m_playbackModel->addClient(*this);
1102     m_playbackModel->setMediaElement(m_videoElement.get());
1103
1104     m_fullscreenModel = VideoFullscreenModelVideoElement::create();
1105     m_fullscreenModel->addClient(*this);
1106     m_fullscreenModel->setVideoElement(m_videoElement.get());
1107
1108     bool allowsPictureInPicture = m_videoElement->webkitSupportsPresentationMode(HTMLVideoElement::VideoPresentationMode::PictureInPicture);
1109
1110     IntRect videoElementClientRect = elementRectInWindow(m_videoElement.get());
1111     FloatRect videoLayerFrame = FloatRect(FloatPoint(), videoElementClientRect.size());
1112     m_fullscreenModel->setVideoLayerFrame(videoLayerFrame);
1113
1114     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this, videoElementClientRect, viewRef, mode, allowsPictureInPicture] {
1115 #if USE(WEB_THREAD)
1116         ASSERT(isUIThread());
1117 #endif
1118
1119         Ref<PlaybackSessionInterfaceAVKit> sessionInterface = PlaybackSessionInterfaceAVKit::create(*this);
1120         m_interface = VideoFullscreenInterfaceAVKit::create(sessionInterface.get());
1121         m_interface->setVideoFullscreenChangeObserver(this);
1122         m_interface->setVideoFullscreenModel(this);
1123         m_interface->setVideoFullscreenChangeObserver(this);
1124
1125         m_videoFullscreenView = adoptNS([PAL::allocUIViewInstance() init]);
1126
1127         m_interface->setupFullscreen(*m_videoFullscreenView.get(), videoElementClientRect, viewRef.get(), mode, allowsPictureInPicture, false);
1128     });
1129 }
1130
1131 void VideoFullscreenControllerContext::exitFullscreen()
1132 {
1133 #if USE(WEB_THREAD)
1134     ASSERT(WebThreadIsCurrent() || isMainThread());
1135 #endif
1136     IntRect clientRect = elementRectInWindow(m_videoElement.get());
1137     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this, clientRect] {
1138 #if USE(WEB_THREAD)
1139         ASSERT(isUIThread());
1140 #endif
1141         m_interface->exitFullscreen(clientRect);
1142     });
1143 }
1144
1145 void VideoFullscreenControllerContext::requestHideAndExitFullscreen()
1146 {
1147 #if USE(WEB_THREAD)
1148     ASSERT(isUIThread());
1149 #endif
1150     m_interface->requestHideAndExitFullscreen();
1151 }
1152
1153 @implementation WebVideoFullscreenController {
1154     RefPtr<VideoFullscreenControllerContext> _context;
1155     RefPtr<HTMLVideoElement> _videoElement;
1156 }
1157
1158 - (instancetype)init
1159 {
1160     if (!(self = [super init]))
1161         return nil;
1162     
1163     return self;
1164 }
1165
1166 - (void)setVideoElement:(HTMLVideoElement*)videoElement
1167 {
1168     _videoElement = videoElement;
1169 }
1170
1171 - (HTMLVideoElement*)videoElement
1172 {
1173     return _videoElement.get();
1174 }
1175
1176 - (void)enterFullscreen:(UIView *)view mode:(HTMLMediaElementEnums::VideoFullscreenMode)mode
1177 {
1178 #if USE(WEB_THREAD)
1179     ASSERT(isMainThread());
1180 #endif
1181     _context = VideoFullscreenControllerContext::create();
1182     _context->setController(self);
1183     _context->setUpFullscreen(*_videoElement.get(), view, mode);
1184 }
1185
1186 - (void)exitFullscreen
1187 {
1188 #if USE(WEB_THREAD)
1189     ASSERT(WebThreadIsCurrent() || isMainThread());
1190 #endif
1191     _context->exitFullscreen();
1192 }
1193
1194 - (void)requestHideAndExitFullscreen
1195 {
1196 #if USE(WEB_THREAD)
1197     ASSERT(isUIThread());
1198 #endif
1199     if (_context)
1200         _context->requestHideAndExitFullscreen();
1201 }
1202
1203 - (void)didFinishFullscreen:(VideoFullscreenControllerContext*)context
1204 {
1205     ASSERT(WebThreadIsCurrent());
1206     ASSERT_UNUSED(context, context == _context);
1207     [[self retain] autorelease]; // retain self before breaking a retain cycle.
1208     _context->setController(nil);
1209     _context = nullptr;
1210     _videoElement = nullptr;
1211 }
1212
1213 @end
1214
1215 #endif // !HAVE(AVKIT)
1216
1217 #endif // PLATFORM(IOS_FAMILY)