Build fix for r228115, simple naming issue succeeded —> success.
[WebKit-https.git] / Source / WebCore / platform / ios / VideoFullscreenInterfaceAVKit.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 #import "config.h"
27
28 #if PLATFORM(IOS)
29 #import "VideoFullscreenInterfaceAVKit.h"
30
31 #if HAVE(AVKIT)
32
33 #import "GeometryUtilities.h"
34 #import "Logging.h"
35 #import "PlaybackSessionInterfaceAVKit.h"
36 #import "RuntimeApplicationChecks.h"
37 #import "TimeRanges.h"
38 #import "VideoFullscreenChangeObserver.h"
39 #import "VideoFullscreenModel.h"
40 #import "WebAVPlayerController.h"
41 #import <AVFoundation/AVTime.h>
42 #import <UIKit/UIKit.h>
43 #import <objc/message.h>
44 #import <objc/runtime.h>
45 #import <pal/spi/cocoa/AVKitSPI.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 <pal/cf/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_OPTIONAL(AVKit)
62 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPictureInPictureController)
63 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPlayerViewController)
64 SOFT_LINK_CLASS_OPTIONAL(AVKit, __AVPlayerLayerView)
65
66 SOFT_LINK_FRAMEWORK(UIKit)
67 SOFT_LINK_CLASS(UIKit, UIApplication)
68 SOFT_LINK_CLASS(UIKit, UIScreen)
69 SOFT_LINK_CLASS(UIKit, UIWindow)
70 SOFT_LINK_CLASS(UIKit, UIView)
71 SOFT_LINK_CLASS(UIKit, UIViewController)
72 SOFT_LINK_CLASS(UIKit, UIColor)
73 SOFT_LINK_CONSTANT(UIKit, UITextEffectsBeneathStatusBarWindowLevel, UIWindowLevel)
74
75 @interface UIWindow ()
76 - (BOOL)_isHostedInAnotherProcess;
77 @end
78
79 @interface UIViewController ()
80 @property (nonatomic, assign, setter=_setIgnoreAppSupportedOrientations:) BOOL _ignoreAppSupportedOrientations;
81 @end
82
83 static UIColor *clearUIColor()
84 {
85     return (UIColor *)[getUIColorClass() clearColor];
86 }
87
88 #if !LOG_DISABLED
89 static const char* boolString(bool val)
90 {
91     return val ? "true" : "false";
92 }
93 #endif
94
95 static const Seconds defaultWatchdogTimerInterval { 1_s };
96
97 @class WebAVMediaSelectionOption;
98
99 @interface WebAVPlayerViewControllerDelegate : NSObject <AVPlayerViewControllerDelegate_WebKitOnly> {
100     RefPtr<VideoFullscreenInterfaceAVKit> _fullscreenInterface;
101 }
102 @property (assign) VideoFullscreenInterfaceAVKit* fullscreenInterface;
103 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason;
104 @end
105
106 @implementation WebAVPlayerViewControllerDelegate
107 - (VideoFullscreenInterfaceAVKit*)fullscreenInterface
108 {
109     return _fullscreenInterface.get();
110 }
111
112 - (void)setFullscreenInterface:(VideoFullscreenInterfaceAVKit*)fullscreenInterface
113 {
114     _fullscreenInterface = fullscreenInterface;
115 }
116
117 - (void)playerViewControllerWillStartPictureInPicture:(AVPlayerViewController *)playerViewController
118 {
119     UNUSED_PARAM(playerViewController);
120     self.fullscreenInterface->willStartPictureInPicture();
121 }
122
123 - (void)playerViewControllerDidStartPictureInPicture:(AVPlayerViewController *)playerViewController
124 {
125     UNUSED_PARAM(playerViewController);
126     self.fullscreenInterface->didStartPictureInPicture();
127 }
128
129 - (void)playerViewControllerFailedToStartPictureInPicture:(AVPlayerViewController *)playerViewController withError:(NSError *)error
130 {
131     UNUSED_PARAM(playerViewController);
132     UNUSED_PARAM(error);
133     self.fullscreenInterface->failedToStartPictureInPicture();
134 }
135
136 - (void)playerViewControllerWillStopPictureInPicture:(AVPlayerViewController *)playerViewController
137 {
138     UNUSED_PARAM(playerViewController);
139     self.fullscreenInterface->willStopPictureInPicture();
140 }
141
142 - (void)playerViewControllerDidStopPictureInPicture:(AVPlayerViewController *)playerViewController
143 {
144     UNUSED_PARAM(playerViewController);
145     self.fullscreenInterface->didStopPictureInPicture();
146 }
147
148 static VideoFullscreenInterfaceAVKit::ExitFullScreenReason convertToExitFullScreenReason(AVPlayerViewControllerExitFullScreenReason reason)
149 {
150     switch (reason) {
151     case AVPlayerViewControllerExitFullScreenReasonDoneButtonTapped:
152         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::DoneButtonTapped;
153     case AVPlayerViewControllerExitFullScreenReasonFullScreenButtonTapped:
154         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::FullScreenButtonTapped;
155     case AVPlayerViewControllerExitFullScreenReasonPictureInPictureStarted:
156         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::PictureInPictureStarted;
157     case AVPlayerViewControllerExitFullScreenReasonPinchGestureHandled:
158         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::PinchGestureHandled;
159     case AVPlayerViewControllerExitFullScreenReasonRemoteControlStopEventReceived:
160         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::RemoteControlStopEventReceived;
161     }
162 }
163
164 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason
165 {
166     UNUSED_PARAM(playerViewController);
167     return self.fullscreenInterface->shouldExitFullscreenWithReason(convertToExitFullScreenReason(reason));
168 }
169
170 - (void)playerViewController:(AVPlayerViewController *)playerViewController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler
171 {
172     UNUSED_PARAM(playerViewController);
173     self.fullscreenInterface->prepareForPictureInPictureStopWithCompletionHandler(completionHandler);
174 }
175
176 #if ENABLE(FULLSCREEN_API)
177 - (BOOL)playerViewControllerShouldStartPictureInPictureFromInlineWhenEnteringBackground:(AVPlayerViewController *)playerViewController
178 {
179     UNUSED_PARAM(playerViewController);
180     return YES;
181 }
182 #endif
183
184 @end
185
186 @interface WebAVPlayerLayer : CALayer
187 @property (nonatomic, retain) NSString *videoGravity;
188 @property (nonatomic, getter=isReadyForDisplay) BOOL readyForDisplay;
189 @property (nonatomic, assign) VideoFullscreenInterfaceAVKit* fullscreenInterface;
190 @property (nonatomic, retain) AVPlayerController *playerController;
191 @property (nonatomic, retain) CALayer *videoSublayer;
192 @property (nonatomic, copy, nullable) NSDictionary *pixelBufferAttributes;
193 @property CGSize videoDimensions;
194 @property CGRect modelVideoLayerFrame;
195 @end
196
197 @implementation WebAVPlayerLayer {
198     RefPtr<VideoFullscreenInterfaceAVKit> _fullscreenInterface;
199     RetainPtr<WebAVPlayerController> _avPlayerController;
200     RetainPtr<CALayer> _videoSublayer;
201     RetainPtr<NSString> _videoGravity;
202 }
203
204 - (instancetype)init
205 {
206     self = [super init];
207     if (self) {
208         [self setMasksToBounds:YES];
209         _videoGravity = getAVLayerVideoGravityResizeAspect();
210     }
211     return self;
212 }
213
214 - (void)dealloc
215 {
216     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
217     [_pixelBufferAttributes release];
218     [super dealloc];
219 }
220
221 - (VideoFullscreenInterfaceAVKit*)fullscreenInterface
222 {
223     return _fullscreenInterface.get();
224 }
225
226 - (void)setFullscreenInterface:(VideoFullscreenInterfaceAVKit*)fullscreenInterface
227 {
228     _fullscreenInterface = fullscreenInterface;
229 }
230
231 - (AVPlayerController *)playerController
232 {
233     return (AVPlayerController *)_avPlayerController.get();
234 }
235
236 - (void)setPlayerController:(AVPlayerController *)playerController
237 {
238     ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]);
239     _avPlayerController = (WebAVPlayerController *)playerController;
240 }
241
242 - (void)setVideoSublayer:(CALayer *)videoSublayer
243 {
244     _videoSublayer = videoSublayer;
245 }
246
247 - (CALayer*)videoSublayer
248 {
249     return _videoSublayer.get();
250 }
251
252 - (void)layoutSublayers
253 {
254     if ([_videoSublayer superlayer] != self)
255         return;
256
257     if (![_avPlayerController delegate])
258         return;
259
260     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];
261
262     if (self.videoDimensions.height <= 0 || self.videoDimensions.width <= 0)
263         return;
264
265     FloatRect sourceVideoFrame;
266     FloatRect targetVideoFrame;
267     float videoAspectRatio = self.videoDimensions.width / self.videoDimensions.height;
268     
269     if ([getAVLayerVideoGravityResize() isEqualToString:self.videoGravity]) {
270         sourceVideoFrame = self.modelVideoLayerFrame;
271         targetVideoFrame = self.bounds;
272     } else if ([getAVLayerVideoGravityResizeAspect() isEqualToString:self.videoGravity]) {
273         sourceVideoFrame = largestRectWithAspectRatioInsideRect(videoAspectRatio, self.modelVideoLayerFrame);
274         targetVideoFrame = largestRectWithAspectRatioInsideRect(videoAspectRatio, self.bounds);
275     } else if ([getAVLayerVideoGravityResizeAspectFill() isEqualToString:self.videoGravity]) {
276         sourceVideoFrame = smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.modelVideoLayerFrame);
277         self.modelVideoLayerFrame = CGRectMake(0, 0, sourceVideoFrame.width(), sourceVideoFrame.height());
278         ASSERT(_fullscreenInterface->model());
279         _fullscreenInterface->model()->setVideoLayerFrame(self.modelVideoLayerFrame);
280         targetVideoFrame = smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.bounds);
281     } else
282         ASSERT_NOT_REACHED();
283
284     UIView *view = (UIView *)[_videoSublayer delegate];
285     CGAffineTransform transform = CGAffineTransformMakeScale(targetVideoFrame.width() / sourceVideoFrame.width(), targetVideoFrame.height() / sourceVideoFrame.height());
286     [view setTransform:transform];
287     
288     NSTimeInterval animationDuration = [CATransaction animationDuration];
289     dispatch_async(dispatch_get_main_queue(), ^{
290         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
291
292         [self performSelector:@selector(resolveBounds) withObject:nil afterDelay:animationDuration + 0.1];
293     });
294 }
295
296 - (void)resolveBounds
297 {
298     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
299     if (![_avPlayerController delegate])
300         return;
301     
302     if ([_videoSublayer superlayer] != self)
303         return;
304     
305     if (CGRectEqualToRect(self.modelVideoLayerFrame, [self bounds]) && CGAffineTransformIsIdentity([(UIView *)[_videoSublayer delegate] transform]))
306         return;
307     
308     [CATransaction begin];
309     [CATransaction setAnimationDuration:0];
310     [CATransaction setDisableActions:YES];
311     
312     if (!CGRectEqualToRect(self.modelVideoLayerFrame, [self bounds])) {
313         self.modelVideoLayerFrame = [self bounds];
314         ASSERT(_fullscreenInterface->model());
315         _fullscreenInterface->model()->setVideoLayerFrame(self.modelVideoLayerFrame);
316     }
317     [(UIView *)[_videoSublayer delegate] setTransform:CGAffineTransformIdentity];
318     
319     [CATransaction commit];
320 }
321
322 - (void)setVideoGravity:(NSString *)videoGravity
323 {
324     _videoGravity = videoGravity;
325     
326     if (![_avPlayerController delegate])
327         return;
328
329     WebCore::VideoFullscreenModel::VideoGravity gravity = WebCore::VideoFullscreenModel::VideoGravityResizeAspect;
330     if (videoGravity == getAVLayerVideoGravityResize())
331         gravity = WebCore::VideoFullscreenModel::VideoGravityResize;
332     if (videoGravity == getAVLayerVideoGravityResizeAspect())
333         gravity = WebCore::VideoFullscreenModel::VideoGravityResizeAspect;
334     else if (videoGravity == getAVLayerVideoGravityResizeAspectFill())
335         gravity = WebCore::VideoFullscreenModel::VideoGravityResizeAspectFill;
336     else
337         ASSERT_NOT_REACHED();
338     
339     ASSERT(_fullscreenInterface->model());
340     _fullscreenInterface->model()->setVideoLayerGravity(gravity);
341 }
342
343 - (NSString *)videoGravity
344 {
345     return _videoGravity.get();
346 }
347
348 - (CGRect)videoRect
349 {
350     if (self.videoDimensions.width <= 0 || self.videoDimensions.height <= 0)
351         return self.bounds;
352     
353     float videoAspectRatio = self.videoDimensions.width / self.videoDimensions.height;
354
355     if ([getAVLayerVideoGravityResizeAspect() isEqualToString:self.videoGravity])
356         return largestRectWithAspectRatioInsideRect(videoAspectRatio, self.bounds);
357     if ([getAVLayerVideoGravityResizeAspectFill() isEqualToString:self.videoGravity])
358         return smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.bounds);
359
360     return self.bounds;
361 }
362
363 + (NSSet *)keyPathsForValuesAffectingVideoRect
364 {
365     return [NSSet setWithObjects:@"videoDimensions", @"videoGravity", nil];
366 }
367
368 @end
369
370 @interface WebAVPictureInPicturePlayerLayerView : UIView
371 @end
372
373 static Class WebAVPictureInPicturePlayerLayerView_layerClass(id, SEL)
374 {
375     return [WebAVPlayerLayer class];
376 }
377
378 static WebAVPictureInPicturePlayerLayerView *allocWebAVPictureInPicturePlayerLayerViewInstance()
379 {
380     static Class theClass = nil;
381     static dispatch_once_t onceToken;
382     dispatch_once(&onceToken, ^{
383         theClass = objc_allocateClassPair(getUIViewClass(), "WebAVPictureInPicturePlayerLayerView", 0);
384         objc_registerClassPair(theClass);
385         Class metaClass = objc_getMetaClass("WebAVPictureInPicturePlayerLayerView");
386         class_addMethod(metaClass, @selector(layerClass), (IMP)WebAVPictureInPicturePlayerLayerView_layerClass, "@@:");
387     });
388     
389     return (WebAVPictureInPicturePlayerLayerView *)[theClass alloc];
390 }
391
392 @interface WebAVPlayerLayerView : __AVPlayerLayerView
393 @property (retain) UIView* videoView;
394 @end
395
396 static Class WebAVPlayerLayerView_layerClass(id, SEL)
397 {
398     return [WebAVPlayerLayer class];
399 }
400
401 static AVPlayerController *WebAVPlayerLayerView_playerController(id aSelf, SEL)
402 {
403     __AVPlayerLayerView *playerLayer = aSelf;
404     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayer playerLayer];
405     return [webAVPlayerLayer playerController];
406 }
407
408 static void WebAVPlayerLayerView_setPlayerController(id aSelf, SEL, AVPlayerController *playerController)
409 {
410     __AVPlayerLayerView *playerLayerView = aSelf;
411     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
412     [webAVPlayerLayer setPlayerController: playerController];
413 }
414
415 static UIView *WebAVPlayerLayerView_videoView(id aSelf, SEL)
416 {
417     __AVPlayerLayerView *playerLayer = aSelf;
418     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayer playerLayer];
419     CALayer* videoLayer = [webAVPlayerLayer videoSublayer];
420     if (!videoLayer || !videoLayer.delegate)
421         return nil;
422     ASSERT([[videoLayer delegate] isKindOfClass:getUIViewClass()]);
423     return (UIView *)[videoLayer delegate];
424 }
425
426 static void WebAVPlayerLayerView_setVideoView(id aSelf, SEL, UIView *videoView)
427 {
428     __AVPlayerLayerView *playerLayerView = aSelf;
429     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
430     [webAVPlayerLayer setVideoSublayer:[videoView layer]];
431 }
432
433 static void WebAVPlayerLayerView_startRoutingVideoToPictureInPicturePlayerLayerView(id aSelf, SEL)
434 {
435     WebAVPlayerLayerView *playerLayerView = aSelf;
436     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[playerLayerView pictureInPicturePlayerLayerView];
437
438     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
439     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
440     [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()];
441     [pipPlayerLayer setVideoSublayer:playerLayer.videoSublayer];
442     [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions];
443     [pipPlayerLayer setVideoGravity:playerLayer.videoGravity];
444     [pipPlayerLayer setModelVideoLayerFrame:playerLayer.modelVideoLayerFrame];
445     [pipPlayerLayer setPlayerController:playerLayer.playerController];
446     [pipPlayerLayer setFullscreenInterface:playerLayer.fullscreenInterface];
447     [pipView addSubview:playerLayerView.videoView];
448 }
449
450 static void WebAVPlayerLayerView_stopRoutingVideoToPictureInPicturePlayerLayerView(id aSelf, SEL)
451 {
452     WebAVPlayerLayerView *playerLayerView = aSelf;
453     if (UIView *videoView = playerLayerView.videoView)
454         [playerLayerView addSubview:videoView];
455     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[playerLayerView pictureInPicturePlayerLayerView];
456     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
457     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
458     [playerLayer setModelVideoLayerFrame:pipPlayerLayer.modelVideoLayerFrame];
459 }
460
461 static WebAVPictureInPicturePlayerLayerView *WebAVPlayerLayerView_pictureInPicturePlayerLayerView(id aSelf, SEL)
462 {
463     WebAVPlayerLayerView *playerLayerView = aSelf;
464     WebAVPictureInPicturePlayerLayerView *pipView = [playerLayerView valueForKey:@"_pictureInPicturePlayerLayerView"];
465     if (!pipView) {
466         pipView = [allocWebAVPictureInPicturePlayerLayerViewInstance() initWithFrame:CGRectZero];
467         [playerLayerView setValue:pipView forKey:@"_pictureInPicturePlayerLayerView"];
468     }
469     return pipView;
470 }
471
472 static void WebAVPlayerLayerView_dealloc(id aSelf, SEL)
473 {
474     WebAVPlayerLayerView *playerLayerView = aSelf;
475     RetainPtr<WebAVPictureInPicturePlayerLayerView> pipView = adoptNS([playerLayerView valueForKey:@"_pictureInPicturePlayerLayerView"]);
476     [playerLayerView setValue:nil forKey:@"_pictureInPicturePlayerLayerView"];
477     objc_super superClass { playerLayerView, get__AVPlayerLayerViewClass() };
478     auto super_dealloc = reinterpret_cast<void(*)(objc_super*, SEL)>(objc_msgSendSuper);
479     super_dealloc(&superClass, @selector(dealloc));
480 }
481
482 #pragma mark - Methods
483
484 static WebAVPlayerLayerView *allocWebAVPlayerLayerViewInstance()
485 {
486     static Class theClass = nil;
487     static dispatch_once_t onceToken;
488     dispatch_once(&onceToken, ^{
489         theClass = objc_allocateClassPair(get__AVPlayerLayerViewClass(), "WebAVPlayerLayerView", 0);
490         class_addMethod(theClass, @selector(dealloc), (IMP)WebAVPlayerLayerView_dealloc, "v@:");
491         class_addMethod(theClass, @selector(setPlayerController:), (IMP)WebAVPlayerLayerView_setPlayerController, "v@:@");
492         class_addMethod(theClass, @selector(playerController), (IMP)WebAVPlayerLayerView_playerController, "@@:");
493         class_addMethod(theClass, @selector(setVideoView:), (IMP)WebAVPlayerLayerView_setVideoView, "v@:@");
494         class_addMethod(theClass, @selector(videoView), (IMP)WebAVPlayerLayerView_videoView, "@@:");
495         class_addMethod(theClass, @selector(startRoutingVideoToPictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_startRoutingVideoToPictureInPicturePlayerLayerView, "v@:");
496         class_addMethod(theClass, @selector(stopRoutingVideoToPictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_stopRoutingVideoToPictureInPicturePlayerLayerView, "v@:");
497         class_addMethod(theClass, @selector(pictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_pictureInPicturePlayerLayerView, "@@:");
498         
499         class_addIvar(theClass, "_pictureInPicturePlayerLayerView", sizeof(WebAVPictureInPicturePlayerLayerView *), log2(sizeof(WebAVPictureInPicturePlayerLayerView *)), "@");
500         
501         objc_registerClassPair(theClass);
502         Class metaClass = objc_getMetaClass("WebAVPlayerLayerView");
503         class_addMethod(metaClass, @selector(layerClass), (IMP)WebAVPlayerLayerView_layerClass, "@@:");
504     });
505     return (WebAVPlayerLayerView *)[theClass alloc];
506 }
507
508 Ref<VideoFullscreenInterfaceAVKit> VideoFullscreenInterfaceAVKit::create(PlaybackSessionInterfaceAVKit& playbackSessionInterface)
509 {
510     Ref<VideoFullscreenInterfaceAVKit> interface = adoptRef(*new VideoFullscreenInterfaceAVKit(playbackSessionInterface));
511     [interface->m_playerViewControllerDelegate setFullscreenInterface:interface.ptr()];
512     return interface;
513 }
514
515 VideoFullscreenInterfaceAVKit::VideoFullscreenInterfaceAVKit(PlaybackSessionInterfaceAVKit& playbackSessionInterface)
516     : m_playbackSessionInterface(playbackSessionInterface)
517     , m_playerViewControllerDelegate(adoptNS([[WebAVPlayerViewControllerDelegate alloc] init]))
518     , m_watchdogTimer(RunLoop::main(), this, &VideoFullscreenInterfaceAVKit::watchdogTimerFired)
519 {
520 }
521
522 VideoFullscreenInterfaceAVKit::~VideoFullscreenInterfaceAVKit()
523 {
524     WebAVPlayerController* playerController = this->playerController();
525     if (playerController && playerController.externalPlaybackActive)
526         externalPlaybackChanged(false, PlaybackSessionModel::TargetTypeNone, "");
527     if (m_videoFullscreenModel)
528         m_videoFullscreenModel->removeClient(*this);
529 }
530
531 WebAVPlayerController *VideoFullscreenInterfaceAVKit::playerController() const
532 {
533     return m_playbackSessionInterface->playerController();
534 }
535
536 void VideoFullscreenInterfaceAVKit::setVideoFullscreenModel(VideoFullscreenModel* model)
537 {
538     if (m_videoFullscreenModel)
539         m_videoFullscreenModel->removeClient(*this);
540
541     m_videoFullscreenModel = model;
542
543     if (m_videoFullscreenModel)
544         m_videoFullscreenModel->addClient(*this);
545
546     hasVideoChanged(m_videoFullscreenModel ? m_videoFullscreenModel->hasVideo() : false);
547     videoDimensionsChanged(m_videoFullscreenModel ? m_videoFullscreenModel->videoDimensions() : FloatSize());
548 }
549
550 void VideoFullscreenInterfaceAVKit::setVideoFullscreenChangeObserver(VideoFullscreenChangeObserver* observer)
551 {
552     m_fullscreenChangeObserver = observer;
553 }
554
555 void VideoFullscreenInterfaceAVKit::hasVideoChanged(bool hasVideo)
556 {
557     [playerController() setHasEnabledVideo:hasVideo];
558     [playerController() setHasVideo:hasVideo];
559 }
560
561 void VideoFullscreenInterfaceAVKit::videoDimensionsChanged(const FloatSize& videoDimensions)
562 {
563     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
564
565     [playerLayer setVideoDimensions:videoDimensions];
566     [playerController() setContentDimensions:videoDimensions];
567     [m_playerLayerView setNeedsLayout];
568
569     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[m_playerLayerView pictureInPicturePlayerLayerView];
570     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
571     [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions];
572     [pipView setNeedsLayout];
573 }
574
575 void VideoFullscreenInterfaceAVKit::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType, const String&)
576 {
577     [m_playerLayerView setHidden:enabled];
578 }
579
580 #if !ENABLE(FULLSCREEN_API)
581
582 void VideoFullscreenInterfaceAVKit::applicationDidBecomeActive()
583 {
584     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this);
585     if (m_shouldReturnToFullscreenAfterEnteringForeground && m_videoFullscreenModel && m_videoFullscreenModel->isVisible()) {
586         [m_playerViewController stopPictureInPicture];
587         return;
588     }
589
590     // 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
591     // because the originating view is not visible, so hide the fullscreen window.
592     if (m_currentMode.hasFullscreen() && m_currentMode.hasPictureInPicture()) {
593         [[m_playerViewController view] layoutIfNeeded];
594         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) {
595             if (!success) {
596                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
597                 ASSERT_NOT_REACHED();
598             }
599
600             [m_window setHidden:YES];
601             [[m_playerViewController view] setHidden:YES];
602         }];
603     }
604 }
605
606 void VideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback, bool standby)
607 {
608     ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
609     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
610
611     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
612     m_videoView = &videoView;
613     m_parentView = parentView;
614     m_parentWindow = parentView.window;
615
616     ASSERT_UNUSED(standby, !standby);
617     bool isInPictureInPictureMode = m_currentMode.hasPictureInPicture();
618     m_currentMode = mode;
619     m_inlineRect = initialRect;
620
621     [CATransaction begin];
622     [CATransaction setDisableActions:YES];
623
624     if (![[m_parentView window] _isHostedInAnotherProcess]) {
625         if (!m_window)
626             m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
627         [m_window setBackgroundColor:clearUIColor()];
628         if (!m_viewController)
629             m_viewController = adoptNS([allocUIViewControllerInstance() init]);
630         [[m_viewController view] setFrame:[m_window bounds]];
631         [m_viewController _setIgnoreAppSupportedOrientations:YES];
632         [m_window setRootViewController:m_viewController.get()];
633         [m_window setWindowLevel:getUITextEffectsBeneathStatusBarWindowLevel() + 1];
634         [m_window makeKeyAndVisible];
635     }
636
637     if (!m_playerLayerView)
638         m_playerLayerView = adoptNS([allocWebAVPlayerLayerViewInstance() init]);
639     [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]];
640     [m_playerLayerView setBackgroundColor:clearUIColor()];
641
642     if (!isInPictureInPictureMode) {
643         [m_playerLayerView setVideoView:m_videoView.get()];
644         [m_playerLayerView addSubview:m_videoView.get()];
645     }
646
647     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
648
649     [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, m_inlineRect.width(), m_inlineRect.height())];
650     [playerLayer setVideoDimensions:[playerController() contentDimensions]];
651     playerLayer.fullscreenInterface = this;
652
653     if (!m_playerViewController)
654         m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithPlayerLayerView:m_playerLayerView.get()]);
655
656     [m_playerViewController setShowsPlaybackControls:NO];
657     [m_playerViewController setPlayerController:(AVPlayerController *)playerController()];
658     [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()];
659     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
660
661     [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback];
662
663     if (m_viewController) {
664         [m_viewController addChildViewController:m_playerViewController.get()];
665         [[m_viewController view] addSubview:[m_playerViewController view]];
666     } else
667         [m_parentView addSubview:[m_playerViewController view]];
668
669     [m_playerViewController view].frame = [m_parentView convertRect:m_inlineRect toView:[m_playerViewController view].superview];
670
671     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
672     [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)];
673
674     [[m_playerViewController view] setNeedsLayout];
675     [[m_playerViewController view] layoutIfNeeded];
676
677     [CATransaction commit];
678
679     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
680         if (m_fullscreenChangeObserver)
681             m_fullscreenChangeObserver->didSetupFullscreen();
682     });
683 }
684
685 void VideoFullscreenInterfaceAVKit::enterFullscreen()
686 {
687     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreen(%p) %d", this, mode());
688
689     m_exitCompleted = false;
690     m_exitRequested = false;
691     m_enterRequested = true;
692
693     if (m_currentMode.isPictureInPicture())
694         enterPictureInPicture();
695     else if (m_currentMode.isFullscreen())
696         enterFullscreenStandard();
697     else
698         ASSERT_NOT_REACHED();
699 }
700
701 void VideoFullscreenInterfaceAVKit::enterPictureInPicture()
702 {
703     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterPictureInPicture(%p)", this);
704     
705     if ([m_playerViewController isPictureInPicturePossible])
706         [m_playerViewController startPictureInPicture];
707     else
708         failedToStartPictureInPicture();
709 }
710
711 void VideoFullscreenInterfaceAVKit::enterFullscreenStandard()
712 {
713     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
714
715     if ([m_playerViewController isPictureInPictureActive]) {
716         // NOTE: The fullscreen mode will be restored in prepareForPictureInPictureStopWithCompletionHandler().
717         m_shouldReturnToFullscreenWhenStoppingPiP = true;
718         [m_playerViewController stopPictureInPicture];
719         return;
720     }
721
722     [[m_playerViewController view] layoutIfNeeded];
723     [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, protectedThis = makeRefPtr(this)] (BOOL succeeded, NSError* error) {
724         if (!succeeded) {
725             WTFLogAlways("-[AVPlayerViewController enterFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
726             ASSERT_NOT_REACHED();
727             m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, YES);
728             return;
729         }
730
731         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
732         [m_playerViewController setShowsPlaybackControls:YES];
733
734         if (m_fullscreenChangeObserver)
735             m_fullscreenChangeObserver->didEnterFullscreen();
736     }];
737 }
738
739 void VideoFullscreenInterfaceAVKit::exitFullscreen(const IntRect& finalRect)
740 {
741     m_watchdogTimer.stop();
742
743     m_exitRequested = true;
744     if (m_exitCompleted) {
745         if (m_fullscreenChangeObserver)
746             m_fullscreenChangeObserver->didExitFullscreen();
747         return;
748     }
749     
750     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::exitFullscreen(%p)", this);
751     [m_playerViewController setShowsPlaybackControls:NO];
752     
753     [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:[m_playerViewController view].superview];
754
755     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
756     if ([playerLayer videoGravity] != getAVLayerVideoGravityResizeAspect())
757         [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()];
758
759     [[m_playerViewController view] layoutIfNeeded];
760     if (m_currentMode.isPictureInPicture()) {
761         m_shouldReturnToFullscreenWhenStoppingPiP = false;
762         [m_window setHidden:NO];
763         [m_playerViewController stopPictureInPicture];
764     } else if (m_currentMode.hasPictureInPicture() && m_currentMode.hasFullscreen()) {
765         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) {
766             if (!success) {
767                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
768                 ASSERT_NOT_REACHED();
769             }
770
771             clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
772             [m_window setHidden:NO];
773             [m_playerViewController stopPictureInPicture];
774         }];
775     } else if (m_currentMode.isFullscreen()) {
776         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) mutable {
777             if (!success) {
778                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
779                 ASSERT_NOT_REACHED();
780             }
781
782             m_exitCompleted = true;
783
784             [CATransaction begin];
785             [CATransaction setDisableActions:YES];
786             [m_playerLayerView setBackgroundColor:clearUIColor()];
787             [[m_playerViewController view] setBackgroundColor:clearUIColor()];
788             [CATransaction commit];
789
790             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this]() {
791                 if (m_fullscreenChangeObserver)
792                     m_fullscreenChangeObserver->didExitFullscreen();
793             });
794         }];
795     };
796 }
797
798 void VideoFullscreenInterfaceAVKit::cleanupFullscreen()
799 {
800     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
801
802     if (m_window) {
803         [m_window setHidden:YES];
804         [m_window setRootViewController:nil];
805     }
806     
807     [m_playerViewController setDelegate:nil];
808     [m_playerViewController setPlayerController:nil];
809     
810     if (m_currentMode.hasPictureInPicture())
811         [m_playerViewController stopPictureInPicture];
812     if (m_currentMode.hasFullscreen()) {
813         [[m_playerViewController view] layoutIfNeeded];
814         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL success, NSError* error) {
815             if (!success) {
816                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
817                 ASSERT_NOT_REACHED();
818             }
819         }];
820     }
821     
822     [[m_playerViewController view] removeFromSuperview];
823     if (m_viewController)
824         [m_playerViewController removeFromParentViewController];
825     
826     [m_playerLayerView removeFromSuperview];
827     [[m_viewController view] removeFromSuperview];
828
829     m_playerLayerView = nil;
830     m_playerViewController = nil;
831     m_window = nil;
832     m_videoView = nil;
833     m_parentView = nil;
834     m_parentWindow = nil;
835     
836     if (m_fullscreenChangeObserver)
837         m_fullscreenChangeObserver->didCleanupFullscreen();
838
839     m_enterRequested = false;
840 }
841
842 void VideoFullscreenInterfaceAVKit::invalidate()
843 {
844     m_videoFullscreenModel = nil;
845     m_fullscreenChangeObserver = nil;
846     
847     cleanupFullscreen();
848 }
849
850 void VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
851 {
852     if (!m_enterRequested)
853         return;
854     
855     if (m_currentMode.hasPictureInPicture())
856         return;
857     
858     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
859
860     [m_window setHidden:YES];
861     [[m_playerViewController view] setHidden:YES];
862
863     if (playbackSessionModel() && m_videoFullscreenModel && !m_exitRequested) {
864         playbackSessionModel()->pause();
865         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
866     }
867 }
868
869 void VideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
870 {
871     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
872     if (m_prepareToInlineCallback) {
873         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview];
874         WTF::Function<void(bool)> callback = WTFMove(m_prepareToInlineCallback);
875         callback(visible);
876     }
877 }
878
879 bool VideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
880 {
881     return [playerController() isPlaying] && m_currentMode.isFullscreen() && supportsPictureInPicture();
882 }
883
884 void VideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(WTF::Function<void(bool)>&& callback)
885 {
886     m_prepareToInlineCallback = WTFMove(callback);
887     if (m_fullscreenChangeObserver)
888         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
889 }
890
891 void VideoFullscreenInterfaceAVKit::willStartPictureInPicture()
892 {
893     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
894     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
895 }
896
897 void VideoFullscreenInterfaceAVKit::didStartPictureInPicture()
898 {
899     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
900     m_shouldReturnToFullscreenAfterEnteringForeground = [m_playerViewController pictureInPictureWasStartedWhenEnteringBackground];
901     [m_playerViewController setShowsPlaybackControls:YES];
902
903     if (m_currentMode.hasFullscreen()) {
904         if (![m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]) {
905             [[m_playerViewController view] layoutIfNeeded];
906             [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) {
907                 if (!success) {
908                     WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
909                     ASSERT_NOT_REACHED();
910                 }
911                 [m_window setHidden:YES];
912                 [[m_playerViewController view] setHidden:YES];
913             }];
914         }
915     } else {
916         [m_window setHidden:YES];
917         [[m_playerViewController view] setHidden:YES];
918     }
919
920     if (m_fullscreenChangeObserver)
921         m_fullscreenChangeObserver->didEnterFullscreen();
922 }
923
924 void VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
925 {
926     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
927     [m_playerViewController setShowsPlaybackControls:YES];
928
929     if (m_currentMode.hasFullscreen())
930         return;
931
932     m_exitCompleted = true;
933
934     if (m_fullscreenChangeObserver)
935         m_fullscreenChangeObserver->didEnterFullscreen();
936
937     if (m_videoFullscreenModel)
938         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
939 }
940
941 void VideoFullscreenInterfaceAVKit::willStopPictureInPicture()
942 {
943     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
944
945     m_shouldReturnToFullscreenWhenStoppingPiP = false;
946     m_shouldReturnToFullscreenAfterEnteringForeground = false;
947
948     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop)
949         return;
950
951     [m_window setHidden:NO];
952     [[m_playerViewController view] setHidden:NO];
953
954     if (m_videoFullscreenModel)
955         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
956 }
957
958 void VideoFullscreenInterfaceAVKit::didStopPictureInPicture()
959 {
960     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
961
962     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop) {
963         clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
964         [m_playerViewController setShowsPlaybackControls:YES];
965
966         return;
967     }
968
969     m_exitCompleted = true;
970
971     [m_playerLayerView setBackgroundColor:clearUIColor()];
972     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
973
974     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
975
976     if (m_fullscreenChangeObserver)
977         m_fullscreenChangeObserver->didExitFullscreen();
978 }
979
980 void VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
981 {
982     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
983     if (m_shouldReturnToFullscreenWhenStoppingPiP || m_shouldReturnToFullscreenAfterEnteringForeground) {
984
985         m_shouldReturnToFullscreenAfterEnteringForeground = false;
986         m_shouldReturnToFullscreenWhenStoppingPiP = false;
987         m_restoringFullscreenForPictureInPictureStop = true;
988
989         [m_window setHidden:NO];
990         [[m_playerViewController view] setHidden:NO];
991
992         [[m_playerViewController view] layoutIfNeeded];
993         [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL succeeded, NSError *error) {
994             if (!succeeded) {
995                 WTFLogAlways("-[AVPlayerViewController enterFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
996                 ASSERT_NOT_REACHED();
997                 m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, YES);
998                 return;
999             }
1000
1001             m_restoringFullscreenForPictureInPictureStop = false;
1002             setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1003             completionHandler(succeeded);
1004             if (m_fullscreenChangeObserver)
1005                 m_fullscreenChangeObserver->didEnterFullscreen();
1006         }];
1007         return;
1008     }
1009
1010     fullscreenMayReturnToInline([protectedThis = makeRefPtr(this), strongCompletionHandler = adoptNS([completionHandler copy])](bool restored)  {
1011         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", protectedThis.get(), boolString(restored));
1012         ((void (^)(BOOL))strongCompletionHandler.get())(restored);
1013     });
1014 }
1015
1016 bool VideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(VideoFullscreenInterfaceAVKit::ExitFullScreenReason reason)
1017 {
1018     if (!m_videoFullscreenModel)
1019         return true;
1020
1021     if (reason == ExitFullScreenReason::PictureInPictureStarted) {
1022         if ([m_playerViewController pictureInPictureWasStartedWhenEnteringBackground])
1023             return false;
1024
1025         m_shouldReturnToFullscreenWhenStoppingPiP = m_currentMode.hasFullscreen();
1026         clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1027         return true;
1028     }
1029
1030     if (playbackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived))
1031         playbackSessionModel()->pause();
1032
1033     BOOL finished = reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::PinchGestureHandled;
1034     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, finished);
1035
1036     if (!m_watchdogTimer.isActive())
1037         m_watchdogTimer.startOneShot(defaultWatchdogTimerInterval);
1038
1039     return false;
1040 }
1041
1042 #else // ENABLE(FULLSCREEN_API)
1043
1044 void VideoFullscreenInterfaceAVKit::applicationDidBecomeActive()
1045 {
1046     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this);
1047
1048     // 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
1049     // because the originating view is not visible, so hide the fullscreen window.
1050     if (m_currentMode.hasFullscreen() && m_currentMode.hasPictureInPicture()) {
1051         [[m_playerViewController view] layoutIfNeeded];
1052         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1053             exitFullscreenHandler(success, error);
1054         }];
1055     }
1056 }
1057
1058 void VideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback, bool standby)
1059 {
1060     ASSERT(standby || mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
1061     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
1062
1063     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
1064     m_videoView = &videoView;
1065     m_parentView = parentView;
1066     m_parentWindow = parentView.window;
1067
1068     m_targetStandby = standby;
1069     m_targetMode = mode;
1070     setInlineRect(initialRect, true);
1071     doSetup();
1072 }
1073
1074 void VideoFullscreenInterfaceAVKit::enterFullscreen()
1075 {
1076     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreen(%p) %d", this, mode());
1077
1078     doEnterFullscreen();
1079 }
1080
1081 void VideoFullscreenInterfaceAVKit::exitFullscreen(const IntRect& finalRect)
1082 {
1083     m_watchdogTimer.stop();
1084
1085     m_targetMode = HTMLMediaElementEnums::VideoFullscreenModeNone;
1086
1087     setInlineRect(finalRect, true);
1088     doExitFullscreen();
1089 }
1090
1091 void VideoFullscreenInterfaceAVKit::cleanupFullscreen()
1092 {
1093     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
1094
1095     m_cleanupNeedsReturnVideoContentLayer = true;
1096     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
1097         m_fullscreenChangeObserver->returnVideoContentLayer();
1098         return;
1099     }
1100     m_cleanupNeedsReturnVideoContentLayer = false;
1101
1102     if (m_window) {
1103         [m_window setHidden:YES];
1104         [m_window setRootViewController:nil];
1105     }
1106     
1107     [m_playerViewController setDelegate:nil];
1108     [m_playerViewController setPlayerController:nil];
1109     
1110     if (m_currentMode.hasPictureInPicture())
1111         [m_playerViewController stopPictureInPicture];
1112     if (m_currentMode.hasFullscreen()) {
1113         [[m_playerViewController view] layoutIfNeeded];
1114         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL success, NSError* error) {
1115             if (!success) {
1116                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1117                 ASSERT_NOT_REACHED();
1118             }
1119         }];
1120     }
1121     
1122     [[m_playerViewController view] removeFromSuperview];
1123     if (m_viewController)
1124         [m_playerViewController removeFromParentViewController];
1125     
1126     [m_playerLayerView removeFromSuperview];
1127     [[m_viewController view] removeFromSuperview];
1128
1129     m_playerLayerView = nil;
1130     m_playerViewController = nil;
1131     m_window = nil;
1132     m_videoView = nil;
1133     m_parentView = nil;
1134     m_parentWindow = nil;
1135     
1136     if (m_fullscreenChangeObserver)
1137         m_fullscreenChangeObserver->didCleanupFullscreen();
1138 }
1139
1140 void VideoFullscreenInterfaceAVKit::invalidate()
1141 {
1142     m_videoFullscreenModel = nil;
1143     m_fullscreenChangeObserver = nil;
1144     
1145     cleanupFullscreen();
1146 }
1147
1148 void VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
1149 {
1150     if (m_currentMode.hasPictureInPicture())
1151         return;
1152     
1153     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
1154
1155     [m_window setHidden:YES];
1156     [[m_playerViewController view] setHidden:YES];
1157
1158     if (playbackSessionModel() && m_videoFullscreenModel) {
1159         playbackSessionModel()->pause();
1160         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1161     }
1162 }
1163
1164 void VideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
1165 {
1166     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
1167     setInlineRect(inlineRect, visible);
1168     if (m_prepareToInlineCallback) {
1169         WTF::Function<void(bool)> callback = WTFMove(m_prepareToInlineCallback);
1170         callback(visible);
1171     }
1172 }
1173
1174 bool VideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
1175 {
1176     return [playerController() isPlaying] && (m_standby || m_currentMode.isFullscreen()) && supportsPictureInPicture();
1177 }
1178
1179 void VideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(WTF::Function<void(bool)>&& callback)
1180 {
1181     m_prepareToInlineCallback = WTFMove(callback);
1182     if (m_fullscreenChangeObserver)
1183         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
1184 }
1185
1186 void VideoFullscreenInterfaceAVKit::willStartPictureInPicture()
1187 {
1188     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
1189     if (m_standby && !m_currentMode.hasVideo()) {
1190         [m_window setHidden:NO];
1191         [[m_playerViewController view] setHidden:NO];
1192     }
1193
1194     if (!m_hasVideoContentLayer)
1195         m_fullscreenChangeObserver->requestVideoContentLayer();
1196 }
1197
1198 void VideoFullscreenInterfaceAVKit::didStartPictureInPicture()
1199 {
1200     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
1201     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1202     [m_playerViewController setShowsPlaybackControls:YES];
1203
1204     if (m_currentMode.hasFullscreen()) {
1205         if (![m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]) {
1206             [[m_playerViewController view] layoutIfNeeded];
1207             [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1208                 exitFullscreenHandler(success, error);
1209             }];
1210         }
1211     } else {
1212         [m_window setHidden:YES];
1213         [[m_playerViewController view] setHidden:YES];
1214     }
1215
1216     if (m_enterFullscreenNeedsEnterPictureInPicture)
1217         doEnterFullscreen();
1218 }
1219
1220 void VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
1221 {
1222     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
1223     [m_playerViewController setShowsPlaybackControls:YES];
1224
1225     m_targetMode.setPictureInPicture(false);
1226     if (m_currentMode.hasFullscreen())
1227         return;
1228
1229     if (m_fullscreenChangeObserver)
1230         m_fullscreenChangeObserver->didEnterFullscreen();
1231
1232     if (m_videoFullscreenModel)
1233         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1234 }
1235
1236 void VideoFullscreenInterfaceAVKit::willStopPictureInPicture()
1237 {
1238     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
1239
1240     m_shouldReturnToFullscreenWhenStoppingPiP = false;
1241
1242     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop)
1243         return;
1244
1245     [m_window setHidden:NO];
1246     [[m_playerViewController view] setHidden:NO];
1247 }
1248
1249 void VideoFullscreenInterfaceAVKit::didStopPictureInPicture()
1250 {
1251     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
1252     m_targetMode.setPictureInPicture(false);
1253
1254     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop) {
1255         clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1256         [m_playerViewController setShowsPlaybackControls:YES];
1257
1258         if (m_exitFullscreenNeedsExitPictureInPicture)
1259             doExitFullscreen();
1260         return;
1261     }
1262
1263     [m_playerLayerView setBackgroundColor:clearUIColor()];
1264     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1265
1266     if (m_videoFullscreenModel)
1267         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1268
1269     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1270
1271     if (m_enterFullscreenNeedsExitPictureInPicture)
1272         doEnterFullscreen();
1273
1274     if (m_exitFullscreenNeedsExitPictureInPicture)
1275         doExitFullscreen();
1276 }
1277
1278 void VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
1279 {
1280     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
1281     if (m_shouldReturnToFullscreenWhenStoppingPiP) {
1282
1283         m_shouldReturnToFullscreenWhenStoppingPiP = false;
1284         m_restoringFullscreenForPictureInPictureStop = true;
1285
1286         [m_window setHidden:NO];
1287         [[m_playerViewController view] setHidden:NO];
1288
1289         [[m_playerViewController view] layoutIfNeeded];
1290         [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL success, NSError *error) {
1291             enterFullscreenHandler(success, error);
1292             completionHandler(success);
1293         }];
1294         return;
1295     }
1296
1297     fullscreenMayReturnToInline([protectedThis = makeRefPtr(this), strongCompletionHandler = adoptNS([completionHandler copy])](bool restored)  {
1298         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", protectedThis.get(), boolString(restored));
1299         ((void (^)(BOOL))strongCompletionHandler.get())(restored);
1300     });
1301 }
1302
1303 bool VideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(VideoFullscreenInterfaceAVKit::ExitFullScreenReason reason)
1304 {
1305     if (!m_videoFullscreenModel)
1306         return true;
1307
1308     if (reason == ExitFullScreenReason::PictureInPictureStarted) {
1309         m_shouldReturnToFullscreenWhenStoppingPiP = m_currentMode.hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1310         dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] () mutable {
1311             [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis = WTFMove(protectedThis), this] (BOOL success, NSError *error) {
1312                 exitFullscreenHandler(success, error);
1313             }];
1314         });
1315         return false;
1316     }
1317
1318     if (playbackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived))
1319         playbackSessionModel()->pause();
1320
1321     BOOL finished = reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::PinchGestureHandled;
1322     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, finished);
1323
1324     if (!m_watchdogTimer.isActive())
1325         m_watchdogTimer.startOneShot(defaultWatchdogTimerInterval);
1326
1327     return false;
1328 }
1329
1330 void VideoFullscreenInterfaceAVKit::setHasVideoContentLayer(bool value)
1331 {
1332     m_hasVideoContentLayer = value;
1333
1334     if (m_hasVideoContentLayer && m_finalizeSetupNeedsVideoContentLayer)
1335         finalizeSetup();
1336     if (!m_hasVideoContentLayer && m_cleanupNeedsReturnVideoContentLayer)
1337         cleanupFullscreen();
1338     if (!m_hasVideoContentLayer && m_returnToStandbyNeedsReturnVideoContentLayer)
1339         returnToStandby();
1340     if (!m_hasVideoContentLayer && m_finalizeSetupNeedsReturnVideoContentLayer)
1341         finalizeSetup();
1342     if (!m_hasVideoContentLayer && m_exitFullscreenNeedsReturnContentLayer)
1343         doExitFullscreen();
1344 }
1345
1346 void VideoFullscreenInterfaceAVKit::setInlineRect(const IntRect& inlineRect, bool visible)
1347 {
1348     m_inlineRect = inlineRect;
1349     m_inlineIsVisible = visible;
1350     m_hasUpdatedInlineRect = true;
1351
1352     if (m_playerViewController && m_parentView) {
1353         [CATransaction begin];
1354         [CATransaction setDisableActions:YES];
1355         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview];
1356         [CATransaction commit];
1357     }
1358
1359     if (m_setupNeedsInlineRect)
1360         doSetup();
1361
1362     if (m_exitFullscreenNeedInlineRect)
1363         doExitFullscreen();
1364 }
1365
1366 void VideoFullscreenInterfaceAVKit::doSetup()
1367 {
1368     Mode changes { m_currentMode.mode() ^ m_targetMode.mode() };
1369
1370     if (m_currentMode.hasVideo() && m_targetMode.hasVideo() && (m_standby != m_targetStandby)) {
1371         m_standby = m_targetStandby;
1372         finalizeSetup();
1373         return;
1374     }
1375
1376     if (!m_hasUpdatedInlineRect && m_fullscreenChangeObserver) {
1377         m_setupNeedsInlineRect = true;
1378         m_fullscreenChangeObserver->requestUpdateInlineRect();
1379         return;
1380     }
1381     m_setupNeedsInlineRect = false;
1382
1383     [CATransaction begin];
1384     [CATransaction setDisableActions:YES];
1385     if (![[m_parentView window] _isHostedInAnotherProcess] && !m_window) {
1386         if (!m_window)
1387             m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
1388         [m_window setBackgroundColor:clearUIColor()];
1389         if (!m_viewController)
1390             m_viewController = adoptNS([allocUIViewControllerInstance() init]);
1391         [[m_viewController view] setFrame:[m_window bounds]];
1392         [m_viewController _setIgnoreAppSupportedOrientations:YES];
1393         [m_window setRootViewController:m_viewController.get()];
1394         [m_window setWindowLevel:getUITextEffectsBeneathStatusBarWindowLevel() + 1];
1395         [m_window makeKeyAndVisible];
1396     }
1397
1398     if (!m_playerLayerView)
1399         m_playerLayerView = adoptNS([allocWebAVPlayerLayerViewInstance() init]);
1400     [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]];
1401     [m_playerLayerView setBackgroundColor:clearUIColor()];
1402
1403     if (!m_currentMode.hasPictureInPicture()) {
1404         [m_playerLayerView setVideoView:m_videoView.get()];
1405         [m_playerLayerView addSubview:m_videoView.get()];
1406     }
1407
1408     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
1409
1410     [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, m_inlineRect.width(), m_inlineRect.height())];
1411     [playerLayer setVideoDimensions:[playerController() contentDimensions]];
1412     playerLayer.fullscreenInterface = this;
1413
1414     if (!m_playerViewController)
1415         m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithPlayerLayerView:m_playerLayerView.get()]);
1416
1417     [m_playerViewController setShowsPlaybackControls:NO];
1418     [m_playerViewController setPlayerController:(AVPlayerController *)playerController()];
1419     [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()];
1420     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
1421
1422     [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback];
1423
1424     if (m_viewController) {
1425         [m_viewController addChildViewController:m_playerViewController.get()];
1426         [[m_viewController view] addSubview:[m_playerViewController view]];
1427     } else
1428         [m_parentView addSubview:[m_playerViewController view]];
1429
1430     [m_playerViewController view].frame = [m_parentView convertRect:m_inlineRect toView:[m_playerViewController view].superview];
1431
1432     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1433     [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)];
1434
1435     [[m_playerViewController view] setNeedsLayout];
1436     [[m_playerViewController view] layoutIfNeeded];
1437
1438     if (m_targetStandby && !m_currentMode.hasVideo()) {
1439         [m_window setHidden:YES];
1440         [[m_playerViewController view] setHidden:YES];
1441     }
1442
1443     [CATransaction commit];
1444
1445     finalizeSetup();
1446 }
1447
1448 void VideoFullscreenInterfaceAVKit::finalizeSetup()
1449 {
1450     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
1451         if (m_fullscreenChangeObserver) {
1452             if (!m_hasVideoContentLayer && m_targetMode.hasVideo()) {
1453                 m_finalizeSetupNeedsVideoContentLayer = true;
1454                 m_fullscreenChangeObserver->requestVideoContentLayer();
1455                 return;
1456             }
1457             m_finalizeSetupNeedsVideoContentLayer = false;
1458             if (m_hasVideoContentLayer && !m_targetMode.hasVideo()) {
1459                 m_finalizeSetupNeedsReturnVideoContentLayer = true;
1460                 m_fullscreenChangeObserver->returnVideoContentLayer();
1461                 return;
1462             }
1463             m_finalizeSetupNeedsReturnVideoContentLayer = false;
1464             m_fullscreenChangeObserver->didSetupFullscreen();
1465         }
1466     });
1467 }
1468
1469 void VideoFullscreenInterfaceAVKit::doEnterFullscreen()
1470 {
1471     m_standby = m_targetStandby;
1472
1473     [[m_playerViewController view] layoutIfNeeded];
1474     if (m_targetMode.hasFullscreen() && !m_currentMode.hasFullscreen()) {
1475         m_enterFullscreenNeedsEnterFullscreen = true;
1476         [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, protectedThis = makeRefPtr(this)] (BOOL success, NSError *error) {
1477             enterFullscreenHandler(success, error);
1478         }];
1479         return;
1480     }
1481     m_enterFullscreenNeedsEnterFullscreen = false;
1482
1483     if (m_targetMode.hasPictureInPicture() && !m_currentMode.hasPictureInPicture()) {
1484         m_enterFullscreenNeedsEnterPictureInPicture = true;
1485         if ([m_playerViewController isPictureInPicturePossible])
1486             [m_playerViewController startPictureInPicture];
1487         else
1488             failedToStartPictureInPicture();
1489         return;
1490     }
1491     m_enterFullscreenNeedsEnterPictureInPicture = false;
1492
1493     if (!m_targetMode.hasFullscreen() && m_currentMode.hasFullscreen()) {
1494         m_enterFullscreenNeedsExitFullscreen = true;
1495         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1496             exitFullscreenHandler(success, error);
1497         }];
1498         return;
1499     }
1500     m_enterFullscreenNeedsExitFullscreen = false;
1501
1502     if (!m_targetMode.hasPictureInPicture() && m_currentMode.hasPictureInPicture()) {
1503         m_enterFullscreenNeedsExitPictureInPicture = true;
1504         [m_playerViewController stopPictureInPicture];
1505         return;
1506     }
1507     m_enterFullscreenNeedsExitPictureInPicture = false;
1508
1509     if (m_fullscreenChangeObserver)
1510         m_fullscreenChangeObserver->didEnterFullscreen();
1511 }
1512
1513 void VideoFullscreenInterfaceAVKit::doExitFullscreen()
1514 {
1515     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::doExitFullscreen(%p)", this);
1516
1517     if (m_currentMode.hasVideo() && !m_hasUpdatedInlineRect && m_fullscreenChangeObserver) {
1518         m_exitFullscreenNeedInlineRect = true;
1519         m_fullscreenChangeObserver->requestUpdateInlineRect();
1520         return;
1521     }
1522     m_exitFullscreenNeedInlineRect = false;
1523
1524     if (m_currentMode.hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
1525         m_exitFullscreenNeedsExitFullscreen = true;
1526         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1527             exitFullscreenHandler(success, error);
1528         }];
1529         return;
1530     }
1531     m_exitFullscreenNeedsExitFullscreen = false;
1532
1533     if (m_currentMode.hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
1534         m_exitFullscreenNeedsExitPictureInPicture = true;
1535         m_shouldReturnToFullscreenWhenStoppingPiP = false;
1536         [m_window setHidden:NO];
1537         [m_playerViewController stopPictureInPicture];
1538         return;
1539     }
1540     m_exitFullscreenNeedsExitPictureInPicture = false;
1541
1542     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
1543         m_exitFullscreenNeedsReturnContentLayer = true;
1544         m_fullscreenChangeObserver->returnVideoContentLayer();
1545         return;
1546     }
1547     m_exitFullscreenNeedsReturnContentLayer = false;
1548
1549     m_standby = false;
1550
1551     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
1552         if (m_fullscreenChangeObserver)
1553             m_fullscreenChangeObserver->didExitFullscreen();
1554     });
1555 }
1556
1557 void VideoFullscreenInterfaceAVKit::exitFullscreenHandler(BOOL success, NSError* error)
1558 {
1559     if (!success) {
1560         WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1561         ASSERT_NOT_REACHED();
1562     }
1563
1564     UNUSED_PARAM(success);
1565     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didExitFullscreen(%p) - %d", this, success);
1566
1567     clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1568
1569     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
1570         [m_window setHidden:YES];
1571         [[m_playerViewController view] setHidden:YES];
1572     } else {
1573         [CATransaction begin];
1574         [CATransaction setDisableActions:YES];
1575         [m_playerLayerView setBackgroundColor:clearUIColor()];
1576         [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1577         [CATransaction commit];
1578     }
1579
1580     if (m_enterFullscreenNeedsExitFullscreen)
1581         doEnterFullscreen();
1582     
1583     if (m_exitFullscreenNeedsExitFullscreen)
1584         doExitFullscreen();
1585 }
1586
1587 void VideoFullscreenInterfaceAVKit::enterFullscreenHandler(BOOL success, NSError* error)
1588 {
1589     if (!success) {
1590         WTFLogAlways("-[AVPlayerViewController enterFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1591         ASSERT_NOT_REACHED();
1592         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, YES);
1593         return;
1594     }
1595
1596     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p)", this);
1597     setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1598     [m_playerViewController setShowsPlaybackControls:YES];
1599
1600     m_restoringFullscreenForPictureInPictureStop = false;
1601
1602     if (m_enterFullscreenNeedsEnterFullscreen)
1603         doEnterFullscreen();
1604 }
1605
1606 void VideoFullscreenInterfaceAVKit::returnToStandby()
1607 {
1608     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
1609         m_returnToStandbyNeedsReturnVideoContentLayer = true;
1610         m_fullscreenChangeObserver->returnVideoContentLayer();
1611         return;
1612     }
1613
1614     m_returnToStandbyNeedsReturnVideoContentLayer = false;
1615
1616     [m_window setHidden:YES];
1617     [[m_playerViewController view] setHidden:YES];
1618 }
1619
1620 #endif // ENABLE(FULLSCREEN_API)
1621
1622 NO_RETURN_DUE_TO_ASSERT void VideoFullscreenInterfaceAVKit::watchdogTimerFired()
1623 {
1624     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::watchdogTimerFired(%p) - no exit fullscreen response in %gs; forcing fullscreen hidden.", this, defaultWatchdogTimerInterval.value());
1625     ASSERT_NOT_REACHED();
1626     [m_window setHidden:YES];
1627     [[m_playerViewController view] setHidden:YES];
1628 }
1629
1630 void VideoFullscreenInterfaceAVKit::setMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1631 {
1632     if ((m_currentMode.mode() & mode) == mode)
1633         return;
1634
1635     m_currentMode.setMode(mode);
1636     if (m_videoFullscreenModel)
1637         m_videoFullscreenModel->fullscreenModeChanged(m_currentMode.mode());
1638 }
1639
1640 void VideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1641 {
1642     if ((~m_currentMode.mode() & mode) == mode)
1643         return;
1644
1645     m_currentMode.clearMode(mode);
1646     if (m_videoFullscreenModel)
1647         m_videoFullscreenModel->fullscreenModeChanged(m_currentMode.mode());
1648 }
1649
1650 #endif // HAVE(AVKIT)
1651
1652 bool WebCore::supportsPictureInPicture()
1653 {
1654 #if PLATFORM(IOS) && HAVE(AVKIT)
1655     return [getAVPictureInPictureControllerClass() isPictureInPictureSupported];
1656 #else
1657     return false;
1658 #endif
1659 }
1660
1661 #endif // PLATFORM(IOS)