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