[iOS] When simultaneously exiting-and-entering fullscreen, WebVideoFullscreenManager...
[WebKit-https.git] / Source / WebCore / platform / ios / WebVideoFullscreenInterfaceAVKit.mm
1 /*
2  * Copyright (C) 2014, 2015 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
27 #import "config.h"
28
29 #if PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED > 80200 && HAVE(AVKIT)
30
31 #import "WebVideoFullscreenInterfaceAVKit.h"
32
33 #import "AVKitSPI.h"
34 #import "GeometryUtilities.h"
35 #import "Logging.h"
36 #import "RuntimeApplicationChecksIOS.h"
37 #import "TimeRanges.h"
38 #import "WebCoreSystemInterface.h"
39 #import "WebCoreThreadRun.h"
40 #import "WebVideoFullscreenModel.h"
41 #import <AVFoundation/AVTime.h>
42 #import <UIKit/UIKit.h>
43 #import <wtf/RetainPtr.h>
44 #import <wtf/text/CString.h>
45 #import <wtf/text/WTFString.h>
46
47 using namespace WebCore;
48
49 // Soft-linking headers must be included last since they #define functions, constants, etc.
50 #import "CoreMediaSoftLink.h"
51
52 SOFT_LINK_FRAMEWORK(AVFoundation)
53 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
54
55 SOFT_LINK_FRAMEWORK(AVKit)
56 SOFT_LINK_CLASS(AVKit, AVPlayerController)
57 SOFT_LINK_CLASS(AVKit, AVPlayerViewController)
58 SOFT_LINK_CLASS(AVKit, AVValueTiming)
59
60 SOFT_LINK_FRAMEWORK(UIKit)
61 SOFT_LINK_CLASS(UIKit, UIApplication)
62 SOFT_LINK_CLASS(UIKit, UIScreen)
63 SOFT_LINK_CLASS(UIKit, UIWindow)
64 SOFT_LINK_CLASS(UIKit, UIView)
65 SOFT_LINK_CLASS(UIKit, UIViewController)
66 SOFT_LINK_CLASS(UIKit, UIColor)
67
68 #if !LOG_DISABLED
69 static const char* boolString(bool val)
70 {
71     return val ? "true" : "false";
72 }
73 #endif
74
75
76 @class WebAVMediaSelectionOption;
77
78 @interface WebAVPlayerController : NSObject <AVPlayerViewControllerDelegate>
79 {
80     WebAVMediaSelectionOption *_currentAudioMediaSelectionOption;
81     WebAVMediaSelectionOption *_currentLegibleMediaSelectionOption;
82 }
83
84 -(void)resetState;
85
86 @property (retain) AVPlayerController* playerControllerProxy;
87 @property (assign) WebVideoFullscreenModel* delegate;
88 @property (assign) WebVideoFullscreenInterfaceAVKit* fullscreenInterface;
89
90 @property (readonly) BOOL canScanForward;
91 @property BOOL canScanBackward;
92 @property (readonly) BOOL canSeekToBeginning;
93 @property (readonly) BOOL canSeekToEnd;
94
95 @property BOOL canPlay;
96 @property (getter=isPlaying) BOOL playing;
97 @property BOOL canPause;
98 @property BOOL canTogglePlayback;
99 @property double rate;
100 @property BOOL canSeek;
101 @property NSTimeInterval contentDuration;
102 @property NSSize contentDimensions;
103 @property BOOL hasEnabledAudio;
104 @property BOOL hasEnabledVideo;
105 @property NSTimeInterval minTime;
106 @property NSTimeInterval maxTime;
107 @property NSTimeInterval contentDurationWithinEndTimes;
108 @property (retain) NSArray *loadedTimeRanges;
109 @property AVPlayerControllerStatus status;
110 @property (retain) AVValueTiming *timing;
111 @property (retain) NSArray *seekableTimeRanges;
112
113 @property (readonly) BOOL hasMediaSelectionOptions;
114 @property (readonly) BOOL hasAudioMediaSelectionOptions;
115 @property (retain) NSArray *audioMediaSelectionOptions;
116 @property (retain) WebAVMediaSelectionOption *currentAudioMediaSelectionOption;
117 @property (readonly) BOOL hasLegibleMediaSelectionOptions;
118 @property (retain) NSArray *legibleMediaSelectionOptions;
119 @property (retain) WebAVMediaSelectionOption *currentLegibleMediaSelectionOption;
120
121 @property (readonly, getter=isPlayingOnExternalScreen) BOOL playingOnExternalScreen;
122 @property (getter=isExternalPlaybackActive) BOOL externalPlaybackActive;
123 @property AVPlayerControllerExternalPlaybackType externalPlaybackType;
124 @property (retain) NSString *externalPlaybackAirPlayDeviceLocalizedName;
125
126 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason;
127 @end
128
129 @implementation WebAVPlayerController
130
131 - (instancetype)init
132 {
133     if (!(self = [super init]))
134         return self;
135     
136     initAVPlayerController();
137     self.playerControllerProxy = [[allocAVPlayerControllerInstance() init] autorelease];
138     return self;
139 }
140
141 - (void)dealloc
142 {
143     [_playerControllerProxy release];
144     [_loadedTimeRanges release];
145     [_seekableTimeRanges release];
146     [_timing release];
147     [_audioMediaSelectionOptions release];
148     [_legibleMediaSelectionOptions release];
149     [_currentAudioMediaSelectionOption release];
150     [_currentLegibleMediaSelectionOption release];
151     [super dealloc];
152 }
153
154 -(void)resetState {
155     self.contentDuration = 0;
156     self.maxTime = 0;
157     self.contentDurationWithinEndTimes = 0;
158     self.loadedTimeRanges = @[];
159     
160     self.canPlay = NO;
161     self.canPause = NO;
162     self.canTogglePlayback = NO;
163     self.hasEnabledAudio = NO;
164     self.canSeek = NO;
165     self.minTime = 0;
166     self.status = AVPlayerControllerStatusUnknown;
167     
168     self.timing = nil;
169     self.rate = 0;
170     
171     self.hasEnabledVideo = NO;
172     self.contentDimensions = CGSizeMake(0, 0);
173     
174     self.seekableTimeRanges = [NSMutableArray array];
175     
176     self.canScanBackward = NO;
177     
178     self.audioMediaSelectionOptions = nil;
179     self.currentAudioMediaSelectionOption = nil;
180     
181     self.legibleMediaSelectionOptions = nil;
182     self.currentLegibleMediaSelectionOption = nil;
183 }
184
185 - (AVPlayer*) player {
186     return nil;
187 }
188
189 - (id)forwardingTargetForSelector:(SEL)selector
190 {
191     UNUSED_PARAM(selector);
192     return self.playerControllerProxy;
193 }
194
195 - (void)playerViewControllerWillStartOptimizedFullscreen:(AVPlayerViewController *)playerViewController
196 {
197     UNUSED_PARAM(playerViewController);
198     self.fullscreenInterface->willStartOptimizedFullscreen();
199 }
200
201 - (void)playerViewControllerDidStartOptimizedFullscreen:(AVPlayerViewController *)playerViewController
202 {
203     UNUSED_PARAM(playerViewController);
204     self.fullscreenInterface->didStartOptimizedFullscreen();
205 }
206
207 - (void)playerViewControllerWillStopOptimizedFullscreen:(AVPlayerViewController *)playerViewController
208 {
209     UNUSED_PARAM(playerViewController);
210     self.fullscreenInterface->willStopOptimizedFullscreen();
211 }
212
213 - (void)playerViewControllerDidStopOptimizedFullscreen:(AVPlayerViewController *)playerViewController
214 {
215     UNUSED_PARAM(playerViewController);
216     self.fullscreenInterface->didStopOptimizedFullscreen();
217 }
218
219 - (void)playerViewControllerWillCancelOptimizedFullscreen:(AVPlayerViewController *)playerViewController
220 {
221     UNUSED_PARAM(playerViewController);
222     self.fullscreenInterface->willCancelOptimizedFullscreen();
223 }
224
225 - (void)playerViewControllerDidCancelOptimizedFullscreen:(AVPlayerViewController *)playerViewController
226 {
227     UNUSED_PARAM(playerViewController);
228     self.fullscreenInterface->didCancelOptimizedFullscreen();
229 }
230
231 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason
232 {
233     UNUSED_PARAM(playerViewController);
234     UNUSED_PARAM(reason);
235     if (!self.delegate)
236         return YES;
237     
238     if (reason == AVPlayerViewControllerExitFullScreenReasonDoneButtonTapped || reason == AVPlayerViewControllerExitFullScreenReasonRemoteControlStopEventReceived)
239         self.delegate->pause();
240     
241     self.delegate->requestExitFullscreen();
242     return NO;
243 }
244
245 - (void)playerViewController:(AVPlayerViewController *)playerViewController restoreUserInterfaceForOptimizedFullscreenStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler
246 {
247     UNUSED_PARAM(playerViewController);
248     self.fullscreenInterface->prepareForOptimizedFullscreenStopWithCompletionHandler(completionHandler);
249 }
250
251 - (void)play:(id)sender
252 {
253     UNUSED_PARAM(sender);
254     if (!self.delegate)
255         return;
256     self.delegate->play();
257 }
258
259 - (void)pause:(id)sender
260 {
261     UNUSED_PARAM(sender);
262     if (!self.delegate)
263         return;
264     self.delegate->pause();
265 }
266
267 - (void)togglePlayback:(id)sender
268 {
269     UNUSED_PARAM(sender);
270     if (!self.delegate)
271         return;
272     self.delegate->togglePlayState();
273 }
274
275 - (void)togglePlaybackEvenWhenInBackground:(id)sender
276 {
277     [self togglePlayback:sender];
278 }
279
280 - (BOOL)isPlaying
281 {
282     return [self rate] != 0;
283 }
284
285 - (void)setPlaying:(BOOL)playing
286 {
287     if (!self.delegate)
288         return;
289     if (playing)
290         self.delegate->play();
291     else
292         self.delegate->pause();
293 }
294
295 + (NSSet *)keyPathsForValuesAffectingPlaying
296 {
297     return [NSSet setWithObject:@"rate"];
298 }
299
300 - (void)beginScrubbing:(id)sender
301 {
302     UNUSED_PARAM(sender);
303     if (!self.delegate)
304         return;
305     self.delegate->beginScrubbing();
306 }
307
308 - (void)endScrubbing:(id)sender
309 {
310     UNUSED_PARAM(sender);
311     if (!self.delegate)
312         return;
313     self.delegate->endScrubbing();
314 }
315
316 - (void)seekToTime:(NSTimeInterval)time
317 {
318     if (!self.delegate)
319         return;
320     self.delegate->fastSeek(time);
321 }
322
323 - (BOOL)hasLiveStreamingContent
324 {
325     if ([self status] == AVPlayerControllerStatusReadyToPlay)
326         return [self contentDuration] == std::numeric_limits<float>::infinity();
327     return NO;
328 }
329
330 + (NSSet *)keyPathsForValuesAffectingHasLiveStreamingContent
331 {
332     return [NSSet setWithObjects:@"contentDuration", @"status", nil];
333 }
334
335 - (void)skipBackwardThirtySeconds:(id)sender
336 {
337     UNUSED_PARAM(sender);
338     BOOL isTimeWithinSeekableTimeRanges = NO;
339     CMTime currentTime = CMTimeMakeWithSeconds([[self timing] currentValue], 1000);
340     CMTime thirtySecondsBeforeCurrentTime = CMTimeSubtract(currentTime, CMTimeMake(30, 1));
341     
342     for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) {
343         if (CMTimeRangeContainsTime([seekableTimeRangeValue CMTimeRangeValue], thirtySecondsBeforeCurrentTime)) {
344             isTimeWithinSeekableTimeRanges = YES;
345             break;
346         }
347     }
348     
349     if (isTimeWithinSeekableTimeRanges)
350         [self seekToTime:CMTimeGetSeconds(thirtySecondsBeforeCurrentTime)];
351 }
352
353 - (void)gotoEndOfSeekableRanges:(id)sender
354 {
355     UNUSED_PARAM(sender);
356     NSTimeInterval timeAtEndOfSeekableTimeRanges = NAN;
357     
358     for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) {
359         CMTimeRange seekableTimeRange = [seekableTimeRangeValue CMTimeRangeValue];
360         NSTimeInterval endOfSeekableTimeRange = CMTimeGetSeconds(CMTimeRangeGetEnd(seekableTimeRange));
361         if (isnan(timeAtEndOfSeekableTimeRanges) || endOfSeekableTimeRange > timeAtEndOfSeekableTimeRanges)
362             timeAtEndOfSeekableTimeRanges = endOfSeekableTimeRange;
363     }
364     
365     if (!isnan(timeAtEndOfSeekableTimeRanges))
366         [self seekToTime:timeAtEndOfSeekableTimeRanges];
367 }
368
369 - (BOOL)canScanForward
370 {
371     return [self canPlay];
372 }
373
374 + (NSSet *)keyPathsForValuesAffectingCanScanForward
375 {
376     return [NSSet setWithObject:@"canPlay"];
377 }
378
379 - (void)beginScanningForward:(id)sender
380 {
381     UNUSED_PARAM(sender);
382     if (!self.delegate)
383         return;
384     self.delegate->beginScanningForward();
385 }
386
387 - (void)endScanningForward:(id)sender
388 {
389     UNUSED_PARAM(sender);
390     if (!self.delegate)
391         return;
392     self.delegate->endScanning();
393 }
394
395 - (void)beginScanningBackward:(id)sender
396 {
397     UNUSED_PARAM(sender);
398     if (!self.delegate)
399         return;
400     self.delegate->beginScanningBackward();
401 }
402
403 - (void)endScanningBackward:(id)sender
404 {
405     UNUSED_PARAM(sender);
406     if (!self.delegate)
407         return;
408     self.delegate->endScanning();
409 }
410
411 - (BOOL)canSeekToBeginning
412 {
413     CMTime minimumTime = kCMTimeIndefinite;
414
415     for (NSValue *value in [self seekableTimeRanges])
416         minimumTime = CMTimeMinimum([value CMTimeRangeValue].start, minimumTime);
417
418     return CMTIME_IS_NUMERIC(minimumTime);
419 }
420
421 + (NSSet *)keyPathsForValuesAffectingCanSeekToBeginning
422 {
423     return [NSSet setWithObject:@"seekableTimeRanges"];
424 }
425
426 - (void)seekToBeginning:(id)sender
427 {
428     UNUSED_PARAM(sender);
429     if (!self.delegate)
430         return;
431     self.delegate->seekToTime(-INFINITY);
432 }
433
434 - (void)seekChapterBackward:(id)sender
435 {
436     [self seekToBeginning:sender];
437 }
438
439 - (BOOL)canSeekToEnd
440 {
441     CMTime maximumTime = kCMTimeIndefinite;
442
443     for (NSValue *value in [self seekableTimeRanges])
444         maximumTime = CMTimeMaximum(CMTimeRangeGetEnd([value CMTimeRangeValue]), maximumTime);
445
446     return CMTIME_IS_NUMERIC(maximumTime);
447 }
448
449 + (NSSet *)keyPathsForValuesAffectingCanSeekToEnd
450 {
451     return [NSSet setWithObject:@"seekableTimeRanges"];
452 }
453
454 - (void)seekToEnd:(id)sender
455 {
456     UNUSED_PARAM(sender);
457     if (!self.delegate)
458         return;
459     self.delegate->seekToTime(INFINITY);
460 }
461
462 - (void)seekChapterForward:(id)sender
463 {
464     [self seekToEnd:sender];
465 }
466
467 - (BOOL)hasMediaSelectionOptions
468 {
469     return [self hasAudioMediaSelectionOptions] || [self hasLegibleMediaSelectionOptions];
470 }
471
472 + (NSSet *)keyPathsForValuesAffectingHasMediaSelectionOptions
473 {
474     return [NSSet setWithObjects:@"hasAudioMediaSelectionOptions", @"hasLegibleMediaSelectionOptions", nil];
475 }
476
477 - (BOOL)hasAudioMediaSelectionOptions
478 {
479     return [[self audioMediaSelectionOptions] count] > 1;
480 }
481
482 + (NSSet *)keyPathsForValuesAffectingHasAudioMediaSelectionOptions
483 {
484     return [NSSet setWithObject:@"audioMediaSelectionOptions"];
485 }
486
487 - (BOOL)hasLegibleMediaSelectionOptions
488 {
489     const NSUInteger numDefaultLegibleOptions = 2;
490     return [[self legibleMediaSelectionOptions] count] > numDefaultLegibleOptions;
491 }
492
493 + (NSSet *)keyPathsForValuesAffectingHasLegibleMediaSelectionOptions
494 {
495     return [NSSet setWithObject:@"legibleMediaSelectionOptions"];
496 }
497
498 - (WebAVMediaSelectionOption *)currentAudioMediaSelectionOption
499 {
500     return _currentAudioMediaSelectionOption;
501 }
502
503 - (void)setCurrentAudioMediaSelectionOption:(WebAVMediaSelectionOption *)option
504 {
505     if (option == _currentAudioMediaSelectionOption)
506         return;
507     
508     [_currentAudioMediaSelectionOption release];
509     _currentAudioMediaSelectionOption = [option retain];
510     
511     if (!self.delegate)
512         return;
513     
514     NSInteger index = NSNotFound;
515     
516     if (option && self.audioMediaSelectionOptions)
517         index = [self.audioMediaSelectionOptions indexOfObject:option];
518     
519     self.delegate->selectAudioMediaOption(index != NSNotFound ? index : UINT64_MAX);
520 }
521
522 - (WebAVMediaSelectionOption *)currentLegibleMediaSelectionOption
523 {
524     return _currentLegibleMediaSelectionOption;
525 }
526
527 - (void)setCurrentLegibleMediaSelectionOption:(WebAVMediaSelectionOption *)option
528 {
529     if (option == _currentLegibleMediaSelectionOption)
530         return;
531     
532     [_currentLegibleMediaSelectionOption release];
533     _currentLegibleMediaSelectionOption = [option retain];
534     
535     if (!self.delegate)
536         return;
537     
538     NSInteger index = NSNotFound;
539     
540     if (option && self.legibleMediaSelectionOptions)
541         index = [self.legibleMediaSelectionOptions indexOfObject:option];
542     
543     self.delegate->selectLegibleMediaOption(index != NSNotFound ? index : UINT64_MAX);
544 }
545
546 - (BOOL)isPlayingOnExternalScreen
547 {
548     return [self isExternalPlaybackActive];
549 }
550
551 + (NSSet *)keyPathsForValuesAffectingPlayingOnExternalScreen
552 {
553     return [NSSet setWithObjects:@"externalPlaybackActive", nil];
554 }
555 @end
556
557 @interface WebAVMediaSelectionOption : NSObject
558 @property (retain) NSString *localizedDisplayName;
559 @end
560
561 @implementation WebAVMediaSelectionOption
562 @end
563
564 @interface WebAVVideoLayer : CALayer <AVVideoLayer>
565 +(WebAVVideoLayer *)videoLayer;
566 @property (nonatomic) AVVideoLayerGravity videoLayerGravity;
567 @property (nonatomic, getter = isReadyForDisplay) BOOL readyForDisplay;
568 @property (nonatomic) CGRect videoRect;
569 - (void)setPlayerViewController:(AVPlayerViewController *)playerViewController;
570 - (void)setPlayerController:(AVPlayerController *)playerController;
571 @property (nonatomic, retain) CALayer* videoSublayer;
572 @end
573
574 @implementation WebAVVideoLayer
575 {
576     RetainPtr<WebAVPlayerController> _avPlayerController;
577     RetainPtr<AVPlayerViewController> _avPlayerViewController;
578     RetainPtr<CALayer> _videoSublayer;
579     AVVideoLayerGravity _videoLayerGravity;
580 }
581
582 +(WebAVVideoLayer *)videoLayer
583 {
584     return [[[WebAVVideoLayer alloc] init] autorelease];
585 }
586
587 - (instancetype)init
588 {
589     self = [super init];
590     if (self) {
591         [self setMasksToBounds:YES];
592         [self setVideoLayerGravity:AVVideoLayerGravityResizeAspect];
593     }
594     return self;
595 }
596
597 - (void)dealloc
598 {
599     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
600     [super dealloc];
601 }
602
603 - (void)setPlayerController:(AVPlayerController *)playerController
604 {
605     ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]);
606     _avPlayerController = (WebAVPlayerController *)playerController;
607 }
608
609 - (void)setPlayerViewController:(AVPlayerViewController *)playerViewController
610 {
611     _avPlayerViewController = playerViewController;
612 }
613
614 - (void)setVideoSublayer:(CALayer *)videoSublayer
615 {
616     _videoSublayer = videoSublayer;
617     [self addSublayer:videoSublayer];
618 }
619
620 - (CALayer*)videoSublayer
621 {
622     return _videoSublayer.get();
623 }
624
625 - (void)setBounds:(CGRect)bounds
626 {
627     [super setBounds:bounds];
628
629     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))];
630
631     if (![_avPlayerController delegate] || !_avPlayerViewController)
632         return;
633
634     FloatRect videoFrame = [_avPlayerController delegate]->videoLayerFrame();
635     FloatRect targetFrame;
636     switch ([_avPlayerController delegate]->videoLayerGravity()) {
637     case WebCore::WebVideoFullscreenModel::VideoGravityResize:
638         targetFrame = bounds;
639         break;
640     case WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect:
641         targetFrame = largestRectWithAspectRatioInsideRect(videoFrame.size().aspectRatio(), bounds);
642         break;
643     case WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill:
644         targetFrame = smallestRectWithAspectRatioAroundRect(videoFrame.size().aspectRatio(), bounds);
645         break;
646     }
647     CATransform3D transform = CATransform3DMakeScale(targetFrame.width() / videoFrame.width(), targetFrame.height() / videoFrame.height(), 1);
648     [_videoSublayer setSublayerTransform:transform];
649
650     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
651     [self performSelector:@selector(resolveBounds) withObject:nil afterDelay:[CATransaction animationDuration] + 0.1];
652 }
653
654 - (void)resolveBounds
655 {
656     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
657     if (!_avPlayerController || ![_avPlayerController delegate])
658         return;
659
660     [CATransaction begin];
661     [CATransaction setAnimationDuration:0];
662
663     [_videoSublayer setSublayerTransform:CATransform3DIdentity];
664     [_avPlayerController delegate]->setVideoLayerFrame([self bounds]);
665
666     [CATransaction commit];
667 }
668
669 - (void)setVideoLayerGravity:(AVVideoLayerGravity)videoLayerGravity
670 {
671     _videoLayerGravity = videoLayerGravity;
672     
673     if (![_avPlayerController delegate])
674         return;
675
676     WebCore::WebVideoFullscreenModel::VideoGravity gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect;
677     if (videoLayerGravity == AVVideoLayerGravityResize)
678         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResize;
679     if (videoLayerGravity == AVVideoLayerGravityResizeAspect)
680         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect;
681     else if (videoLayerGravity == AVVideoLayerGravityResizeAspectFill)
682         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill;
683     else
684         ASSERT_NOT_REACHED();
685     
686     [_avPlayerController delegate]->setVideoLayerGravity(gravity);
687 }
688
689 - (AVVideoLayerGravity)videoLayerGravity
690 {
691     return _videoLayerGravity;
692 }
693
694 @end
695
696 WebVideoFullscreenInterfaceAVKit::WebVideoFullscreenInterfaceAVKit()
697     : m_playerController(adoptNS([[WebAVPlayerController alloc] init]))
698     , m_videoFullscreenModel(nullptr)
699     , m_fullscreenChangeObserver(nullptr)
700     , m_mode(HTMLMediaElement::VideoFullscreenModeNone)
701     , m_exitRequested(false)
702     , m_exitCompleted(false)
703     , m_enterRequested(false)
704 {
705     [m_playerController setFullscreenInterface:this];
706 }
707
708 void WebVideoFullscreenInterfaceAVKit::resetMediaState()
709 {
710     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
711     
712     dispatch_async(dispatch_get_main_queue(), [strongThis] {
713         if (!strongThis->m_playerController) {
714             strongThis->m_playerController = adoptNS([[WebAVPlayerController alloc] init]);
715             [strongThis->m_playerController setDelegate:strongThis->m_videoFullscreenModel];
716             [strongThis->m_playerController setFullscreenInterface:strongThis.get()];
717             
718         } else
719             [strongThis->m_playerController resetState];
720     });
721 }
722
723 void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenModel(WebVideoFullscreenModel* model)
724 {
725     m_videoFullscreenModel = model;
726     [m_playerController setDelegate:m_videoFullscreenModel];
727 }
728
729 void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenChangeObserver(WebVideoFullscreenChangeObserver* observer)
730 {
731     m_fullscreenChangeObserver = observer;
732 }
733
734 void WebVideoFullscreenInterfaceAVKit::setDuration(double duration)
735 {
736     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
737     
738     dispatch_async(dispatch_get_main_queue(), [strongThis, duration] {
739         WebAVPlayerController* playerController = strongThis->m_playerController.get();
740
741         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=127017 use correct values instead of duration for all these
742         playerController.contentDuration = duration;
743         playerController.maxTime = duration;
744         playerController.contentDurationWithinEndTimes = duration;
745
746         // FIXME: we take this as an indication that playback is ready.
747         playerController.canPlay = YES;
748         playerController.canPause = YES;
749         playerController.canTogglePlayback = YES;
750         playerController.hasEnabledAudio = YES;
751         playerController.canSeek = YES;
752         playerController.minTime = 0;
753         playerController.status = AVPlayerControllerStatusReadyToPlay;
754     });
755 }
756
757 void WebVideoFullscreenInterfaceAVKit::setCurrentTime(double currentTime, double anchorTime)
758 {
759     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
760     
761     dispatch_async(dispatch_get_main_queue(), [strongThis, currentTime, anchorTime] {
762         NSTimeInterval anchorTimeStamp = ![strongThis->m_playerController rate] ? NAN : anchorTime;
763         AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime
764             anchorTimeStamp:anchorTimeStamp rate:0];
765         [strongThis->m_playerController setTiming:timing];
766     });
767 }
768
769 void WebVideoFullscreenInterfaceAVKit::setBufferedTime(double bufferedTime)
770 {
771     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
772
773     dispatch_async(dispatch_get_main_queue(), [strongThis, bufferedTime] {
774         WebAVPlayerController* playerController = strongThis->m_playerController.get();
775         double duration = playerController.contentDuration;
776         double normalizedBufferedTime;
777         if (!duration)
778             normalizedBufferedTime = 0;
779         else
780             normalizedBufferedTime = bufferedTime / duration;
781         playerController.loadedTimeRanges = @[@0, @(normalizedBufferedTime)];
782     });
783 }
784
785 void WebVideoFullscreenInterfaceAVKit::setRate(bool isPlaying, float playbackRate)
786 {
787     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
788     
789     dispatch_async(dispatch_get_main_queue(), [strongThis, isPlaying, playbackRate] {
790         [strongThis->m_playerController setRate:isPlaying ? playbackRate : 0.];
791     });
792 }
793
794 void WebVideoFullscreenInterfaceAVKit::setVideoDimensions(bool hasVideo, float width, float height)
795 {
796     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
797     
798     dispatch_async(dispatch_get_main_queue(), [strongThis, hasVideo, width, height] {
799         [strongThis->m_playerController setHasEnabledVideo:hasVideo];
800         [strongThis->m_playerController setContentDimensions:CGSizeMake(width, height)];
801     });
802 }
803
804 void WebVideoFullscreenInterfaceAVKit::setSeekableRanges(const TimeRanges& timeRanges)
805 {
806     RetainPtr<NSMutableArray> seekableRanges = adoptNS([[NSMutableArray alloc] init]);
807     ExceptionCode exceptionCode;
808
809     for (unsigned i = 0; i < timeRanges.length(); i++) {
810         double start = timeRanges.start(i, exceptionCode);
811         double end = timeRanges.end(i, exceptionCode);
812         
813         CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000));
814         [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]];
815     }
816     
817     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
818     
819     dispatch_async(dispatch_get_main_queue(), [strongThis, seekableRanges] {
820         [strongThis->m_playerController setSeekableTimeRanges:seekableRanges.get()];
821     });
822 }
823
824 void WebVideoFullscreenInterfaceAVKit::setCanPlayFastReverse(bool canPlayFastReverse)
825 {
826     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
827     
828     dispatch_async(dispatch_get_main_queue(), [strongThis, canPlayFastReverse] {
829         [strongThis->m_playerController setCanScanBackward:canPlayFastReverse];
830     });
831 }
832
833 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<String>& options)
834 {
835     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
836     for (auto& name : options) {
837         RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]);
838         [webOption setLocalizedDisplayName:name];
839         [webOptions addObject:webOption.get()];
840     }
841     return webOptions;
842 }
843
844 void WebVideoFullscreenInterfaceAVKit::setAudioMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
845 {
846     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
847     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
848     
849     dispatch_async(dispatch_get_main_queue(), [webOptions, strongThis, selectedIndex] {
850         [strongThis->m_playerController setAudioMediaSelectionOptions:webOptions.get()];
851         if (selectedIndex < [webOptions count])
852             [strongThis->m_playerController setCurrentAudioMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
853     });
854 }
855
856 void WebVideoFullscreenInterfaceAVKit::setLegibleMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
857 {
858     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
859     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
860
861     dispatch_async(dispatch_get_main_queue(), [webOptions, strongThis, selectedIndex] {
862         [strongThis->m_playerController setLegibleMediaSelectionOptions:webOptions.get()];
863         if (selectedIndex < [webOptions count])
864             [strongThis->m_playerController setCurrentLegibleMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
865     });
866 }
867
868 void WebVideoFullscreenInterfaceAVKit::setExternalPlayback(bool enabled, ExternalPlaybackTargetType targetType, String localizedDeviceName)
869 {
870     AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone;
871     if (targetType == TargetTypeAirPlay)
872         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay;
873     else if (targetType == TargetTypeTVOut)
874         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut;
875
876     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
877
878     dispatch_async(dispatch_get_main_queue(), [strongThis, enabled, localizedDeviceName, externalPlaybackType] {
879         WebAVPlayerController* playerController = strongThis->m_playerController.get();
880         playerController.externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName;
881         playerController.externalPlaybackType = externalPlaybackType;
882         playerController.externalPlaybackActive = enabled;
883         [strongThis->m_videoLayerContainer.get() setHidden:enabled];
884     });
885 }
886
887 void WebVideoFullscreenInterfaceAVKit::setupFullscreen(PlatformLayer& videoLayer, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElement::VideoFullscreenMode mode, bool allowOptimizedFullscreen)
888 {
889     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
890
891     ASSERT(mode != HTMLMediaElement::VideoFullscreenModeNone);
892     m_videoLayer = &videoLayer;
893
894     m_mode = mode;
895
896     dispatch_async(dispatch_get_main_queue(), [strongThis, &videoLayer, initialRect, parentView, mode, allowOptimizedFullscreen] {
897         strongThis->setupFullscreenInternal(videoLayer, initialRect, parentView, mode, allowOptimizedFullscreen);
898     });
899 }
900
901 void WebVideoFullscreenInterfaceAVKit::setupFullscreenInternal(PlatformLayer& videoLayer, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElement::VideoFullscreenMode mode, bool allowOptimizedFullscreen)
902 {
903     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::setupFullscreenInternal(%p)", this);
904     UNUSED_PARAM(videoLayer);
905     UNUSED_PARAM(mode);
906
907     [CATransaction begin];
908     [CATransaction setDisableActions:YES];
909     m_parentView = parentView;
910     m_parentWindow = parentView.window;
911
912     if (!applicationIsAdSheet()) {
913         m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
914         [m_window setBackgroundColor:[getUIColorClass() clearColor]];
915         m_viewController = adoptNS([allocUIViewControllerInstance() init]);
916         [[m_viewController view] setFrame:[m_window bounds]];
917         [m_window setRootViewController:m_viewController.get()];
918         [m_window makeKeyAndVisible];
919     }
920
921     [m_videoLayer removeFromSuperlayer];
922
923     m_videoLayerContainer = [WebAVVideoLayer videoLayer];
924     [m_videoLayerContainer setHidden:[m_playerController isExternalPlaybackActive]];
925     [m_videoLayerContainer setVideoSublayer:m_videoLayer.get()];
926
927     CGSize videoSize = [m_playerController contentDimensions];
928     CGRect videoRect = CGRectMake(0, 0, videoSize.width, videoSize.height);
929     [m_videoLayerContainer setVideoRect:videoRect];
930     if (m_videoFullscreenModel)
931         m_videoFullscreenModel->setVideoLayerFrame(videoRect);
932
933     m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithVideoLayer:m_videoLayerContainer.get()]);
934     [m_playerViewController setShowsPlaybackControls:NO];
935     [m_playerViewController setPlayerController:(AVPlayerController *)m_playerController.get()];
936     [m_playerViewController setDelegate:m_playerController.get()];
937     [m_playerViewController setAllowsOptimizedFullscreen:allowOptimizedFullscreen];
938
939     [m_videoLayerContainer setPlayerViewController:m_playerViewController.get()];
940
941     if (m_viewController) {
942         [m_viewController addChildViewController:m_playerViewController.get()];
943         [[m_viewController view] addSubview:[m_playerViewController view]];
944         [m_playerViewController view].frame = [parentView convertRect:initialRect toView:nil];
945     } else {
946         [parentView addSubview:[m_playerViewController view]];
947         [m_playerViewController view].frame = initialRect;
948     }
949
950     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
951     [[m_playerViewController view] setNeedsLayout];
952     [[m_playerViewController view] layoutIfNeeded];
953
954     [CATransaction commit];
955
956     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
957     WebThreadRun([strongThis] {
958         if (strongThis->m_fullscreenChangeObserver)
959             strongThis->m_fullscreenChangeObserver->didSetupFullscreen();
960     });
961 }
962
963 void WebVideoFullscreenInterfaceAVKit::enterFullscreen()
964 {
965     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreen(%p)", this);
966
967     m_exitCompleted = false;
968     m_exitRequested = false;
969     m_enterRequested = true;
970
971     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
972     dispatch_async(dispatch_get_main_queue(), [strongThis] {
973         [strongThis->m_videoLayerContainer setBackgroundColor:[[getUIColorClass() blackColor] CGColor]];
974         if (strongThis->mode() == HTMLMediaElement::VideoFullscreenModeOptimized)
975             strongThis->enterFullscreenOptimized();
976         else if (strongThis->mode() == HTMLMediaElement::VideoFullscreenModeStandard)
977             strongThis->enterFullscreenStandard();
978         else
979             ASSERT_NOT_REACHED();
980     });
981 }
982
983 void WebVideoFullscreenInterfaceAVKit::enterFullscreenOptimized()
984 {
985     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
986
987     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenOptimized(%p)", this);
988     [m_playerViewController startOptimizedFullscreen];
989 }
990
991 void WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard()
992 {
993     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
994     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
995     [m_playerViewController enterFullScreenWithCompletionHandler:[this, strongThis] (BOOL succeeded, NSError*) {
996         UNUSED_PARAM(succeeded);
997         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
998         [m_playerViewController setShowsPlaybackControls:YES];
999
1000         WebThreadRun([this, strongThis] {
1001             if (m_fullscreenChangeObserver)
1002                 m_fullscreenChangeObserver->didEnterFullscreen();
1003         });
1004     }];
1005 }
1006
1007 void WebVideoFullscreenInterfaceAVKit::exitFullscreen(const WebCore::IntRect& finalRect)
1008 {
1009     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1010
1011     m_exitRequested = true;
1012     if (m_exitCompleted) {
1013         WebThreadRun([strongThis] {
1014             if (strongThis->m_fullscreenChangeObserver)
1015                 strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1016         });
1017         return;
1018     }
1019
1020     dispatch_async(dispatch_get_main_queue(), [strongThis, finalRect] {
1021         strongThis->exitFullscreenInternal(finalRect);
1022     });
1023 }
1024
1025 void WebVideoFullscreenInterfaceAVKit::exitFullscreenInternal(const WebCore::IntRect& finalRect)
1026 {
1027     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::exitFullscreenInternal(%p)", this);
1028     [m_playerViewController setShowsPlaybackControls:NO];
1029     if (m_viewController)
1030         [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:nil];
1031     else
1032         [m_playerViewController view].frame = finalRect;
1033
1034     if ([m_videoLayerContainer videoLayerGravity] != AVVideoLayerGravityResizeAspect)
1035         [m_videoLayerContainer setVideoLayerGravity:AVVideoLayerGravityResizeAspect];
1036     [[m_playerViewController view] layoutIfNeeded];
1037
1038
1039     if (isMode(HTMLMediaElement::VideoFullscreenModeOptimized)) {
1040         [m_window setHidden:NO];
1041         [m_playerViewController stopOptimizedFullscreen];
1042     } else if (isMode(HTMLMediaElement::VideoFullscreenModeOptimized | HTMLMediaElement::VideoFullscreenModeStandard)) {
1043         RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1044         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[strongThis] (BOOL, NSError*) {
1045             [strongThis->m_window setHidden:NO];
1046             [strongThis->m_playerViewController stopOptimizedFullscreen];
1047         }];
1048     } else if (isMode(HTMLMediaElement::VideoFullscreenModeStandard)) {
1049         RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1050         [m_playerViewController exitFullScreenWithCompletionHandler:[strongThis] (BOOL, NSError*) {
1051             strongThis->m_exitCompleted = true;
1052
1053             [CATransaction begin];
1054             [CATransaction setDisableActions:YES];
1055             [strongThis->m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1056             [[strongThis->m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1057             [CATransaction commit];
1058
1059             WebThreadRun([strongThis] {
1060                 if (strongThis->m_fullscreenChangeObserver)
1061                     strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1062             });
1063         }];
1064     };
1065 }
1066
1067 @interface UIApplication ()
1068 -(void)_setStatusBarOrientation:(UIInterfaceOrientation)o;
1069 @end
1070
1071 @interface UIWindow ()
1072 -(UIInterfaceOrientation)interfaceOrientation;
1073 @end
1074
1075 void WebVideoFullscreenInterfaceAVKit::cleanupFullscreen()
1076 {
1077     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1078     
1079     dispatch_async(dispatch_get_main_queue(), [strongThis] {
1080         strongThis->cleanupFullscreenInternal();
1081     });
1082 }
1083
1084 void WebVideoFullscreenInterfaceAVKit::cleanupFullscreenInternal()
1085 {
1086     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::cleanupFullscreenInternal(%p)", this);
1087     if (m_window) {
1088         [m_window setHidden:YES];
1089         [m_window setRootViewController:nil];
1090         if (m_parentWindow)
1091             [[getUIApplicationClass() sharedApplication] _setStatusBarOrientation:[m_parentWindow interfaceOrientation]];
1092     }
1093     
1094     [m_playerController setDelegate:nil];
1095     [m_playerController setFullscreenInterface:nil];
1096     
1097     [m_playerViewController setDelegate:nil];
1098     [m_playerViewController setPlayerController:nil];
1099     
1100     if (hasMode(HTMLMediaElement::VideoFullscreenModeOptimized))
1101         [m_playerViewController cancelOptimizedFullscreen];
1102     if (hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1103         [m_playerViewController exitFullScreenAnimated:NO completionHandler:nil];
1104     
1105     [[m_playerViewController view] removeFromSuperview];
1106     if (m_viewController)
1107         [m_playerViewController removeFromParentViewController];
1108     
1109     [m_videoLayer removeFromSuperlayer];
1110     [m_videoLayerContainer removeFromSuperlayer];
1111     [m_videoLayerContainer setPlayerViewController:nil];
1112     [[m_viewController view] removeFromSuperview];
1113     
1114     m_videoLayer = nil;
1115     m_videoLayerContainer = nil;
1116     m_playerViewController = nil;
1117     m_playerController = nil;
1118     m_viewController = nil;
1119     m_window = nil;
1120     m_parentView = nil;
1121     m_parentWindow = nil;
1122     
1123     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1124     WebThreadRun([strongThis] {
1125         if (strongThis->m_fullscreenChangeObserver)
1126             strongThis->m_fullscreenChangeObserver->didCleanupFullscreen();
1127         strongThis->m_enterRequested = false;
1128     });
1129 }
1130
1131 void WebVideoFullscreenInterfaceAVKit::invalidate()
1132 {
1133     m_videoFullscreenModel = nil;
1134     m_fullscreenChangeObserver = nil;
1135     
1136     cleanupFullscreenInternal();
1137 }
1138
1139 void WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
1140 {
1141     if (!m_enterRequested)
1142         return;
1143     
1144     if (hasMode(HTMLMediaElement::VideoFullscreenModeOptimized))
1145         return;
1146     
1147     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
1148
1149     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1150     dispatch_async(dispatch_get_main_queue(), [strongThis] {
1151         [strongThis->m_window setHidden:YES];
1152     });
1153
1154     if (m_videoFullscreenModel && !m_exitRequested) {
1155         m_videoFullscreenModel->pause();
1156         m_videoFullscreenModel->requestExitFullscreen();
1157     }
1158 }
1159
1160 void WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
1161 {
1162     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
1163     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1164     dispatch_async(dispatch_get_main_queue(), [strongThis, visible, inlineRect] {
1165         if (strongThis->m_prepareToInlineCallback) {
1166             
1167             if (strongThis->m_viewController)
1168                 [strongThis->m_playerViewController view].frame = [strongThis->m_parentView convertRect:inlineRect toView:nil];
1169             else
1170                 [strongThis->m_playerViewController view].frame = inlineRect;
1171
1172             std::function<void(bool)> callback = WTF::move(strongThis->m_prepareToInlineCallback);
1173             callback(visible);
1174         }
1175     });
1176 }
1177
1178 bool WebVideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoOptimized() const
1179 {
1180     return [m_playerController isPlaying] && m_mode == HTMLMediaElement::VideoFullscreenModeStandard && wkIsOptimizedFullscreenSupported();
1181 }
1182
1183 void WebVideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(std::function<void(bool)> callback)
1184 {
1185     m_prepareToInlineCallback = callback;
1186     if (m_fullscreenChangeObserver)
1187         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
1188 }
1189
1190 void WebVideoFullscreenInterfaceAVKit::willStartOptimizedFullscreen()
1191 {
1192     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartOptimizedFullscreen(%p)", this);
1193     setMode(HTMLMediaElement::VideoFullscreenModeOptimized);
1194
1195     if (!hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1196         return;
1197
1198     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1199     fullscreenMayReturnToInline([strongThis](bool visible) {
1200         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartOptimizedFullscreen - lambda(%p) - visible(%s)", strongThis.get(), boolString(visible));
1201
1202         if (!visible) {
1203             [strongThis->m_window setHidden:YES];
1204             return;
1205         }
1206
1207         [[strongThis->m_playerViewController view] layoutIfNeeded];
1208
1209         [strongThis->m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis] (BOOL completed, NSError*) {
1210             if (!completed)
1211                 return;
1212             strongThis->clearMode(HTMLMediaElement::VideoFullscreenModeStandard);
1213             [strongThis->m_window setHidden:YES];
1214         }];
1215     });
1216 }
1217
1218 void WebVideoFullscreenInterfaceAVKit::didStartOptimizedFullscreen()
1219 {
1220     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStartOptimizedFullscreen(%p)", this);
1221     [m_playerViewController setShowsPlaybackControls:YES];
1222
1223     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1224     WebThreadRun([strongThis] {
1225         [strongThis->m_window setHidden:YES];
1226         if (strongThis->m_fullscreenChangeObserver)
1227             strongThis->m_fullscreenChangeObserver->didEnterFullscreen();
1228     });
1229 }
1230
1231 void WebVideoFullscreenInterfaceAVKit::willStopOptimizedFullscreen()
1232 {
1233     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStopOptimizedFullscreen(%p)", this);
1234     [m_window setHidden:NO];
1235
1236     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1237     WebThreadRun([strongThis] {
1238         if (strongThis->m_videoFullscreenModel)
1239             strongThis->m_videoFullscreenModel->requestExitFullscreen();
1240     });
1241 }
1242
1243 void WebVideoFullscreenInterfaceAVKit::didStopOptimizedFullscreen()
1244 {
1245     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStopOptimizedFullscreen(%p)", this);
1246     if (hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1247         return;
1248
1249     m_exitCompleted = true;
1250
1251     [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1252     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1253
1254     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1255     WebThreadRun([strongThis] {
1256         strongThis->clearMode(HTMLMediaElement::VideoFullscreenModeOptimized);
1257         [strongThis->m_window setHidden:YES];
1258         if (strongThis->m_fullscreenChangeObserver)
1259             strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1260     });
1261 }
1262
1263 void WebVideoFullscreenInterfaceAVKit::willCancelOptimizedFullscreen()
1264 {
1265     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willCancelOptimizedFullscreen(%p)", this);
1266     [m_window setHidden:NO];
1267     [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1268     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1269
1270     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1271     WebThreadRun([strongThis] {
1272         if (strongThis->m_videoFullscreenModel)
1273             strongThis->m_videoFullscreenModel->requestExitFullscreen();
1274     });
1275 }
1276
1277 void WebVideoFullscreenInterfaceAVKit::didCancelOptimizedFullscreen()
1278 {
1279     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didCancelOptimizedFullscreen(%p)", this);
1280     if (hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1281         return;
1282
1283     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1284     WebThreadRun([strongThis] {
1285         strongThis->clearMode(HTMLMediaElement::VideoFullscreenModeOptimized);
1286         [strongThis->m_window setHidden:YES];
1287         if (strongThis->m_fullscreenChangeObserver)
1288             strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1289     });
1290 }
1291
1292 void WebVideoFullscreenInterfaceAVKit::prepareForOptimizedFullscreenStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
1293 {
1294     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForOptimizedFullscreenStopWithCompletionHandler(%p)", this);
1295     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1296     RetainPtr<id> strongCompletionHandler = adoptNS([completionHandler copy]);
1297     fullscreenMayReturnToInline([strongThis, strongCompletionHandler](bool restored)  {
1298         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForOptimizedFullscreenStopWithCompletionHandler lambda(%p) - restored(%s)", strongThis.get(), boolString(restored));
1299         void (^completionHandler)(BOOL restored) = strongCompletionHandler.get();
1300         completionHandler(restored);
1301     });
1302 }
1303
1304 void WebVideoFullscreenInterfaceAVKit::setMode(HTMLMediaElement::VideoFullscreenMode mode)
1305 {
1306     HTMLMediaElement::VideoFullscreenMode newMode = m_mode | mode;
1307     if (m_mode == newMode)
1308         return;
1309
1310     m_mode = newMode;
1311     if (m_videoFullscreenModel)
1312         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1313 }
1314
1315 void WebVideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElement::VideoFullscreenMode mode)
1316 {
1317     HTMLMediaElement::VideoFullscreenMode newMode = m_mode & ~mode;
1318     if (m_mode == newMode)
1319         return;
1320
1321     m_mode = newMode;
1322     if (m_videoFullscreenModel)
1323         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1324 }
1325
1326
1327 #endif