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