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