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