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