d28bb6e73b3212cefc01bb066eba5624a23c67b3
[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
556 - (BOOL)isOptimizedFullscreenPossible
557 {
558     return self.fullscreenInterface->allowOptimizedFullscreen();
559 }
560 @end
561
562 @interface WebAVMediaSelectionOption : NSObject
563 @property (retain) NSString *localizedDisplayName;
564 @end
565
566 @implementation WebAVMediaSelectionOption
567 @end
568
569
570 @interface WebCALayerHostWrapper : CALayer
571 @property (assign) WebVideoFullscreenModel* model;
572 @end
573
574 @implementation WebCALayerHostWrapper {
575     RetainPtr<CALayer> _videoSublayer;
576 }
577
578 - (void)setVideoSublayer:(CALayer*)videoSublayer
579 {
580     _videoSublayer = videoSublayer;
581     [self addSublayer:videoSublayer];
582 }
583
584 - (CALayer*)videoSublayer
585 {
586     return _videoSublayer.get();
587 }
588
589 - (void)setBounds:(CGRect)bounds
590 {
591     if (CGRectEqualToRect(bounds, self.bounds))
592         return;
593
594     [super setBounds:bounds];
595
596     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))];
597
598     if (!self.model)
599         return;
600
601     FloatRect videoFrame = self.model->videoLayerFrame();
602     FloatRect targetFrame;
603     switch (self.model->videoLayerGravity()) {
604     case WebCore::WebVideoFullscreenModel::VideoGravityResize:
605         targetFrame = bounds;
606         break;
607     case WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect:
608         targetFrame = largestRectWithAspectRatioInsideRect(videoFrame.size().aspectRatio(), bounds);
609         break;
610     case WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill:
611         targetFrame = smallestRectWithAspectRatioAroundRect(videoFrame.size().aspectRatio(), bounds);
612         break;
613     }
614     CATransform3D transform = CATransform3DMakeScale(targetFrame.width() / videoFrame.width(), targetFrame.height() / videoFrame.height(), 1);
615     [_videoSublayer setSublayerTransform:transform];
616
617     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
618     [self performSelector:@selector(resolveBounds) withObject:nil afterDelay:[CATransaction animationDuration] + 0.1];
619 }
620
621 - (void)resolveBounds
622 {
623     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
624     if (!self.model)
625         return;
626
627     [CATransaction begin];
628     [CATransaction setAnimationDuration:0];
629
630     [_videoSublayer setSublayerTransform:CATransform3DIdentity];
631     self.model->setVideoLayerFrame([self bounds]);
632     
633     [CATransaction commit];
634 }
635 @end
636
637 @interface WebAVVideoLayer : CALayer <AVVideoLayer>
638 +(WebAVVideoLayer *)videoLayer;
639 @property (nonatomic) AVVideoLayerGravity videoLayerGravity;
640 @property (nonatomic, getter = isReadyForDisplay) BOOL readyForDisplay;
641 @property (nonatomic) CGRect videoRect;
642 - (void)setPlayerViewController:(AVPlayerViewController *)playerViewController;
643 - (void)setPlayerController:(AVPlayerController *)playerController;
644 @property (nonatomic, retain) CALayer* videoSublayer;
645 @end
646
647 @implementation WebAVVideoLayer
648 {
649     RetainPtr<WebAVPlayerController> _avPlayerController;
650     RetainPtr<AVPlayerViewController> _avPlayerViewController;
651     RetainPtr<CALayer> _videoSublayer;
652     AVVideoLayerGravity _videoLayerGravity;
653 }
654
655 +(WebAVVideoLayer *)videoLayer
656 {
657     return [[[WebAVVideoLayer alloc] init] autorelease];
658 }
659
660 - (instancetype)init
661 {
662     self = [super init];
663     if (self) {
664         [self setMasksToBounds:YES];
665         [self setVideoLayerGravity:AVVideoLayerGravityResizeAspect];
666     }
667     return self;
668 }
669
670 - (void)dealloc
671 {
672     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
673     [super dealloc];
674 }
675
676 - (void)setPlayerController:(AVPlayerController *)playerController
677 {
678     ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]);
679     _avPlayerController = (WebAVPlayerController *)playerController;
680 }
681
682 - (void)setPlayerViewController:(AVPlayerViewController *)playerViewController
683 {
684     _avPlayerViewController = playerViewController;
685 }
686
687 - (void)setVideoSublayer:(CALayer *)videoSublayer
688 {
689     _videoSublayer = videoSublayer;
690     [self addSublayer:videoSublayer];
691 }
692
693 - (CALayer*)videoSublayer
694 {
695     return _videoSublayer.get();
696 }
697
698 - (void)setBounds:(CGRect)bounds
699 {
700     [super setBounds:bounds];
701
702     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))];
703     [_videoSublayer setBounds:bounds];
704 }
705
706 - (void)setVideoLayerGravity:(AVVideoLayerGravity)videoLayerGravity
707 {
708     _videoLayerGravity = videoLayerGravity;
709     
710     if (![_avPlayerController delegate])
711         return;
712
713     WebCore::WebVideoFullscreenModel::VideoGravity gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect;
714     if (videoLayerGravity == AVVideoLayerGravityResize)
715         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResize;
716     if (videoLayerGravity == AVVideoLayerGravityResizeAspect)
717         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect;
718     else if (videoLayerGravity == AVVideoLayerGravityResizeAspectFill)
719         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill;
720     else
721         ASSERT_NOT_REACHED();
722     
723     [_avPlayerController delegate]->setVideoLayerGravity(gravity);
724 }
725
726 - (AVVideoLayerGravity)videoLayerGravity
727 {
728     return _videoLayerGravity;
729 }
730
731 - (void)enterOptimizedFullScreenModeRedirectingVideoToLayer:(CALayer *)layer
732 {
733     [_videoSublayer removeFromSuperlayer];
734     [layer addSublayer:_videoSublayer.get()];
735 }
736
737 - (void)leaveOptimizedFullScreenMode
738 {
739     [_videoSublayer removeFromSuperlayer];
740     [self addSublayer:_videoSublayer.get()];
741 }
742 @end
743
744 WebVideoFullscreenInterfaceAVKit::WebVideoFullscreenInterfaceAVKit()
745     : m_playerController(adoptNS([[WebAVPlayerController alloc] init]))
746 {
747     [m_playerController setFullscreenInterface:this];
748 }
749
750 void WebVideoFullscreenInterfaceAVKit::resetMediaState()
751 {
752     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
753     
754     dispatch_async(dispatch_get_main_queue(), [strongThis] {
755         if (!strongThis->m_playerController) {
756             strongThis->m_playerController = adoptNS([[WebAVPlayerController alloc] init]);
757             [strongThis->m_playerController setDelegate:strongThis->m_videoFullscreenModel];
758             [strongThis->m_playerController setFullscreenInterface:strongThis.get()];
759             
760         } else
761             [strongThis->m_playerController resetState];
762     });
763 }
764
765 void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenModel(WebVideoFullscreenModel* model)
766 {
767     m_videoFullscreenModel = model;
768     [m_playerController setDelegate:m_videoFullscreenModel];
769 }
770
771 void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenChangeObserver(WebVideoFullscreenChangeObserver* observer)
772 {
773     m_fullscreenChangeObserver = observer;
774 }
775
776 void WebVideoFullscreenInterfaceAVKit::setDuration(double duration)
777 {
778     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
779     
780     dispatch_async(dispatch_get_main_queue(), [strongThis, duration] {
781         WebAVPlayerController* playerController = strongThis->m_playerController.get();
782
783         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=127017 use correct values instead of duration for all these
784         playerController.contentDuration = duration;
785         playerController.maxTime = duration;
786         playerController.contentDurationWithinEndTimes = duration;
787
788         // FIXME: we take this as an indication that playback is ready.
789         playerController.canPlay = YES;
790         playerController.canPause = YES;
791         playerController.canTogglePlayback = YES;
792         playerController.hasEnabledAudio = YES;
793         playerController.canSeek = YES;
794         playerController.minTime = 0;
795         playerController.status = AVPlayerControllerStatusReadyToPlay;
796     });
797 }
798
799 void WebVideoFullscreenInterfaceAVKit::setCurrentTime(double currentTime, double anchorTime)
800 {
801     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
802     
803     dispatch_async(dispatch_get_main_queue(), [strongThis, currentTime, anchorTime] {
804         NSTimeInterval anchorTimeStamp = ![strongThis->m_playerController rate] ? NAN : anchorTime;
805         AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime
806             anchorTimeStamp:anchorTimeStamp rate:0];
807         [strongThis->m_playerController setTiming:timing];
808     });
809 }
810
811 void WebVideoFullscreenInterfaceAVKit::setBufferedTime(double bufferedTime)
812 {
813     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
814
815     dispatch_async(dispatch_get_main_queue(), [strongThis, bufferedTime] {
816         WebAVPlayerController* playerController = strongThis->m_playerController.get();
817         double duration = playerController.contentDuration;
818         double normalizedBufferedTime;
819         if (!duration)
820             normalizedBufferedTime = 0;
821         else
822             normalizedBufferedTime = bufferedTime / duration;
823         playerController.loadedTimeRanges = @[@0, @(normalizedBufferedTime)];
824     });
825 }
826
827 void WebVideoFullscreenInterfaceAVKit::setRate(bool isPlaying, float playbackRate)
828 {
829     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
830     
831     dispatch_async(dispatch_get_main_queue(), [strongThis, isPlaying, playbackRate] {
832         [strongThis->m_playerController setRate:isPlaying ? playbackRate : 0.];
833     });
834 }
835
836 void WebVideoFullscreenInterfaceAVKit::setVideoDimensions(bool hasVideo, float width, float height)
837 {
838     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
839     
840     dispatch_async(dispatch_get_main_queue(), [strongThis, hasVideo, width, height] {
841         [strongThis->m_playerController setHasEnabledVideo:hasVideo];
842         [strongThis->m_playerController setContentDimensions:CGSizeMake(width, height)];
843     });
844 }
845
846 void WebVideoFullscreenInterfaceAVKit::setSeekableRanges(const TimeRanges& timeRanges)
847 {
848     RetainPtr<NSMutableArray> seekableRanges = adoptNS([[NSMutableArray alloc] init]);
849     ExceptionCode exceptionCode;
850
851     for (unsigned i = 0; i < timeRanges.length(); i++) {
852         double start = timeRanges.start(i, exceptionCode);
853         double end = timeRanges.end(i, exceptionCode);
854         
855         CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000));
856         [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]];
857     }
858     
859     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
860     
861     dispatch_async(dispatch_get_main_queue(), [strongThis, seekableRanges] {
862         [strongThis->m_playerController setSeekableTimeRanges:seekableRanges.get()];
863     });
864 }
865
866 void WebVideoFullscreenInterfaceAVKit::setCanPlayFastReverse(bool canPlayFastReverse)
867 {
868     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
869     
870     dispatch_async(dispatch_get_main_queue(), [strongThis, canPlayFastReverse] {
871         [strongThis->m_playerController setCanScanBackward:canPlayFastReverse];
872     });
873 }
874
875 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<String>& options)
876 {
877     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
878     for (auto& name : options) {
879         RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]);
880         [webOption setLocalizedDisplayName:name];
881         [webOptions addObject:webOption.get()];
882     }
883     return webOptions;
884 }
885
886 void WebVideoFullscreenInterfaceAVKit::setAudioMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
887 {
888     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
889     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
890     
891     dispatch_async(dispatch_get_main_queue(), [webOptions, strongThis, selectedIndex] {
892         [strongThis->m_playerController setAudioMediaSelectionOptions:webOptions.get()];
893         if (selectedIndex < [webOptions count])
894             [strongThis->m_playerController setCurrentAudioMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
895     });
896 }
897
898 void WebVideoFullscreenInterfaceAVKit::setLegibleMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
899 {
900     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
901     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
902
903     dispatch_async(dispatch_get_main_queue(), [webOptions, strongThis, selectedIndex] {
904         [strongThis->m_playerController setLegibleMediaSelectionOptions:webOptions.get()];
905         if (selectedIndex < [webOptions count])
906             [strongThis->m_playerController setCurrentLegibleMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
907     });
908 }
909
910 void WebVideoFullscreenInterfaceAVKit::setExternalPlayback(bool enabled, ExternalPlaybackTargetType targetType, String localizedDeviceName)
911 {
912     AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone;
913     if (targetType == TargetTypeAirPlay)
914         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay;
915     else if (targetType == TargetTypeTVOut)
916         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut;
917
918     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
919
920     dispatch_async(dispatch_get_main_queue(), [strongThis, enabled, localizedDeviceName, externalPlaybackType] {
921         WebAVPlayerController* playerController = strongThis->m_playerController.get();
922         playerController.externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName;
923         playerController.externalPlaybackType = externalPlaybackType;
924         playerController.externalPlaybackActive = enabled;
925         [strongThis->m_videoLayerContainer.get() setHidden:enabled];
926     });
927 }
928
929 void WebVideoFullscreenInterfaceAVKit::setupFullscreen(PlatformLayer& videoLayer, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElement::VideoFullscreenMode mode, bool allowOptimizedFullscreen)
930 {
931     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
932
933     ASSERT(mode != HTMLMediaElement::VideoFullscreenModeNone);
934     m_videoLayer = &videoLayer;
935
936     m_mode = mode;
937
938     dispatch_async(dispatch_get_main_queue(), [strongThis, &videoLayer, initialRect, parentView, mode, allowOptimizedFullscreen] {
939         strongThis->setupFullscreenInternal(videoLayer, initialRect, parentView, mode, allowOptimizedFullscreen);
940     });
941 }
942
943 void WebVideoFullscreenInterfaceAVKit::setupFullscreenInternal(PlatformLayer& videoLayer, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElement::VideoFullscreenMode mode, bool allowOptimizedFullscreen)
944 {
945     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::setupFullscreenInternal(%p)", this);
946     UNUSED_PARAM(videoLayer);
947     UNUSED_PARAM(mode);
948
949     m_allowOptimizedFullscreen = allowOptimizedFullscreen;
950
951     [CATransaction begin];
952     [CATransaction setDisableActions:YES];
953     m_parentView = parentView;
954     m_parentWindow = parentView.window;
955
956     if (!applicationIsAdSheet()) {
957         m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
958         [m_window setBackgroundColor:[getUIColorClass() clearColor]];
959         m_viewController = adoptNS([allocUIViewControllerInstance() init]);
960         [[m_viewController view] setFrame:[m_window bounds]];
961         [m_window setRootViewController:m_viewController.get()];
962         [m_window makeKeyAndVisible];
963     }
964
965     [m_videoLayer removeFromSuperlayer];
966
967     m_layerHostWrapper = adoptNS([[WebCALayerHostWrapper alloc] init]);
968     [m_layerHostWrapper setModel:m_videoFullscreenModel];
969     [m_layerHostWrapper setVideoSublayer:m_videoLayer.get()];
970
971     m_videoLayerContainer = [WebAVVideoLayer videoLayer];
972     [m_videoLayerContainer setHidden:[m_playerController isExternalPlaybackActive]];
973     [m_videoLayerContainer setVideoSublayer:m_layerHostWrapper.get()];
974
975     CGSize videoSize = [m_playerController contentDimensions];
976     CGRect videoRect = CGRectMake(0, 0, videoSize.width, videoSize.height);
977     [m_videoLayerContainer setVideoRect:videoRect];
978     if (m_videoFullscreenModel)
979         m_videoFullscreenModel->setVideoLayerFrame(videoRect);
980
981     m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithVideoLayer:m_videoLayerContainer.get()]);
982     [m_playerViewController setShowsPlaybackControls:NO];
983     [m_playerViewController setPlayerController:(AVPlayerController *)m_playerController.get()];
984     [m_playerViewController setDelegate:m_playerController.get()];
985     [m_playerViewController setAllowsOptimizedFullscreen:allowOptimizedFullscreen];
986
987     [m_videoLayerContainer setPlayerViewController:m_playerViewController.get()];
988
989     if (m_viewController) {
990         [m_viewController addChildViewController:m_playerViewController.get()];
991         [[m_viewController view] addSubview:[m_playerViewController view]];
992         [m_playerViewController view].frame = [parentView convertRect:initialRect toView:nil];
993     } else {
994         [parentView addSubview:[m_playerViewController view]];
995         [m_playerViewController view].frame = initialRect;
996     }
997
998     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
999     [[m_playerViewController view] setNeedsLayout];
1000     [[m_playerViewController view] layoutIfNeeded];
1001
1002     [CATransaction commit];
1003
1004     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1005     WebThreadRun([strongThis] {
1006         if (strongThis->m_fullscreenChangeObserver)
1007             strongThis->m_fullscreenChangeObserver->didSetupFullscreen();
1008     });
1009 }
1010
1011 void WebVideoFullscreenInterfaceAVKit::enterFullscreen()
1012 {
1013     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreen(%p)", this);
1014
1015     m_exitCompleted = false;
1016     m_exitRequested = false;
1017     m_enterRequested = true;
1018
1019     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1020     dispatch_async(dispatch_get_main_queue(), [strongThis] {
1021         [strongThis->m_videoLayerContainer setBackgroundColor:[[getUIColorClass() blackColor] CGColor]];
1022         if (strongThis->mode() == HTMLMediaElement::VideoFullscreenModeOptimized)
1023             strongThis->enterFullscreenOptimized();
1024         else if (strongThis->mode() == HTMLMediaElement::VideoFullscreenModeStandard)
1025             strongThis->enterFullscreenStandard();
1026         else
1027             ASSERT_NOT_REACHED();
1028     });
1029 }
1030
1031 void WebVideoFullscreenInterfaceAVKit::enterFullscreenOptimized()
1032 {
1033     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1034
1035     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenOptimized(%p)", this);
1036     [m_playerViewController startOptimizedFullscreen];
1037 }
1038
1039 void WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard()
1040 {
1041     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
1042     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1043     [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, strongThis] (BOOL succeeded, NSError*) {
1044         UNUSED_PARAM(succeeded);
1045         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
1046         [m_playerViewController setShowsPlaybackControls:YES];
1047
1048         WebThreadRun([this, strongThis] {
1049             if (m_fullscreenChangeObserver)
1050                 m_fullscreenChangeObserver->didEnterFullscreen();
1051         });
1052     }];
1053 }
1054
1055 void WebVideoFullscreenInterfaceAVKit::exitFullscreen(const WebCore::IntRect& finalRect)
1056 {
1057     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1058
1059     m_exitRequested = true;
1060     if (m_exitCompleted) {
1061         WebThreadRun([strongThis] {
1062             if (strongThis->m_fullscreenChangeObserver)
1063                 strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1064         });
1065         return;
1066     }
1067
1068     dispatch_async(dispatch_get_main_queue(), [strongThis, finalRect] {
1069         strongThis->exitFullscreenInternal(finalRect);
1070     });
1071 }
1072
1073 void WebVideoFullscreenInterfaceAVKit::exitFullscreenInternal(const WebCore::IntRect& finalRect)
1074 {
1075     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::exitFullscreenInternal(%p)", this);
1076     [m_playerViewController setShowsPlaybackControls:NO];
1077     if (m_viewController)
1078         [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:nil];
1079     else
1080         [m_playerViewController view].frame = finalRect;
1081
1082     if ([m_videoLayerContainer videoLayerGravity] != AVVideoLayerGravityResizeAspect)
1083         [m_videoLayerContainer setVideoLayerGravity:AVVideoLayerGravityResizeAspect];
1084     [[m_playerViewController view] layoutIfNeeded];
1085
1086
1087     if (isMode(HTMLMediaElement::VideoFullscreenModeOptimized)) {
1088         [m_window setHidden:NO];
1089         [m_playerViewController stopOptimizedFullscreen];
1090     } else if (isMode(HTMLMediaElement::VideoFullscreenModeOptimized | HTMLMediaElement::VideoFullscreenModeStandard)) {
1091         RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1092         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[strongThis] (BOOL, NSError*) {
1093             [strongThis->m_window setHidden:NO];
1094             [strongThis->m_playerViewController stopOptimizedFullscreen];
1095         }];
1096     } else if (isMode(HTMLMediaElement::VideoFullscreenModeStandard)) {
1097         RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1098         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis] (BOOL, NSError*) {
1099             strongThis->m_exitCompleted = true;
1100
1101             [CATransaction begin];
1102             [CATransaction setDisableActions:YES];
1103             [strongThis->m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1104             [[strongThis->m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1105             [CATransaction commit];
1106
1107             WebThreadRun([strongThis] {
1108                 if (strongThis->m_fullscreenChangeObserver)
1109                     strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1110             });
1111         }];
1112     };
1113 }
1114
1115 @interface UIApplication ()
1116 -(void)_setStatusBarOrientation:(UIInterfaceOrientation)o;
1117 @end
1118
1119 @interface UIWindow ()
1120 -(UIInterfaceOrientation)interfaceOrientation;
1121 @end
1122
1123 void WebVideoFullscreenInterfaceAVKit::cleanupFullscreen()
1124 {
1125     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1126     
1127     dispatch_async(dispatch_get_main_queue(), [strongThis] {
1128         strongThis->cleanupFullscreenInternal();
1129     });
1130 }
1131
1132 void WebVideoFullscreenInterfaceAVKit::cleanupFullscreenInternal()
1133 {
1134     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::cleanupFullscreenInternal(%p)", this);
1135     if (m_window) {
1136         [m_window setHidden:YES];
1137         [m_window setRootViewController:nil];
1138         if (m_parentWindow)
1139             [[getUIApplicationClass() sharedApplication] _setStatusBarOrientation:[m_parentWindow interfaceOrientation]];
1140     }
1141     
1142     [m_playerController setDelegate:nil];
1143     [m_playerController setFullscreenInterface:nil];
1144     
1145     [m_playerViewController setDelegate:nil];
1146     [m_playerViewController setPlayerController:nil];
1147     
1148     if (hasMode(HTMLMediaElement::VideoFullscreenModeOptimized))
1149         [m_playerViewController stopOptimizedFullscreen];
1150     if (hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1151         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL, NSError *) { }];
1152     
1153     [[m_playerViewController view] removeFromSuperview];
1154     if (m_viewController)
1155         [m_playerViewController removeFromParentViewController];
1156     
1157     [m_videoLayer removeFromSuperlayer];
1158     [m_videoLayerContainer removeFromSuperlayer];
1159     [m_videoLayerContainer setPlayerViewController:nil];
1160     [[m_viewController view] removeFromSuperview];
1161
1162     [m_layerHostWrapper setModel:nullptr];
1163
1164     m_layerHostWrapper = nil;
1165     m_videoLayer = nil;
1166     m_videoLayerContainer = nil;
1167     m_playerViewController = nil;
1168     m_playerController = nil;
1169     m_viewController = nil;
1170     m_window = nil;
1171     m_parentView = nil;
1172     m_parentWindow = nil;
1173     
1174     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1175     WebThreadRun([strongThis] {
1176         if (strongThis->m_fullscreenChangeObserver)
1177             strongThis->m_fullscreenChangeObserver->didCleanupFullscreen();
1178         strongThis->m_enterRequested = false;
1179     });
1180 }
1181
1182 void WebVideoFullscreenInterfaceAVKit::invalidate()
1183 {
1184     m_videoFullscreenModel = nil;
1185     m_fullscreenChangeObserver = nil;
1186     
1187     cleanupFullscreenInternal();
1188 }
1189
1190 void WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
1191 {
1192     if (!m_enterRequested)
1193         return;
1194     
1195     if (hasMode(HTMLMediaElement::VideoFullscreenModeOptimized))
1196         return;
1197     
1198     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
1199
1200     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1201     dispatch_async(dispatch_get_main_queue(), [strongThis] {
1202         [strongThis->m_window setHidden:YES];
1203     });
1204
1205     if (m_videoFullscreenModel && !m_exitRequested) {
1206         m_videoFullscreenModel->pause();
1207         m_videoFullscreenModel->requestExitFullscreen();
1208     }
1209 }
1210
1211 void WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
1212 {
1213     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
1214     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1215     dispatch_async(dispatch_get_main_queue(), [strongThis, visible, inlineRect] {
1216         if (strongThis->m_prepareToInlineCallback) {
1217             
1218             if (strongThis->m_viewController)
1219                 [strongThis->m_playerViewController view].frame = [strongThis->m_parentView convertRect:inlineRect toView:nil];
1220             else
1221                 [strongThis->m_playerViewController view].frame = inlineRect;
1222
1223             std::function<void(bool)> callback = WTF::move(strongThis->m_prepareToInlineCallback);
1224             callback(visible);
1225         }
1226     });
1227 }
1228
1229 bool WebVideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoOptimized() const
1230 {
1231     return [m_playerController isPlaying] && m_mode == HTMLMediaElement::VideoFullscreenModeStandard && wkIsOptimizedFullscreenSupported();
1232 }
1233
1234 void WebVideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(std::function<void(bool)> callback)
1235 {
1236     m_prepareToInlineCallback = callback;
1237     if (m_fullscreenChangeObserver)
1238         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
1239 }
1240
1241 void WebVideoFullscreenInterfaceAVKit::willStartOptimizedFullscreen()
1242 {
1243     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartOptimizedFullscreen(%p)", this);
1244     setMode(HTMLMediaElement::VideoFullscreenModeOptimized);
1245
1246     if (!hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1247         return;
1248
1249     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1250     fullscreenMayReturnToInline([strongThis](bool visible) {
1251         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartOptimizedFullscreen - lambda(%p) - visible(%s)", strongThis.get(), boolString(visible));
1252
1253         if (!visible) {
1254             [strongThis->m_window setHidden:YES];
1255             return;
1256         }
1257
1258         [[strongThis->m_playerViewController view] layoutIfNeeded];
1259
1260         [strongThis->m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis] (BOOL completed, NSError*) {
1261             if (!completed)
1262                 return;
1263             strongThis->clearMode(HTMLMediaElement::VideoFullscreenModeStandard);
1264             [strongThis->m_window setHidden:YES];
1265         }];
1266     });
1267 }
1268
1269 void WebVideoFullscreenInterfaceAVKit::didStartOptimizedFullscreen()
1270 {
1271     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStartOptimizedFullscreen(%p)", this);
1272     [m_playerViewController setShowsPlaybackControls:YES];
1273
1274     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1275     WebThreadRun([strongThis] {
1276         [strongThis->m_window setHidden:YES];
1277         if (strongThis->m_fullscreenChangeObserver)
1278             strongThis->m_fullscreenChangeObserver->didEnterFullscreen();
1279     });
1280 }
1281
1282 void WebVideoFullscreenInterfaceAVKit::willStopOptimizedFullscreen()
1283 {
1284     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStopOptimizedFullscreen(%p)", this);
1285     [m_window setHidden:NO];
1286
1287     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1288     WebThreadRun([strongThis] {
1289         if (strongThis->m_videoFullscreenModel)
1290             strongThis->m_videoFullscreenModel->requestExitFullscreen();
1291     });
1292 }
1293
1294 void WebVideoFullscreenInterfaceAVKit::didStopOptimizedFullscreen()
1295 {
1296     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStopOptimizedFullscreen(%p)", this);
1297     if (hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1298         return;
1299
1300     m_exitCompleted = true;
1301
1302     [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1303     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1304
1305     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1306     WebThreadRun([strongThis] {
1307         strongThis->clearMode(HTMLMediaElement::VideoFullscreenModeOptimized);
1308         [strongThis->m_window setHidden:YES];
1309         if (strongThis->m_fullscreenChangeObserver)
1310             strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1311     });
1312 }
1313
1314 void WebVideoFullscreenInterfaceAVKit::willCancelOptimizedFullscreen()
1315 {
1316     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willCancelOptimizedFullscreen(%p)", this);
1317     [m_window setHidden:NO];
1318     [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1319     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1320
1321     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1322     WebThreadRun([strongThis] {
1323         if (strongThis->m_videoFullscreenModel)
1324             strongThis->m_videoFullscreenModel->requestExitFullscreen();
1325     });
1326 }
1327
1328 void WebVideoFullscreenInterfaceAVKit::didCancelOptimizedFullscreen()
1329 {
1330     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didCancelOptimizedFullscreen(%p)", this);
1331     if (hasMode(HTMLMediaElement::VideoFullscreenModeStandard))
1332         return;
1333
1334     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1335     WebThreadRun([strongThis] {
1336         strongThis->clearMode(HTMLMediaElement::VideoFullscreenModeOptimized);
1337         [strongThis->m_window setHidden:YES];
1338         if (strongThis->m_fullscreenChangeObserver)
1339             strongThis->m_fullscreenChangeObserver->didExitFullscreen();
1340     });
1341 }
1342
1343 void WebVideoFullscreenInterfaceAVKit::prepareForOptimizedFullscreenStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
1344 {
1345     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForOptimizedFullscreenStopWithCompletionHandler(%p)", this);
1346     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1347     RetainPtr<id> strongCompletionHandler = adoptNS([completionHandler copy]);
1348     fullscreenMayReturnToInline([strongThis, strongCompletionHandler](bool restored)  {
1349         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForOptimizedFullscreenStopWithCompletionHandler lambda(%p) - restored(%s)", strongThis.get(), boolString(restored));
1350         void (^completionHandler)(BOOL restored) = strongCompletionHandler.get();
1351         completionHandler(restored);
1352     });
1353 }
1354
1355 void WebVideoFullscreenInterfaceAVKit::setMode(HTMLMediaElement::VideoFullscreenMode mode)
1356 {
1357     HTMLMediaElement::VideoFullscreenMode newMode = m_mode | mode;
1358     if (m_mode == newMode)
1359         return;
1360
1361     m_mode = newMode;
1362     if (m_videoFullscreenModel)
1363         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1364 }
1365
1366 void WebVideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElement::VideoFullscreenMode mode)
1367 {
1368     HTMLMediaElement::VideoFullscreenMode newMode = m_mode & ~mode;
1369     if (m_mode == newMode)
1370         return;
1371
1372     m_mode = newMode;
1373     if (m_videoFullscreenModel)
1374         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1375 }
1376
1377 #endif