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