71842b4612282cf32d39db1595301a6a3f701ab9
[WebKit-https.git] / Source / WebCore / platform / ios / WebVideoFullscreenInterfaceAVKit.mm
1 /*
2  * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26
27 #import "config.h"
28
29 #if PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED > 80200 && HAVE(AVKIT)
30
31 #import "WebVideoFullscreenInterfaceAVKit.h"
32
33 #import "AVKitSPI.h"
34 #import "GeometryUtilities.h"
35 #import "Logging.h"
36 #import "RuntimeApplicationChecksIOS.h"
37 #import "TimeRanges.h"
38 #import "WebCoreSystemInterface.h"
39 #import "WebVideoFullscreenModel.h"
40 #import <AVFoundation/AVTime.h>
41 #import <UIKit/UIKit.h>
42 #import <wtf/RetainPtr.h>
43 #import <wtf/text/CString.h>
44 #import <wtf/text/WTFString.h>
45
46 using namespace WebCore;
47
48 // Soft-linking headers must be included last since they #define functions, constants, etc.
49 #import "CoreMediaSoftLink.h"
50
51 SOFT_LINK_FRAMEWORK(AVFoundation)
52 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
53
54 SOFT_LINK_FRAMEWORK(AVKit)
55 SOFT_LINK_CLASS(AVKit, AVPlayerController)
56 SOFT_LINK_CLASS(AVKit, AVPlayerViewController)
57 SOFT_LINK_CLASS(AVKit, AVValueTiming)
58
59 SOFT_LINK_FRAMEWORK(UIKit)
60 SOFT_LINK_CLASS(UIKit, UIApplication)
61 SOFT_LINK_CLASS(UIKit, UIScreen)
62 SOFT_LINK_CLASS(UIKit, UIWindow)
63 SOFT_LINK_CLASS(UIKit, UIView)
64 SOFT_LINK_CLASS(UIKit, UIViewController)
65 SOFT_LINK_CLASS(UIKit, UIColor)
66
67 #if !LOG_DISABLED
68 static const char* boolString(bool val)
69 {
70     return val ? "true" : "false";
71 }
72 #endif
73
74
75 @class WebAVMediaSelectionOption;
76
77 @interface WebAVPlayerController : NSObject <AVPlayerViewControllerDelegate>
78 {
79     WebAVMediaSelectionOption *_currentAudioMediaSelectionOption;
80     WebAVMediaSelectionOption *_currentLegibleMediaSelectionOption;
81 }
82
83 -(void)resetState;
84
85 @property (retain) AVPlayerController* playerControllerProxy;
86 @property (assign) WebVideoFullscreenModel* delegate;
87 @property (assign) WebVideoFullscreenInterfaceAVKit* fullscreenInterface;
88
89 @property (readonly) BOOL canScanForward;
90 @property BOOL canScanBackward;
91 @property (readonly) BOOL canSeekToBeginning;
92 @property (readonly) BOOL canSeekToEnd;
93
94 @property BOOL canPlay;
95 @property (getter=isPlaying) BOOL playing;
96 @property BOOL canPause;
97 @property BOOL canTogglePlayback;
98 @property double rate;
99 @property BOOL canSeek;
100 @property NSTimeInterval contentDuration;
101 @property NSSize contentDimensions;
102 @property BOOL hasEnabledAudio;
103 @property BOOL hasEnabledVideo;
104 @property NSTimeInterval minTime;
105 @property NSTimeInterval maxTime;
106 @property NSTimeInterval contentDurationWithinEndTimes;
107 @property (retain) NSArray *loadedTimeRanges;
108 @property AVPlayerControllerStatus status;
109 @property (retain) AVValueTiming *timing;
110 @property (retain) NSArray *seekableTimeRanges;
111
112 @property (readonly) BOOL hasMediaSelectionOptions;
113 @property (readonly) BOOL hasAudioMediaSelectionOptions;
114 @property (retain) NSArray *audioMediaSelectionOptions;
115 @property (retain) WebAVMediaSelectionOption *currentAudioMediaSelectionOption;
116 @property (readonly) BOOL hasLegibleMediaSelectionOptions;
117 @property (retain) NSArray *legibleMediaSelectionOptions;
118 @property (retain) WebAVMediaSelectionOption *currentLegibleMediaSelectionOption;
119
120 @property (readonly, getter=isPlayingOnExternalScreen) BOOL playingOnExternalScreen;
121 @property (getter=isExternalPlaybackActive) BOOL externalPlaybackActive;
122 @property AVPlayerControllerExternalPlaybackType externalPlaybackType;
123 @property (retain) NSString *externalPlaybackAirPlayDeviceLocalizedName;
124
125 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason;
126 @end
127
128 @implementation WebAVPlayerController
129
130 - (instancetype)init
131 {
132     if (!(self = [super init]))
133         return self;
134     
135     initAVPlayerController();
136     self.playerControllerProxy = [[allocAVPlayerControllerInstance() init] autorelease];
137     return self;
138 }
139
140 - (void)dealloc
141 {
142     [_playerControllerProxy release];
143     [_loadedTimeRanges release];
144     [_seekableTimeRanges release];
145     [_timing release];
146     [_audioMediaSelectionOptions release];
147     [_legibleMediaSelectionOptions release];
148     [_currentAudioMediaSelectionOption release];
149     [_currentLegibleMediaSelectionOption release];
150     [super dealloc];
151 }
152
153 -(void)resetState {
154     self.contentDuration = 0;
155     self.maxTime = 0;
156     self.contentDurationWithinEndTimes = 0;
157     self.loadedTimeRanges = @[];
158     
159     self.canPlay = NO;
160     self.canPause = NO;
161     self.canTogglePlayback = NO;
162     self.hasEnabledAudio = NO;
163     self.canSeek = NO;
164     self.minTime = 0;
165     self.status = AVPlayerControllerStatusUnknown;
166     
167     self.timing = nil;
168     self.rate = 0;
169     
170     self.hasEnabledVideo = NO;
171     self.contentDimensions = CGSizeMake(0, 0);
172     
173     self.seekableTimeRanges = [NSMutableArray array];
174     
175     self.canScanBackward = NO;
176     
177     self.audioMediaSelectionOptions = nil;
178     self.currentAudioMediaSelectionOption = nil;
179     
180     self.legibleMediaSelectionOptions = nil;
181     self.currentLegibleMediaSelectionOption = nil;
182 }
183
184 - (AVPlayer*) player {
185     return nil;
186 }
187
188 - (id)forwardingTargetForSelector:(SEL)selector
189 {
190     UNUSED_PARAM(selector);
191     return self.playerControllerProxy;
192 }
193
194 - (void)playerViewControllerWillStartPictureInPicture:(AVPlayerViewController *)playerViewController
195 {
196     UNUSED_PARAM(playerViewController);
197     self.fullscreenInterface->willStartPictureInPicture();
198 }
199
200 - (void)playerViewControllerDidStartPictureInPicture:(AVPlayerViewController *)playerViewController
201 {
202     UNUSED_PARAM(playerViewController);
203     self.fullscreenInterface->didStartPictureInPicture();
204 }
205
206 - (void)playerViewControllerFailedToStartPictureInPicture:(AVPlayerViewController *)playerViewController withError:(NSError *)error
207 {
208     UNUSED_PARAM(playerViewController);
209     UNUSED_PARAM(error);
210     self.fullscreenInterface->failedToStartPictureInPicture();
211 }
212
213 - (void)playerViewControllerWillStopPictureInPicture:(AVPlayerViewController *)playerViewController
214 {
215     UNUSED_PARAM(playerViewController);
216     self.fullscreenInterface->willStopPictureInPicture();
217 }
218
219 - (void)playerViewControllerDidStopPictureInPicture:(AVPlayerViewController *)playerViewController
220 {
221     UNUSED_PARAM(playerViewController);
222     self.fullscreenInterface->didStopPictureInPicture();
223 }
224
225 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason
226 {
227     UNUSED_PARAM(playerViewController);
228     UNUSED_PARAM(reason);
229     if (!self.delegate)
230         return YES;
231     
232     if (reason == AVPlayerViewControllerExitFullScreenReasonDoneButtonTapped || reason == AVPlayerViewControllerExitFullScreenReasonRemoteControlStopEventReceived)
233         self.delegate->pause();
234     
235     self.delegate->requestExitFullscreen();
236     return NO;
237 }
238
239 - (void)playerViewController:(AVPlayerViewController *)playerViewController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler
240 {
241     UNUSED_PARAM(playerViewController);
242     self.fullscreenInterface->prepareForPictureInPictureStopWithCompletionHandler(completionHandler);
243 }
244
245 - (void)play:(id)sender
246 {
247     UNUSED_PARAM(sender);
248     if (!self.delegate)
249         return;
250     self.delegate->play();
251 }
252
253 - (void)pause:(id)sender
254 {
255     UNUSED_PARAM(sender);
256     if (!self.delegate)
257         return;
258     self.delegate->pause();
259 }
260
261 - (void)togglePlayback:(id)sender
262 {
263     UNUSED_PARAM(sender);
264     if (!self.delegate)
265         return;
266     self.delegate->togglePlayState();
267 }
268
269 - (void)togglePlaybackEvenWhenInBackground:(id)sender
270 {
271     [self togglePlayback:sender];
272 }
273
274 - (BOOL)isPlaying
275 {
276     return [self rate] != 0;
277 }
278
279 - (void)setPlaying:(BOOL)playing
280 {
281     if (!self.delegate)
282         return;
283     if (playing)
284         self.delegate->play();
285     else
286         self.delegate->pause();
287 }
288
289 + (NSSet *)keyPathsForValuesAffectingPlaying
290 {
291     return [NSSet setWithObject:@"rate"];
292 }
293
294 - (void)beginScrubbing:(id)sender
295 {
296     UNUSED_PARAM(sender);
297     if (!self.delegate)
298         return;
299     self.delegate->beginScrubbing();
300 }
301
302 - (void)endScrubbing:(id)sender
303 {
304     UNUSED_PARAM(sender);
305     if (!self.delegate)
306         return;
307     self.delegate->endScrubbing();
308 }
309
310 - (void)seekToTime:(NSTimeInterval)time
311 {
312     if (!self.delegate)
313         return;
314     self.delegate->fastSeek(time);
315 }
316
317 - (BOOL)hasLiveStreamingContent
318 {
319     if ([self status] == AVPlayerControllerStatusReadyToPlay)
320         return [self contentDuration] == std::numeric_limits<float>::infinity();
321     return NO;
322 }
323
324 + (NSSet *)keyPathsForValuesAffectingHasLiveStreamingContent
325 {
326     return [NSSet setWithObjects:@"contentDuration", @"status", nil];
327 }
328
329 - (void)skipBackwardThirtySeconds:(id)sender
330 {
331     UNUSED_PARAM(sender);
332     BOOL isTimeWithinSeekableTimeRanges = NO;
333     CMTime currentTime = CMTimeMakeWithSeconds([[self timing] currentValue], 1000);
334     CMTime thirtySecondsBeforeCurrentTime = CMTimeSubtract(currentTime, CMTimeMake(30, 1));
335     
336     for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) {
337         if (CMTimeRangeContainsTime([seekableTimeRangeValue CMTimeRangeValue], thirtySecondsBeforeCurrentTime)) {
338             isTimeWithinSeekableTimeRanges = YES;
339             break;
340         }
341     }
342     
343     if (isTimeWithinSeekableTimeRanges)
344         [self seekToTime:CMTimeGetSeconds(thirtySecondsBeforeCurrentTime)];
345 }
346
347 - (void)gotoEndOfSeekableRanges:(id)sender
348 {
349     UNUSED_PARAM(sender);
350     NSTimeInterval timeAtEndOfSeekableTimeRanges = NAN;
351     
352     for (NSValue *seekableTimeRangeValue in [self seekableTimeRanges]) {
353         CMTimeRange seekableTimeRange = [seekableTimeRangeValue CMTimeRangeValue];
354         NSTimeInterval endOfSeekableTimeRange = CMTimeGetSeconds(CMTimeRangeGetEnd(seekableTimeRange));
355         if (isnan(timeAtEndOfSeekableTimeRanges) || endOfSeekableTimeRange > timeAtEndOfSeekableTimeRanges)
356             timeAtEndOfSeekableTimeRanges = endOfSeekableTimeRange;
357     }
358     
359     if (!isnan(timeAtEndOfSeekableTimeRanges))
360         [self seekToTime:timeAtEndOfSeekableTimeRanges];
361 }
362
363 - (BOOL)canScanForward
364 {
365     return [self canPlay];
366 }
367
368 + (NSSet *)keyPathsForValuesAffectingCanScanForward
369 {
370     return [NSSet setWithObject:@"canPlay"];
371 }
372
373 - (void)beginScanningForward:(id)sender
374 {
375     UNUSED_PARAM(sender);
376     if (!self.delegate)
377         return;
378     self.delegate->beginScanningForward();
379 }
380
381 - (void)endScanningForward:(id)sender
382 {
383     UNUSED_PARAM(sender);
384     if (!self.delegate)
385         return;
386     self.delegate->endScanning();
387 }
388
389 - (void)beginScanningBackward:(id)sender
390 {
391     UNUSED_PARAM(sender);
392     if (!self.delegate)
393         return;
394     self.delegate->beginScanningBackward();
395 }
396
397 - (void)endScanningBackward:(id)sender
398 {
399     UNUSED_PARAM(sender);
400     if (!self.delegate)
401         return;
402     self.delegate->endScanning();
403 }
404
405 - (BOOL)canSeekToBeginning
406 {
407     CMTime minimumTime = kCMTimeIndefinite;
408
409     for (NSValue *value in [self seekableTimeRanges])
410         minimumTime = CMTimeMinimum([value CMTimeRangeValue].start, minimumTime);
411
412     return CMTIME_IS_NUMERIC(minimumTime);
413 }
414
415 + (NSSet *)keyPathsForValuesAffectingCanSeekToBeginning
416 {
417     return [NSSet setWithObject:@"seekableTimeRanges"];
418 }
419
420 - (void)seekToBeginning:(id)sender
421 {
422     UNUSED_PARAM(sender);
423     if (!self.delegate)
424         return;
425     self.delegate->seekToTime(-INFINITY);
426 }
427
428 - (void)seekChapterBackward:(id)sender
429 {
430     [self seekToBeginning:sender];
431 }
432
433 - (BOOL)canSeekToEnd
434 {
435     CMTime maximumTime = kCMTimeIndefinite;
436
437     for (NSValue *value in [self seekableTimeRanges])
438         maximumTime = CMTimeMaximum(CMTimeRangeGetEnd([value CMTimeRangeValue]), maximumTime);
439
440     return CMTIME_IS_NUMERIC(maximumTime);
441 }
442
443 + (NSSet *)keyPathsForValuesAffectingCanSeekToEnd
444 {
445     return [NSSet setWithObject:@"seekableTimeRanges"];
446 }
447
448 - (void)seekToEnd:(id)sender
449 {
450     UNUSED_PARAM(sender);
451     if (!self.delegate)
452         return;
453     self.delegate->seekToTime(INFINITY);
454 }
455
456 - (void)seekChapterForward:(id)sender
457 {
458     [self seekToEnd:sender];
459 }
460
461 - (BOOL)hasMediaSelectionOptions
462 {
463     return [self hasAudioMediaSelectionOptions] || [self hasLegibleMediaSelectionOptions];
464 }
465
466 + (NSSet *)keyPathsForValuesAffectingHasMediaSelectionOptions
467 {
468     return [NSSet setWithObjects:@"hasAudioMediaSelectionOptions", @"hasLegibleMediaSelectionOptions", nil];
469 }
470
471 - (BOOL)hasAudioMediaSelectionOptions
472 {
473     return [[self audioMediaSelectionOptions] count] > 1;
474 }
475
476 + (NSSet *)keyPathsForValuesAffectingHasAudioMediaSelectionOptions
477 {
478     return [NSSet setWithObject:@"audioMediaSelectionOptions"];
479 }
480
481 - (BOOL)hasLegibleMediaSelectionOptions
482 {
483     const NSUInteger numDefaultLegibleOptions = 2;
484     return [[self legibleMediaSelectionOptions] count] > numDefaultLegibleOptions;
485 }
486
487 + (NSSet *)keyPathsForValuesAffectingHasLegibleMediaSelectionOptions
488 {
489     return [NSSet setWithObject:@"legibleMediaSelectionOptions"];
490 }
491
492 - (WebAVMediaSelectionOption *)currentAudioMediaSelectionOption
493 {
494     return _currentAudioMediaSelectionOption;
495 }
496
497 - (void)setCurrentAudioMediaSelectionOption:(WebAVMediaSelectionOption *)option
498 {
499     if (option == _currentAudioMediaSelectionOption)
500         return;
501     
502     [_currentAudioMediaSelectionOption release];
503     _currentAudioMediaSelectionOption = [option retain];
504     
505     if (!self.delegate)
506         return;
507     
508     NSInteger index = NSNotFound;
509     
510     if (option && self.audioMediaSelectionOptions)
511         index = [self.audioMediaSelectionOptions indexOfObject:option];
512     
513     self.delegate->selectAudioMediaOption(index != NSNotFound ? index : UINT64_MAX);
514 }
515
516 - (WebAVMediaSelectionOption *)currentLegibleMediaSelectionOption
517 {
518     return _currentLegibleMediaSelectionOption;
519 }
520
521 - (void)setCurrentLegibleMediaSelectionOption:(WebAVMediaSelectionOption *)option
522 {
523     if (option == _currentLegibleMediaSelectionOption)
524         return;
525     
526     [_currentLegibleMediaSelectionOption release];
527     _currentLegibleMediaSelectionOption = [option retain];
528     
529     if (!self.delegate)
530         return;
531     
532     NSInteger index = NSNotFound;
533     
534     if (option && self.legibleMediaSelectionOptions)
535         index = [self.legibleMediaSelectionOptions indexOfObject:option];
536     
537     self.delegate->selectLegibleMediaOption(index != NSNotFound ? index : UINT64_MAX);
538 }
539
540 - (BOOL)isPlayingOnExternalScreen
541 {
542     return [self isExternalPlaybackActive];
543 }
544
545 + (NSSet *)keyPathsForValuesAffectingPlayingOnExternalScreen
546 {
547     return [NSSet setWithObjects:@"externalPlaybackActive", nil];
548 }
549
550 - (BOOL)isPictureInPicturePossible
551 {
552     return self.fullscreenInterface->allowsPictureInPicturePlayback();
553 }
554 @end
555
556 @interface WebAVMediaSelectionOption : NSObject
557 @property (retain) NSString *localizedDisplayName;
558 @end
559
560 @implementation WebAVMediaSelectionOption
561 @end
562
563
564 @interface WebCALayerHostWrapper : CALayer
565 @property (assign) WebVideoFullscreenModel* model;
566 @end
567
568 @implementation WebCALayerHostWrapper {
569     RetainPtr<CALayer> _videoSublayer;
570 }
571
572 - (void)setVideoSublayer:(CALayer*)videoSublayer
573 {
574     _videoSublayer = videoSublayer;
575     [self addSublayer:videoSublayer];
576 }
577
578 - (CALayer*)videoSublayer
579 {
580     return _videoSublayer.get();
581 }
582
583 - (void)setBounds:(CGRect)bounds
584 {
585     if (CGRectEqualToRect(bounds, self.bounds))
586         return;
587
588     [super setBounds:bounds];
589
590     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))];
591
592     if (!self.model)
593         return;
594
595     FloatRect videoFrame = self.model->videoLayerFrame();
596     FloatRect targetFrame;
597     switch (self.model->videoLayerGravity()) {
598     case WebCore::WebVideoFullscreenModel::VideoGravityResize:
599         targetFrame = bounds;
600         break;
601     case WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect:
602         targetFrame = largestRectWithAspectRatioInsideRect(videoFrame.size().aspectRatio(), bounds);
603         break;
604     case WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill:
605         targetFrame = smallestRectWithAspectRatioAroundRect(videoFrame.size().aspectRatio(), bounds);
606         break;
607     }
608     CATransform3D transform = CATransform3DMakeScale(targetFrame.width() / videoFrame.width(), targetFrame.height() / videoFrame.height(), 1);
609     [_videoSublayer setSublayerTransform:transform];
610
611     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
612     [self performSelector:@selector(resolveBounds) withObject:nil afterDelay:[CATransaction animationDuration] + 0.1];
613 }
614
615 - (void)resolveBounds
616 {
617     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
618     if (!self.model)
619         return;
620
621     [CATransaction begin];
622     [CATransaction setAnimationDuration:0];
623
624     [_videoSublayer setSublayerTransform:CATransform3DIdentity];
625     self.model->setVideoLayerFrame([self bounds]);
626     
627     [CATransaction commit];
628 }
629 @end
630
631 @interface WebAVVideoLayer : CALayer <AVVideoLayer>
632 +(WebAVVideoLayer *)videoLayer;
633 @property (nonatomic) AVVideoLayerGravity videoLayerGravity;
634 @property (nonatomic, getter = isReadyForDisplay) BOOL readyForDisplay;
635 @property (nonatomic) CGRect videoRect;
636 - (void)setPlayerViewController:(AVPlayerViewController *)playerViewController;
637 - (void)setPlayerController:(AVPlayerController *)playerController;
638 @property (nonatomic, retain) CALayer* videoSublayer;
639 @end
640
641 @implementation WebAVVideoLayer
642 {
643     RetainPtr<WebAVPlayerController> _avPlayerController;
644     RetainPtr<AVPlayerViewController> _avPlayerViewController;
645     RetainPtr<CALayer> _videoSublayer;
646     AVVideoLayerGravity _videoLayerGravity;
647 }
648
649 +(WebAVVideoLayer *)videoLayer
650 {
651     return [[[WebAVVideoLayer alloc] init] autorelease];
652 }
653
654 - (instancetype)init
655 {
656     self = [super init];
657     if (self) {
658         [self setMasksToBounds:YES];
659         [self setVideoLayerGravity:AVVideoLayerGravityResizeAspect];
660     }
661     return self;
662 }
663
664 - (void)dealloc
665 {
666     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
667     [super dealloc];
668 }
669
670 - (void)setPlayerController:(AVPlayerController *)playerController
671 {
672     ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]);
673     _avPlayerController = (WebAVPlayerController *)playerController;
674 }
675
676 - (void)setPlayerViewController:(AVPlayerViewController *)playerViewController
677 {
678     _avPlayerViewController = playerViewController;
679 }
680
681 - (void)setVideoSublayer:(CALayer *)videoSublayer
682 {
683     _videoSublayer = videoSublayer;
684     [self addSublayer:videoSublayer];
685 }
686
687 - (CALayer*)videoSublayer
688 {
689     return _videoSublayer.get();
690 }
691
692 - (void)setBounds:(CGRect)bounds
693 {
694     [super setBounds:bounds];
695
696     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))];
697     [_videoSublayer setBounds:bounds];
698 }
699
700 - (void)setVideoLayerGravity:(AVVideoLayerGravity)videoLayerGravity
701 {
702     _videoLayerGravity = videoLayerGravity;
703     
704     if (![_avPlayerController delegate])
705         return;
706
707     WebCore::WebVideoFullscreenModel::VideoGravity gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect;
708     if (videoLayerGravity == AVVideoLayerGravityResize)
709         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResize;
710     if (videoLayerGravity == AVVideoLayerGravityResizeAspect)
711         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspect;
712     else if (videoLayerGravity == AVVideoLayerGravityResizeAspectFill)
713         gravity = WebCore::WebVideoFullscreenModel::VideoGravityResizeAspectFill;
714     else
715         ASSERT_NOT_REACHED();
716     
717     [_avPlayerController delegate]->setVideoLayerGravity(gravity);
718 }
719
720 - (AVVideoLayerGravity)videoLayerGravity
721 {
722     return _videoLayerGravity;
723 }
724
725 - (void)enterOptimizedFullScreenModeRedirectingVideoToLayer:(CALayer *)layer
726 {
727     [_videoSublayer removeFromSuperlayer];
728     [layer addSublayer:_videoSublayer.get()];
729 }
730
731 - (void)leaveOptimizedFullScreenMode
732 {
733     [_videoSublayer removeFromSuperlayer];
734     [self addSublayer:_videoSublayer.get()];
735 }
736 @end
737
738 WebVideoFullscreenInterfaceAVKit::WebVideoFullscreenInterfaceAVKit()
739     : m_playerController(adoptNS([[WebAVPlayerController alloc] init]))
740 {
741     [m_playerController setFullscreenInterface:this];
742 }
743
744 void WebVideoFullscreenInterfaceAVKit::resetMediaState()
745 {
746     if (!m_playerController) {
747         m_playerController = adoptNS([[WebAVPlayerController alloc] init]);
748         [m_playerController setDelegate:m_videoFullscreenModel];
749         [m_playerController setFullscreenInterface:this];
750         
751     } else
752         [m_playerController resetState];
753 }
754
755 void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenModel(WebVideoFullscreenModel* model)
756 {
757     m_videoFullscreenModel = model;
758     [m_playerController setDelegate:m_videoFullscreenModel];
759 }
760
761 void WebVideoFullscreenInterfaceAVKit::setWebVideoFullscreenChangeObserver(WebVideoFullscreenChangeObserver* observer)
762 {
763     m_fullscreenChangeObserver = observer;
764 }
765
766 void WebVideoFullscreenInterfaceAVKit::setDuration(double duration)
767 {
768     WebAVPlayerController* playerController = m_playerController.get();
769
770     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=127017 use correct values instead of duration for all these
771     playerController.contentDuration = duration;
772     playerController.maxTime = duration;
773     playerController.contentDurationWithinEndTimes = duration;
774
775     // FIXME: we take this as an indication that playback is ready.
776     playerController.canPlay = YES;
777     playerController.canPause = YES;
778     playerController.canTogglePlayback = YES;
779     playerController.hasEnabledAudio = YES;
780     playerController.canSeek = YES;
781     playerController.minTime = 0;
782     playerController.status = AVPlayerControllerStatusReadyToPlay;
783 }
784
785 void WebVideoFullscreenInterfaceAVKit::setCurrentTime(double currentTime, double anchorTime)
786 {
787     NSTimeInterval anchorTimeStamp = ![m_playerController rate] ? NAN : anchorTime;
788     AVValueTiming *timing = [getAVValueTimingClass() valueTimingWithAnchorValue:currentTime
789         anchorTimeStamp:anchorTimeStamp rate:0];
790     [m_playerController setTiming:timing];
791 }
792
793 void WebVideoFullscreenInterfaceAVKit::setBufferedTime(double bufferedTime)
794 {
795     WebAVPlayerController* playerController = m_playerController.get();
796     double duration = playerController.contentDuration;
797     double normalizedBufferedTime;
798     if (!duration)
799         normalizedBufferedTime = 0;
800     else
801         normalizedBufferedTime = bufferedTime / duration;
802     playerController.loadedTimeRanges = @[@0, @(normalizedBufferedTime)];
803 }
804
805 void WebVideoFullscreenInterfaceAVKit::setRate(bool isPlaying, float playbackRate)
806 {
807     [m_playerController setRate:isPlaying ? playbackRate : 0.];
808 }
809
810 void WebVideoFullscreenInterfaceAVKit::setVideoDimensions(bool hasVideo, float width, float height)
811 {
812     [m_playerController setHasEnabledVideo:hasVideo];
813     [m_playerController setContentDimensions:CGSizeMake(width, height)];
814 }
815
816 void WebVideoFullscreenInterfaceAVKit::setSeekableRanges(const TimeRanges& timeRanges)
817 {
818     RetainPtr<NSMutableArray> seekableRanges = adoptNS([[NSMutableArray alloc] init]);
819     ExceptionCode exceptionCode;
820
821     for (unsigned i = 0; i < timeRanges.length(); i++) {
822         double start = timeRanges.start(i, exceptionCode);
823         double end = timeRanges.end(i, exceptionCode);
824         
825         CMTimeRange range = CMTimeRangeMake(CMTimeMakeWithSeconds(start, 1000), CMTimeMakeWithSeconds(end-start, 1000));
826         [seekableRanges addObject:[NSValue valueWithCMTimeRange:range]];
827     }
828     
829     [m_playerController setSeekableTimeRanges:seekableRanges.get()];
830 }
831
832 void WebVideoFullscreenInterfaceAVKit::setCanPlayFastReverse(bool canPlayFastReverse)
833 {
834     [m_playerController setCanScanBackward:canPlayFastReverse];
835 }
836
837 static RetainPtr<NSMutableArray> mediaSelectionOptions(const Vector<String>& options)
838 {
839     RetainPtr<NSMutableArray> webOptions = adoptNS([[NSMutableArray alloc] initWithCapacity:options.size()]);
840     for (auto& name : options) {
841         RetainPtr<WebAVMediaSelectionOption> webOption = adoptNS([[WebAVMediaSelectionOption alloc] init]);
842         [webOption setLocalizedDisplayName:name];
843         [webOptions addObject:webOption.get()];
844     }
845     return webOptions;
846 }
847
848 void WebVideoFullscreenInterfaceAVKit::setAudioMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
849 {
850     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
851     [m_playerController setAudioMediaSelectionOptions:webOptions.get()];
852     if (selectedIndex < [webOptions count])
853         [m_playerController setCurrentAudioMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
854 }
855
856 void WebVideoFullscreenInterfaceAVKit::setLegibleMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
857 {
858     RetainPtr<NSMutableArray> webOptions = mediaSelectionOptions(options);
859     [m_playerController setLegibleMediaSelectionOptions:webOptions.get()];
860     if (selectedIndex < [webOptions count])
861         [m_playerController setCurrentLegibleMediaSelectionOption:[webOptions objectAtIndex:static_cast<NSUInteger>(selectedIndex)]];
862 }
863
864 void WebVideoFullscreenInterfaceAVKit::setExternalPlayback(bool enabled, ExternalPlaybackTargetType targetType, String localizedDeviceName)
865 {
866     AVPlayerControllerExternalPlaybackType externalPlaybackType = AVPlayerControllerExternalPlaybackTypeNone;
867     if (targetType == TargetTypeAirPlay)
868         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeAirPlay;
869     else if (targetType == TargetTypeTVOut)
870         externalPlaybackType = AVPlayerControllerExternalPlaybackTypeTVOut;
871
872     WebAVPlayerController* playerController = m_playerController.get();
873     playerController.externalPlaybackAirPlayDeviceLocalizedName = localizedDeviceName;
874     playerController.externalPlaybackType = externalPlaybackType;
875     playerController.externalPlaybackActive = enabled;
876     [m_videoLayerContainer.get() setHidden:enabled];
877 }
878
879 @interface UIWindow ()
880 -(BOOL)_isHostedInAnotherProcess;
881 @end
882
883 void WebVideoFullscreenInterfaceAVKit::setupFullscreen(PlatformLayer& videoLayer, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback)
884 {
885     ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
886     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
887
888     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
889
890     [CATransaction begin];
891     [CATransaction setDisableActions:YES];
892     m_videoLayer = &videoLayer;
893     m_mode = mode;
894     m_parentView = parentView;
895     m_parentWindow = parentView.window;
896
897     if (![[parentView window] _isHostedInAnotherProcess]) {
898         m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
899         [m_window setBackgroundColor:[getUIColorClass() clearColor]];
900         m_viewController = adoptNS([allocUIViewControllerInstance() init]);
901         [[m_viewController view] setFrame:[m_window bounds]];
902         [m_window setRootViewController:m_viewController.get()];
903         [m_window makeKeyAndVisible];
904     }
905
906     [m_videoLayer removeFromSuperlayer];
907
908     m_layerHostWrapper = adoptNS([[WebCALayerHostWrapper alloc] init]);
909     [m_layerHostWrapper setModel:m_videoFullscreenModel];
910     [m_layerHostWrapper setVideoSublayer:m_videoLayer.get()];
911
912     m_videoLayerContainer = [WebAVVideoLayer videoLayer];
913     [m_videoLayerContainer setHidden:[m_playerController isExternalPlaybackActive]];
914     [m_videoLayerContainer setVideoSublayer:m_layerHostWrapper.get()];
915
916     CGSize videoSize = [m_playerController contentDimensions];
917     CGRect videoRect = CGRectMake(0, 0, videoSize.width, videoSize.height);
918     [m_videoLayerContainer setVideoRect:videoRect];
919     if (m_videoFullscreenModel)
920         m_videoFullscreenModel->setVideoLayerFrame(videoRect);
921
922     m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithVideoLayer:m_videoLayerContainer.get()]);
923     [m_playerViewController setShowsPlaybackControls:NO];
924     [m_playerViewController setPlayerController:(AVPlayerController *)m_playerController.get()];
925     [m_playerViewController setDelegate:m_playerController.get()];
926     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
927
928     [m_videoLayerContainer setPlayerViewController:m_playerViewController.get()];
929
930     if (m_viewController) {
931         [m_viewController addChildViewController:m_playerViewController.get()];
932         [[m_viewController view] addSubview:[m_playerViewController view]];
933     } else
934         [parentView.window addSubview:[m_playerViewController view]];
935
936     [m_playerViewController view].frame = [parentView convertRect:initialRect toView:nil];
937
938     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
939     [[m_playerViewController view] setNeedsLayout];
940     [[m_playerViewController view] layoutIfNeeded];
941
942     [CATransaction commit];
943
944     if (m_fullscreenChangeObserver)
945         m_fullscreenChangeObserver->didSetupFullscreen();
946 }
947
948 void WebVideoFullscreenInterfaceAVKit::enterFullscreen()
949 {
950     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreen(%p)", this);
951
952     m_exitCompleted = false;
953     m_exitRequested = false;
954     m_enterRequested = true;
955
956     [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() blackColor] CGColor]];
957     if (mode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
958         enterPictureInPicture();
959     else if (mode() == HTMLMediaElementEnums::VideoFullscreenModeStandard)
960         enterFullscreenStandard();
961     else
962         ASSERT_NOT_REACHED();
963 }
964
965 void WebVideoFullscreenInterfaceAVKit::enterPictureInPicture()
966 {
967     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterPictureInPicture(%p)", this);
968     
969     if ([m_playerViewController isPictureInPicturePossible])
970         [m_playerViewController startPictureInPicture];
971     else
972         failedToStartPictureInPicture();
973 }
974
975 void WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard()
976 {
977     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
978     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
979     [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, strongThis] (BOOL succeeded, NSError*) {
980         UNUSED_PARAM(succeeded);
981         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
982         [m_playerViewController setShowsPlaybackControls:YES];
983
984         if (m_fullscreenChangeObserver)
985             m_fullscreenChangeObserver->didEnterFullscreen();
986     }];
987 }
988
989 void WebVideoFullscreenInterfaceAVKit::exitFullscreen(const WebCore::IntRect& finalRect)
990 {
991     m_exitRequested = true;
992     if (m_exitCompleted) {
993         if (m_fullscreenChangeObserver)
994             m_fullscreenChangeObserver->didExitFullscreen();
995         return;
996     }
997     
998     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::exitFullscreen(%p)", this);
999     [m_playerViewController setShowsPlaybackControls:NO];
1000     if (m_viewController)
1001         [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:nil];
1002     else
1003         [m_playerViewController view].frame = finalRect;
1004
1005     if ([m_videoLayerContainer videoLayerGravity] != AVVideoLayerGravityResizeAspect)
1006         [m_videoLayerContainer setVideoLayerGravity:AVVideoLayerGravityResizeAspect];
1007     [[m_playerViewController view] layoutIfNeeded];
1008
1009     if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
1010         [m_window setHidden:NO];
1011         [m_playerViewController stopPictureInPicture];
1012     } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture | HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
1013         RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1014         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[strongThis, this] (BOOL, NSError*) {
1015             [m_window setHidden:NO];
1016             [m_playerViewController stopPictureInPicture];
1017         }];
1018     } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
1019         RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1020         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis, this] (BOOL, NSError*) {
1021             m_exitCompleted = true;
1022
1023             [CATransaction begin];
1024             [CATransaction setDisableActions:YES];
1025             [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1026             [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1027             [CATransaction commit];
1028
1029             if (m_fullscreenChangeObserver)
1030                 m_fullscreenChangeObserver->didExitFullscreen();
1031         }];
1032     };
1033 }
1034
1035 @interface UIApplication ()
1036 -(void)_setStatusBarOrientation:(UIInterfaceOrientation)o;
1037 @end
1038
1039 @interface UIWindow ()
1040 -(UIInterfaceOrientation)interfaceOrientation;
1041 @end
1042
1043 void WebVideoFullscreenInterfaceAVKit::cleanupFullscreen()
1044 {
1045     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
1046     if (m_window) {
1047         [m_window setHidden:YES];
1048         [m_window setRootViewController:nil];
1049         if (m_parentWindow)
1050             [[getUIApplicationClass() sharedApplication] _setStatusBarOrientation:[m_parentWindow interfaceOrientation]];
1051     }
1052     
1053     [m_playerController setDelegate:nil];
1054     [m_playerController setFullscreenInterface:nil];
1055     
1056     [m_playerViewController setDelegate:nil];
1057     [m_playerViewController setPlayerController:nil];
1058     
1059     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
1060         [m_playerViewController stopPictureInPicture];
1061     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
1062         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL, NSError *) { }];
1063     
1064     [[m_playerViewController view] removeFromSuperview];
1065     if (m_viewController)
1066         [m_playerViewController removeFromParentViewController];
1067     
1068     [m_videoLayer removeFromSuperlayer];
1069     [m_videoLayerContainer removeFromSuperlayer];
1070     [m_videoLayerContainer setPlayerViewController:nil];
1071     [[m_viewController view] removeFromSuperview];
1072
1073     [m_layerHostWrapper setModel:nullptr];
1074
1075     m_layerHostWrapper = nil;
1076     m_videoLayer = nil;
1077     m_videoLayerContainer = nil;
1078     m_playerViewController = nil;
1079     m_playerController = nil;
1080     m_viewController = nil;
1081     m_window = nil;
1082     m_parentView = nil;
1083     m_parentWindow = nil;
1084     
1085     if (m_fullscreenChangeObserver)
1086         m_fullscreenChangeObserver->didCleanupFullscreen();
1087
1088     m_enterRequested = false;
1089 }
1090
1091 void WebVideoFullscreenInterfaceAVKit::invalidate()
1092 {
1093     m_videoFullscreenModel = nil;
1094     m_fullscreenChangeObserver = nil;
1095     
1096     cleanupFullscreen();
1097 }
1098
1099 void WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
1100 {
1101     if (!m_enterRequested)
1102         return;
1103     
1104     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
1105         return;
1106     
1107     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
1108
1109     [m_window setHidden:YES];
1110     [[m_playerViewController view] setHidden:YES];
1111
1112     if (m_videoFullscreenModel && !m_exitRequested) {
1113         m_videoFullscreenModel->pause();
1114         m_videoFullscreenModel->requestExitFullscreen();
1115     }
1116 }
1117
1118 void WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
1119 {
1120     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
1121     if (m_prepareToInlineCallback) {
1122         
1123         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:nil];
1124
1125         std::function<void(bool)> callback = WTF::move(m_prepareToInlineCallback);
1126         callback(visible);
1127     }
1128 }
1129
1130 bool WebVideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
1131 {
1132     return [m_playerController isPlaying] && m_mode == HTMLMediaElementEnums::VideoFullscreenModeStandard && wkIsOptimizedFullscreenSupported();
1133 }
1134
1135 void WebVideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(std::function<void(bool)> callback)
1136 {
1137     m_prepareToInlineCallback = callback;
1138     if (m_fullscreenChangeObserver)
1139         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
1140 }
1141
1142 void WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture()
1143 {
1144     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
1145     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1146
1147     if (!hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
1148         return;
1149
1150     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1151     fullscreenMayReturnToInline([strongThis, this](bool visible) {
1152         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture - lambda(%p) - visible(%s)", this, boolString(visible));
1153
1154         if (!visible) {
1155             [m_window setHidden:YES];
1156             [[m_playerViewController view] setHidden:YES];
1157             return;
1158         }
1159
1160         [[m_playerViewController view] layoutIfNeeded];
1161
1162         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[strongThis, this] (BOOL completed, NSError*) {
1163             if (!completed)
1164                 return;
1165             clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1166             [m_window setHidden:YES];
1167             [[m_playerViewController view] setHidden:YES];
1168         }];
1169     });
1170 }
1171
1172 void WebVideoFullscreenInterfaceAVKit::didStartPictureInPicture()
1173 {
1174     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
1175     [m_playerViewController setShowsPlaybackControls:YES];
1176     [m_window setHidden:YES];
1177     [[m_playerViewController view] setHidden:YES];
1178
1179     if (m_fullscreenChangeObserver)
1180         m_fullscreenChangeObserver->didEnterFullscreen();
1181 }
1182
1183 void WebVideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
1184 {
1185     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
1186     [m_playerViewController setShowsPlaybackControls:YES];
1187
1188     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
1189         return;
1190
1191     m_exitCompleted = true;
1192
1193     if (m_fullscreenChangeObserver)
1194         m_fullscreenChangeObserver->didEnterFullscreen();
1195
1196     if (m_videoFullscreenModel)
1197         m_videoFullscreenModel->requestExitFullscreen();
1198 }
1199
1200 void WebVideoFullscreenInterfaceAVKit::willStopPictureInPicture()
1201 {
1202     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
1203     [m_window setHidden:NO];
1204     [[m_playerViewController view] setHidden:NO];
1205
1206     if (m_videoFullscreenModel)
1207         m_videoFullscreenModel->requestExitFullscreen();
1208 }
1209
1210 void WebVideoFullscreenInterfaceAVKit::didStopPictureInPicture()
1211 {
1212     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
1213     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
1214         return;
1215
1216     m_exitCompleted = true;
1217
1218     [m_videoLayerContainer setBackgroundColor:[[getUIColorClass() clearColor] CGColor]];
1219     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
1220
1221     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1222     [m_window setHidden:YES];
1223     [[m_playerViewController view] setHidden:YES];
1224     
1225     if (m_fullscreenChangeObserver)
1226         m_fullscreenChangeObserver->didExitFullscreen();
1227 }
1228
1229 void WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
1230 {
1231     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
1232     RefPtr<WebVideoFullscreenInterfaceAVKit> strongThis(this);
1233     RetainPtr<id> strongCompletionHandler = adoptNS([completionHandler copy]);
1234     fullscreenMayReturnToInline([strongThis, strongCompletionHandler](bool restored)  {
1235         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", strongThis.get(), boolString(restored));
1236         void (^completionHandler)(BOOL restored) = strongCompletionHandler.get();
1237         completionHandler(restored);
1238     });
1239 }
1240
1241 void WebVideoFullscreenInterfaceAVKit::setMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1242 {
1243     HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode | mode;
1244     if (m_mode == newMode)
1245         return;
1246
1247     m_mode = newMode;
1248     if (m_videoFullscreenModel)
1249         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1250 }
1251
1252 void WebVideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1253 {
1254     HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode & ~mode;
1255     if (m_mode == newMode)
1256         return;
1257
1258     m_mode = newMode;
1259     if (m_videoFullscreenModel)
1260         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1261 }
1262
1263 #endif