PiP from Element Fullscreen should match AVKit's behavior
[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->videoFullscreenModel());
293         _fullscreenInterface->videoFullscreenModel()->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->videoFullscreenModel());
329         _fullscreenInterface->videoFullscreenModel()->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->videoFullscreenModel());
354     _fullscreenInterface->videoFullscreenModel()->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 BOOL VideoFullscreenInterfaceAVKit::pictureInPictureWasStartedWhenEnteringBackground() const
801 {
802     return [m_playerViewController pictureInPictureWasStartedWhenEnteringBackground];
803 }
804
805 #if !ENABLE(FULLSCREEN_API)
806
807 void VideoFullscreenInterfaceAVKit::applicationDidBecomeActive()
808 {
809     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this);
810     if (m_shouldReturnToFullscreenAfterEnteringForeground && m_videoFullscreenModel && m_videoFullscreenModel->isVisible()) {
811         [m_playerViewController stopPictureInPicture];
812         return;
813     }
814
815     // 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
816     // because the originating view is not visible, so hide the fullscreen window.
817     if (m_currentMode.hasFullscreen() && m_currentMode.hasPictureInPicture()) {
818         [[m_playerViewController view] layoutIfNeeded];
819         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) {
820             if (!success)
821                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
822
823             [m_window setHidden:YES];
824             [[m_playerViewController view] setHidden:YES];
825         }];
826     }
827 }
828
829 void VideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback, bool standby)
830 {
831     ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
832     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
833
834     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
835     m_videoView = &videoView;
836     m_parentView = parentView;
837     m_parentWindow = parentView.window;
838
839     ASSERT_UNUSED(standby, !standby);
840     bool isInPictureInPictureMode = m_currentMode.hasPictureInPicture();
841     m_currentMode = mode;
842     m_inlineRect = initialRect;
843
844     [CATransaction begin];
845     [CATransaction setDisableActions:YES];
846
847 #if !PLATFORM(WATCHOS)
848     if (![[m_parentView window] _isHostedInAnotherProcess]) {
849         if (!m_window)
850             m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
851         [m_window setBackgroundColor:clearUIColor()];
852         if (!m_viewController)
853             m_viewController = adoptNS([allocUIViewControllerInstance() init]);
854         [[m_viewController view] setFrame:[m_window bounds]];
855         [m_viewController _setIgnoreAppSupportedOrientations:YES];
856         [m_window setRootViewController:m_viewController.get()];
857         [m_window setWindowLevel:getUITextEffectsBeneathStatusBarWindowLevel() + 1];
858         [m_window makeKeyAndVisible];
859     }
860 #endif
861
862     if (!m_playerLayerView)
863         m_playerLayerView = adoptNS([allocWebAVPlayerLayerViewInstance() init]);
864     [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]];
865     [m_playerLayerView setBackgroundColor:clearUIColor()];
866
867     if (!isInPictureInPictureMode) {
868         [m_playerLayerView setVideoView:m_videoView.get()];
869         [m_playerLayerView addSubview:m_videoView.get()];
870     }
871
872     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
873     [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, m_inlineRect.width(), m_inlineRect.height())];
874     [playerLayer setVideoDimensions:[playerController() contentDimensions]];
875     playerLayer.fullscreenInterface = this;
876
877     if (!m_playerViewController)
878         m_playerViewController = adoptNS([[WebAVPlayerViewController alloc] initWithFullscreenInterface:this]);
879
880     [m_playerViewController setShowsPlaybackControls:NO];
881     [m_playerViewController setPlayerController:(AVPlayerController *)playerController()];
882     [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()];
883     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
884     [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback];
885
886 #if PLATFORM(WATCHOS)
887     m_viewController = videoFullscreenModel()->createVideoFullscreenViewController(m_playerViewController.get().avPlayerViewController);
888 #endif
889
890     if (m_viewController) {
891         [m_viewController addChildViewController:m_playerViewController.get().avPlayerViewController];
892         [[m_viewController view] addSubview:[m_playerViewController view]];
893     } else
894         [m_parentView addSubview:[m_playerViewController view]];
895
896     [m_playerViewController view].frame = [m_parentView convertRect:m_inlineRect toView:[m_playerViewController view].superview];
897     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
898     [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)];
899
900     [[m_playerViewController view] setNeedsLayout];
901     [[m_playerViewController view] layoutIfNeeded];
902
903     [CATransaction commit];
904
905     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
906         if (m_fullscreenChangeObserver)
907             m_fullscreenChangeObserver->didSetupFullscreen();
908     });
909 }
910
911 void VideoFullscreenInterfaceAVKit::enterFullscreen()
912 {
913     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreen(%p) %d", this, mode());
914
915     m_exitCompleted = false;
916     m_exitRequested = false;
917     m_enterRequested = true;
918
919     if (m_currentMode.isPictureInPicture())
920         enterPictureInPicture();
921     else if (m_currentMode.isFullscreen())
922         enterFullscreenStandard();
923     else
924         ASSERT_NOT_REACHED();
925 }
926
927 void VideoFullscreenInterfaceAVKit::enterPictureInPicture()
928 {
929     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterPictureInPicture(%p)", this);
930     
931     if ([m_playerViewController isPictureInPicturePossible])
932         [m_playerViewController startPictureInPicture];
933     else
934         failedToStartPictureInPicture();
935 }
936
937 static UIViewController *fallbackViewController(UIView *view)
938 {
939     for (UIView *currentView = view; currentView; currentView = currentView.superview) {
940         if (UIViewController *viewController = [getUIViewControllerClass() viewControllerForView:currentView]) {
941             if (![viewController parentViewController])
942                 return viewController;
943         }
944     }
945
946     LOG_ERROR("Failed to find a view controller suitable to present fullscreen video");
947     return nil;
948 }
949
950 UIViewController *VideoFullscreenInterfaceAVKit::presentingViewController()
951 {
952     auto *controller = videoFullscreenModel()->presentingViewController();
953     if (!controller)
954         controller = fallbackViewController(m_parentView.get());
955
956     return controller;
957 }
958
959 void VideoFullscreenInterfaceAVKit::enterFullscreenStandard()
960 {
961     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
962
963     if ([m_playerViewController isPictureInPictureActive]) {
964         // NOTE: The fullscreen mode will be restored in prepareForPictureInPictureStopWithCompletionHandler().
965         m_shouldReturnToFullscreenWhenStoppingPiP = true;
966         [m_playerViewController stopPictureInPicture];
967         return;
968     }
969
970     [[m_playerViewController view] layoutIfNeeded];
971     [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, protectedThis = makeRefPtr(this)] (BOOL succeeded, NSError* error) {
972         if (!succeeded)
973             WTFLogAlways("-[AVPlayerViewController enterFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
974
975         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
976         [m_playerViewController setShowsPlaybackControls:YES];
977
978         if (m_fullscreenChangeObserver)
979             m_fullscreenChangeObserver->didEnterFullscreen();
980     }];
981 }
982
983 void VideoFullscreenInterfaceAVKit::exitFullscreen(const IntRect& finalRect)
984 {
985     m_watchdogTimer.stop();
986
987     m_exitRequested = true;
988     if (m_exitCompleted) {
989         if (m_fullscreenChangeObserver)
990             m_fullscreenChangeObserver->didExitFullscreen();
991         return;
992     }
993
994     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::exitFullscreen(%p)", this);
995     [m_playerViewController setShowsPlaybackControls:NO];
996     
997     [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:[m_playerViewController view].superview];
998
999     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
1000     if ([playerLayer videoGravity] != getAVLayerVideoGravityResizeAspect())
1001         [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()];
1002
1003     [[m_playerViewController view] layoutIfNeeded];
1004     if (m_currentMode.isPictureInPicture()) {
1005         m_shouldReturnToFullscreenWhenStoppingPiP = false;
1006         [m_window setHidden:NO];
1007         [m_playerViewController stopPictureInPicture];
1008     } else if (m_currentMode.hasPictureInPicture() && m_currentMode.hasFullscreen()) {
1009         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) {
1010             if (!success)
1011                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1012
1013             clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1014             [m_window setHidden:NO];
1015             [m_playerViewController stopPictureInPicture];
1016         }];
1017     } else if (m_currentMode.isFullscreen()) {
1018         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) mutable {
1019             if (!success)
1020                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1021
1022             m_exitCompleted = true;
1023
1024             [CATransaction begin];
1025             [CATransaction setDisableActions:YES];
1026             [m_playerLayerView setBackgroundColor:clearUIColor()];
1027             [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1028             [CATransaction commit];
1029
1030             dispatch_async(dispatch_get_main_queue(), [protectedThis = WTFMove(protectedThis), this]() {
1031                 if (m_fullscreenChangeObserver)
1032                     m_fullscreenChangeObserver->didExitFullscreen();
1033             });
1034         }];
1035     };
1036 }
1037
1038 void VideoFullscreenInterfaceAVKit::cleanupFullscreen()
1039 {
1040     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
1041
1042     if (m_window) {
1043         [m_window setHidden:YES];
1044         [m_window setRootViewController:nil];
1045     }
1046     
1047     [m_playerViewController setDelegate:nil];
1048     [m_playerViewController setPlayerController:nil];
1049     
1050     if (m_currentMode.hasPictureInPicture())
1051         [m_playerViewController stopPictureInPicture];
1052     if (m_currentMode.hasFullscreen()) {
1053         [[m_playerViewController view] layoutIfNeeded];
1054         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL success, NSError* error) {
1055             if (!success)
1056                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1057         }];
1058     }
1059     
1060     [[m_playerViewController view] removeFromSuperview];
1061     if (m_viewController)
1062         [m_playerViewController removeFromParentViewController];
1063     
1064     [m_playerLayerView removeFromSuperview];
1065     [[m_viewController view] removeFromSuperview];
1066
1067     m_playerLayerView = nil;
1068     m_playerViewController = nil;
1069     m_window = nil;
1070     m_videoView = nil;
1071     m_parentView = nil;
1072     m_parentWindow = nil;
1073     
1074     if (m_fullscreenChangeObserver)
1075         m_fullscreenChangeObserver->didCleanupFullscreen();
1076
1077     m_enterRequested = false;
1078 }
1079
1080 void VideoFullscreenInterfaceAVKit::invalidate()
1081 {
1082     m_videoFullscreenModel = nil;
1083     m_fullscreenChangeObserver = nil;
1084     
1085     cleanupFullscreen();
1086 }
1087
1088 void VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
1089 {
1090     if (!m_enterRequested)
1091         return;
1092     
1093     if (m_currentMode.hasPictureInPicture())
1094         return;
1095     
1096     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
1097
1098     [m_window setHidden:YES];
1099     [[m_playerViewController view] setHidden:YES];
1100
1101     if (playbackSessionModel() && m_videoFullscreenModel && !m_exitRequested) {
1102         playbackSessionModel()->pause();
1103         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1104     }
1105 }
1106
1107 void VideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
1108 {
1109     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
1110     if (m_prepareToInlineCallback) {
1111         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview];
1112         WTF::Function<void(bool)> callback = WTFMove(m_prepareToInlineCallback);
1113         callback(visible);
1114     }
1115 }
1116
1117 void VideoFullscreenInterfaceAVKit::preparedToExitFullscreen()
1118 {
1119     if (!m_waitingForPreparedToExit)
1120         return;
1121
1122     m_waitingForPreparedToExit = false;
1123     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, true);
1124 }
1125
1126 bool VideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
1127 {
1128     return [playerController() isPlaying] && m_currentMode.isFullscreen() && supportsPictureInPicture();
1129 }
1130
1131 void VideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(WTF::Function<void(bool)>&& callback)
1132 {
1133     m_prepareToInlineCallback = WTFMove(callback);
1134     if (m_fullscreenChangeObserver)
1135         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
1136 }
1137
1138 void VideoFullscreenInterfaceAVKit::willStartPictureInPicture()
1139 {
1140     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
1141     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1142     if (m_videoFullscreenModel)
1143         m_videoFullscreenModel->willEnterPictureInPicture();
1144 }
1145
1146 void VideoFullscreenInterfaceAVKit::didStartPictureInPicture()
1147 {
1148     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
1149     m_shouldReturnToFullscreenAfterEnteringForeground = pictureInPictureWasStartedWhenEnteringBackground();
1150     [m_playerViewController setShowsPlaybackControls:YES];
1151
1152     if (m_currentMode.hasFullscreen()) {
1153         if (!pictureInPictureWasStartedWhenEnteringBackground()) {
1154             [[m_playerViewController view] layoutIfNeeded];
1155             [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError* error) {
1156                 if (!success)
1157                     WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1158                 [m_window setHidden:YES];
1159                 [[m_playerViewController view] setHidden:YES];
1160             }];
1161         }
1162     } else {
1163         [m_window setHidden:YES];
1164         [[m_playerViewController view] setHidden:YES];
1165     }
1166
1167     if (m_fullscreenChangeObserver)
1168         m_fullscreenChangeObserver->didEnterFullscreen();
1169     if (m_videoFullscreenModel)
1170         m_videoFullscreenModel->didEnterPictureInPicture();
1171 }
1172
1173 void VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
1174 {
1175     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
1176     [m_playerViewController setShowsPlaybackControls:YES];
1177
1178     if (m_currentMode.hasFullscreen())
1179         return;
1180
1181     m_exitCompleted = true;
1182
1183     if (m_fullscreenChangeObserver)
1184         m_fullscreenChangeObserver->didEnterFullscreen();
1185     if (m_videoFullscreenModel)
1186         m_videoFullscreenModel->failedToEnterPictureInPicture();
1187
1188     if (m_videoFullscreenModel)
1189         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1190 }
1191
1192 void VideoFullscreenInterfaceAVKit::willStopPictureInPicture()
1193 {
1194     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
1195
1196     m_shouldReturnToFullscreenWhenStoppingPiP = false;
1197     m_shouldReturnToFullscreenAfterEnteringForeground = false;
1198
1199     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop)
1200         return;
1201
1202     [m_window setHidden:NO];
1203     [[m_playerViewController view] setHidden:NO];
1204
1205     if (m_videoFullscreenModel)
1206         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1207     if (m_videoFullscreenModel)
1208         m_videoFullscreenModel->willExitPictureInPicture();
1209 }
1210
1211 void VideoFullscreenInterfaceAVKit::didStopPictureInPicture()
1212 {
1213     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
1214
1215     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop) {
1216         clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1217         [m_playerViewController setShowsPlaybackControls:YES];
1218
1219         return;
1220     }
1221
1222     m_exitCompleted = true;
1223
1224     [m_playerLayerView setBackgroundColor:clearUIColor()];
1225     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1226
1227     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1228
1229     if (m_fullscreenChangeObserver)
1230         m_fullscreenChangeObserver->didExitFullscreen();
1231     if (m_videoFullscreenModel)
1232         m_videoFullscreenModel->didExitPictureInPicture();
1233 }
1234
1235 void VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
1236 {
1237     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
1238     if (m_shouldReturnToFullscreenWhenStoppingPiP || m_shouldReturnToFullscreenAfterEnteringForeground) {
1239
1240         m_shouldReturnToFullscreenAfterEnteringForeground = false;
1241         m_shouldReturnToFullscreenWhenStoppingPiP = false;
1242         m_restoringFullscreenForPictureInPictureStop = true;
1243
1244         [m_window setHidden:NO];
1245         [[m_playerViewController view] setHidden:NO];
1246
1247         [[m_playerViewController view] layoutIfNeeded];
1248         [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL succeeded, NSError *error) {
1249             if (!succeeded)
1250                 WTFLogAlways("-[AVPlayerViewController enterFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1251
1252             m_restoringFullscreenForPictureInPictureStop = false;
1253             setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1254             completionHandler(succeeded);
1255             if (m_fullscreenChangeObserver)
1256                 m_fullscreenChangeObserver->didEnterFullscreen();
1257         }];
1258         return;
1259     }
1260
1261     fullscreenMayReturnToInline([protectedThis = makeRefPtr(this), strongCompletionHandler = adoptNS([completionHandler copy])](bool restored)  {
1262         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", protectedThis.get(), boolString(restored));
1263         ((void (^)(BOOL))strongCompletionHandler.get())(restored);
1264     });
1265 }
1266
1267 bool VideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(VideoFullscreenInterfaceAVKit::ExitFullScreenReason reason)
1268 {
1269     if (!m_videoFullscreenModel)
1270         return true;
1271
1272     if (reason == ExitFullScreenReason::PictureInPictureStarted) {
1273         if (pictureInPictureWasStartedWhenEnteringBackground())
1274             return false;
1275
1276         m_shouldReturnToFullscreenWhenStoppingPiP = m_currentMode.hasFullscreen();
1277         clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1278         return true;
1279     }
1280
1281     if (playbackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived))
1282         playbackSessionModel()->pause();
1283
1284     if (!m_watchdogTimer.isActive() && !ignoreWatchdogForDebugging)
1285         m_watchdogTimer.startOneShot(defaultWatchdogTimerInterval);
1286
1287 #if PLATFORM(WATCHOS)
1288     if (m_fullscreenChangeObserver) {
1289         m_waitingForPreparedToExit = true;
1290         m_fullscreenChangeObserver->willExitFullscreen();
1291         return false;
1292     }
1293 #endif
1294
1295     BOOL finished = reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::PinchGestureHandled;
1296     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, finished);
1297
1298     return false;
1299 }
1300
1301 #else // ENABLE(FULLSCREEN_API)
1302
1303 void VideoFullscreenInterfaceAVKit::applicationDidBecomeActive()
1304 {
1305     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this);
1306 }
1307
1308 void VideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback, bool standby)
1309 {
1310     ASSERT(standby || mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
1311     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
1312
1313     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
1314     m_videoView = &videoView;
1315     m_parentView = parentView;
1316     m_parentWindow = parentView.window;
1317
1318     m_targetStandby = standby;
1319     m_targetMode = mode;
1320     setInlineRect(initialRect, true);
1321     doSetup();
1322 }
1323
1324 void VideoFullscreenInterfaceAVKit::enterFullscreen()
1325 {
1326     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreen(%p) %d", this, mode());
1327
1328     doEnterFullscreen();
1329 }
1330
1331 void VideoFullscreenInterfaceAVKit::exitFullscreen(const IntRect& finalRect)
1332 {
1333     m_watchdogTimer.stop();
1334
1335     m_targetMode = HTMLMediaElementEnums::VideoFullscreenModeNone;
1336
1337     setInlineRect(finalRect, true);
1338     doExitFullscreen();
1339 }
1340
1341 void VideoFullscreenInterfaceAVKit::cleanupFullscreen()
1342 {
1343     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
1344
1345     m_cleanupNeedsReturnVideoContentLayer = true;
1346     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
1347         m_fullscreenChangeObserver->returnVideoContentLayer();
1348         return;
1349     }
1350     m_cleanupNeedsReturnVideoContentLayer = false;
1351
1352     if (m_window) {
1353         [m_window setHidden:YES];
1354         [m_window setRootViewController:nil];
1355     }
1356     
1357     [m_playerViewController setDelegate:nil];
1358     [m_playerViewController setPlayerController:nil];
1359     
1360     if (m_currentMode.hasPictureInPicture())
1361         [m_playerViewController stopPictureInPicture];
1362     if (m_currentMode.hasFullscreen()) {
1363         [[m_playerViewController view] layoutIfNeeded];
1364         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL success, NSError* error) {
1365             if (!success)
1366                 WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1367         }];
1368     }
1369     
1370     [[m_playerViewController view] removeFromSuperview];
1371     if (m_viewController)
1372         [m_playerViewController removeFromParentViewController];
1373     
1374     [m_playerLayerView removeFromSuperview];
1375     [[m_viewController view] removeFromSuperview];
1376
1377     m_playerLayerView = nil;
1378     m_playerViewController = nil;
1379     m_window = nil;
1380     m_videoView = nil;
1381     m_parentView = nil;
1382     m_parentWindow = nil;
1383     
1384     if (m_fullscreenChangeObserver)
1385         m_fullscreenChangeObserver->didCleanupFullscreen();
1386 }
1387
1388 void VideoFullscreenInterfaceAVKit::invalidate()
1389 {
1390     m_videoFullscreenModel = nil;
1391     m_fullscreenChangeObserver = nil;
1392     
1393     cleanupFullscreen();
1394 }
1395
1396 void VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
1397 {
1398     if (m_currentMode.hasPictureInPicture())
1399         return;
1400     
1401     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
1402
1403     [m_window setHidden:YES];
1404     [[m_playerViewController view] setHidden:YES];
1405
1406     if (playbackSessionModel() && m_videoFullscreenModel) {
1407         playbackSessionModel()->pause();
1408         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1409     }
1410 }
1411
1412 void VideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
1413 {
1414     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
1415     setInlineRect(inlineRect, visible);
1416     if (m_prepareToInlineCallback) {
1417         WTF::Function<void(bool)> callback = WTFMove(m_prepareToInlineCallback);
1418         callback(visible);
1419     }
1420 }
1421
1422 void VideoFullscreenInterfaceAVKit::preparedToExitFullscreen()
1423 {
1424 }
1425
1426 bool VideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
1427 {
1428     return [playerController() isPlaying] && (m_standby || m_currentMode.isFullscreen()) && supportsPictureInPicture();
1429 }
1430
1431 void VideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(WTF::Function<void(bool)>&& callback)
1432 {
1433     m_prepareToInlineCallback = WTFMove(callback);
1434     if (m_fullscreenChangeObserver)
1435         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
1436 }
1437
1438 void VideoFullscreenInterfaceAVKit::willStartPictureInPicture()
1439 {
1440     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
1441     if (m_standby && !m_currentMode.hasVideo()) {
1442         [m_window setHidden:NO];
1443         [[m_playerViewController view] setHidden:NO];
1444     }
1445
1446     if (!m_hasVideoContentLayer)
1447         m_fullscreenChangeObserver->requestVideoContentLayer();
1448     if (m_videoFullscreenModel)
1449         m_videoFullscreenModel->willEnterPictureInPicture();
1450 }
1451
1452 void VideoFullscreenInterfaceAVKit::didStartPictureInPicture()
1453 {
1454     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
1455     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1456     [m_playerViewController setShowsPlaybackControls:YES];
1457
1458     if (m_currentMode.hasFullscreen()) {
1459         m_shouldReturnToFullscreenWhenStoppingPiP = YES;
1460         [[m_playerViewController view] layoutIfNeeded];
1461         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1462             exitFullscreenHandler(success, error);
1463         }];
1464     } else {
1465         [m_window setHidden:YES];
1466         [[m_playerViewController view] setHidden:YES];
1467     }
1468
1469     if (m_videoFullscreenModel)
1470         m_videoFullscreenModel->didEnterPictureInPicture();
1471
1472     if (m_enterFullscreenNeedsEnterPictureInPicture)
1473         doEnterFullscreen();
1474 }
1475
1476 void VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
1477 {
1478     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
1479     [m_playerViewController setShowsPlaybackControls:YES];
1480
1481     m_targetMode.setPictureInPicture(false);
1482     if (m_currentMode.hasFullscreen())
1483         return;
1484
1485     if (m_fullscreenChangeObserver)
1486         m_fullscreenChangeObserver->didEnterFullscreen();
1487
1488     if (m_videoFullscreenModel)
1489         m_videoFullscreenModel->failedToEnterPictureInPicture();
1490
1491     if (m_videoFullscreenModel)
1492         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1493 }
1494
1495 void VideoFullscreenInterfaceAVKit::willStopPictureInPicture()
1496 {
1497     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
1498
1499     m_shouldReturnToFullscreenWhenStoppingPiP = false;
1500
1501     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop)
1502         return;
1503
1504     [m_window setHidden:NO];
1505     [[m_playerViewController view] setHidden:NO];
1506
1507     if (m_videoFullscreenModel)
1508         m_videoFullscreenModel->willExitPictureInPicture();
1509 }
1510
1511 void VideoFullscreenInterfaceAVKit::didStopPictureInPicture()
1512 {
1513     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
1514     m_targetMode.setPictureInPicture(false);
1515
1516     if (m_currentMode.hasFullscreen() || m_restoringFullscreenForPictureInPictureStop) {
1517         clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1518         [m_playerViewController setShowsPlaybackControls:YES];
1519
1520         if (m_exitFullscreenNeedsExitPictureInPicture)
1521             doExitFullscreen();
1522         return;
1523     }
1524
1525     [m_playerLayerView setBackgroundColor:clearUIColor()];
1526     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1527
1528     if (m_videoFullscreenModel)
1529         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
1530
1531     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
1532
1533     if (m_videoFullscreenModel)
1534         m_videoFullscreenModel->didExitPictureInPicture();
1535
1536     if (m_enterFullscreenNeedsExitPictureInPicture)
1537         doEnterFullscreen();
1538
1539     if (m_exitFullscreenNeedsExitPictureInPicture)
1540         doExitFullscreen();
1541 }
1542
1543 void VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
1544 {
1545     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
1546     if (m_shouldReturnToFullscreenWhenStoppingPiP) {
1547
1548         m_shouldReturnToFullscreenWhenStoppingPiP = false;
1549         m_restoringFullscreenForPictureInPictureStop = true;
1550
1551         [m_window setHidden:NO];
1552         [[m_playerViewController view] setHidden:NO];
1553
1554         [[m_playerViewController view] layoutIfNeeded];
1555         [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL success, NSError *error) {
1556             enterFullscreenHandler(success, error);
1557             completionHandler(success);
1558         }];
1559         return;
1560     }
1561
1562     fullscreenMayReturnToInline([protectedThis = makeRefPtr(this), strongCompletionHandler = adoptNS([completionHandler copy])](bool restored)  {
1563         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", protectedThis.get(), boolString(restored));
1564         ((void (^)(BOOL))strongCompletionHandler.get())(restored);
1565     });
1566 }
1567
1568 bool VideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(VideoFullscreenInterfaceAVKit::ExitFullScreenReason reason)
1569 {
1570     if (!m_videoFullscreenModel)
1571         return true;
1572
1573     if (reason == ExitFullScreenReason::PictureInPictureStarted)
1574         return false;
1575
1576     if (playbackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived))
1577         playbackSessionModel()->pause();
1578
1579     BOOL finished = reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::PinchGestureHandled;
1580     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, finished);
1581
1582     if (!m_watchdogTimer.isActive() && !ignoreWatchdogForDebugging)
1583         m_watchdogTimer.startOneShot(defaultWatchdogTimerInterval);
1584
1585     return false;
1586 }
1587
1588 void VideoFullscreenInterfaceAVKit::setHasVideoContentLayer(bool value)
1589 {
1590     m_hasVideoContentLayer = value;
1591
1592     if (m_hasVideoContentLayer && m_finalizeSetupNeedsVideoContentLayer)
1593         finalizeSetup();
1594     if (!m_hasVideoContentLayer && m_cleanupNeedsReturnVideoContentLayer)
1595         cleanupFullscreen();
1596     if (!m_hasVideoContentLayer && m_returnToStandbyNeedsReturnVideoContentLayer)
1597         returnToStandby();
1598     if (!m_hasVideoContentLayer && m_finalizeSetupNeedsReturnVideoContentLayer)
1599         finalizeSetup();
1600     if (!m_hasVideoContentLayer && m_exitFullscreenNeedsReturnContentLayer)
1601         doExitFullscreen();
1602 }
1603
1604 void VideoFullscreenInterfaceAVKit::setInlineRect(const IntRect& inlineRect, bool visible)
1605 {
1606     m_inlineRect = inlineRect;
1607     m_inlineIsVisible = visible;
1608     m_hasUpdatedInlineRect = true;
1609
1610     if (m_playerViewController && m_parentView) {
1611         [CATransaction begin];
1612         [CATransaction setDisableActions:YES];
1613         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview];
1614         [CATransaction commit];
1615     }
1616
1617     if (m_setupNeedsInlineRect)
1618         doSetup();
1619
1620     if (m_exitFullscreenNeedInlineRect)
1621         doExitFullscreen();
1622 }
1623
1624 void VideoFullscreenInterfaceAVKit::doSetup()
1625 {
1626     Mode changes { m_currentMode.mode() ^ m_targetMode.mode() };
1627
1628     if (m_currentMode.hasVideo() && m_targetMode.hasVideo() && (m_standby != m_targetStandby)) {
1629         m_standby = m_targetStandby;
1630         finalizeSetup();
1631         return;
1632     }
1633
1634     if (!m_hasUpdatedInlineRect && m_fullscreenChangeObserver) {
1635         m_setupNeedsInlineRect = true;
1636         m_fullscreenChangeObserver->requestUpdateInlineRect();
1637         return;
1638     }
1639     m_setupNeedsInlineRect = false;
1640
1641     [CATransaction begin];
1642     [CATransaction setDisableActions:YES];
1643
1644 #if !PLATFORM(WATCHOS)
1645     if (![[m_parentView window] _isHostedInAnotherProcess] && !m_window) {
1646         if (!m_window)
1647             m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
1648         [m_window setBackgroundColor:clearUIColor()];
1649         if (!m_viewController)
1650             m_viewController = adoptNS([allocUIViewControllerInstance() init]);
1651         [[m_viewController view] setFrame:[m_window bounds]];
1652         [m_viewController _setIgnoreAppSupportedOrientations:YES];
1653         [m_window setRootViewController:m_viewController.get()];
1654         [m_window setWindowLevel:getUITextEffectsBeneathStatusBarWindowLevel() + 1];
1655         [m_window makeKeyAndVisible];
1656     }
1657 #endif
1658
1659     if (!m_playerLayerView)
1660         m_playerLayerView = adoptNS([allocWebAVPlayerLayerViewInstance() init]);
1661     [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]];
1662     [m_playerLayerView setBackgroundColor:clearUIColor()];
1663
1664     if (!m_currentMode.hasPictureInPicture()) {
1665         [m_playerLayerView setVideoView:m_videoView.get()];
1666         [m_playerLayerView addSubview:m_videoView.get()];
1667     }
1668
1669     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
1670
1671     [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, m_inlineRect.width(), m_inlineRect.height())];
1672     [playerLayer setVideoDimensions:[playerController() contentDimensions]];
1673     playerLayer.fullscreenInterface = this;
1674
1675     if (!m_playerViewController)
1676         m_playerViewController = adoptNS([[WebAVPlayerViewController alloc] initWithFullscreenInterface:this]);
1677
1678     [m_playerViewController setShowsPlaybackControls:NO];
1679     [m_playerViewController setPlayerController:(AVPlayerController *)playerController()];
1680     [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()];
1681     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
1682     [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback];
1683
1684 #if PLATFORM(WATCHOS)
1685     m_viewController = videoFullscreenModel()->createVideoFullscreenViewController(m_playerViewController.get().avPlayerViewController);
1686 #endif
1687
1688     if (m_viewController) {
1689         [m_viewController addChildViewController:m_playerViewController.get().avPlayerViewController];
1690         [[m_viewController view] addSubview:[m_playerViewController view]];
1691     } else
1692         [m_parentView addSubview:[m_playerViewController view]];
1693
1694     [m_playerViewController view].frame = [m_parentView convertRect:m_inlineRect toView:[m_playerViewController view].superview];
1695     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1696     [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)];
1697
1698     [[m_playerViewController view] setNeedsLayout];
1699     [[m_playerViewController view] layoutIfNeeded];
1700
1701     if (m_targetStandby && !m_currentMode.hasVideo()) {
1702         [m_window setHidden:YES];
1703         [[m_playerViewController view] setHidden:YES];
1704     }
1705
1706     [CATransaction commit];
1707
1708     finalizeSetup();
1709 }
1710
1711 void VideoFullscreenInterfaceAVKit::finalizeSetup()
1712 {
1713     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
1714         if (m_fullscreenChangeObserver) {
1715             if (!m_hasVideoContentLayer && m_targetMode.hasVideo()) {
1716                 m_finalizeSetupNeedsVideoContentLayer = true;
1717                 m_fullscreenChangeObserver->requestVideoContentLayer();
1718                 return;
1719             }
1720             m_finalizeSetupNeedsVideoContentLayer = false;
1721             if (m_hasVideoContentLayer && !m_targetMode.hasVideo()) {
1722                 m_finalizeSetupNeedsReturnVideoContentLayer = true;
1723                 m_fullscreenChangeObserver->returnVideoContentLayer();
1724                 return;
1725             }
1726             m_finalizeSetupNeedsReturnVideoContentLayer = false;
1727             m_fullscreenChangeObserver->didSetupFullscreen();
1728         }
1729     });
1730 }
1731
1732 void VideoFullscreenInterfaceAVKit::doEnterFullscreen()
1733 {
1734     m_standby = m_targetStandby;
1735
1736     [[m_playerViewController view] layoutIfNeeded];
1737     if (m_targetMode.hasFullscreen() && !m_currentMode.hasFullscreen()) {
1738         m_enterFullscreenNeedsEnterFullscreen = true;
1739         [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, protectedThis = makeRefPtr(this)] (BOOL success, NSError *error) {
1740             enterFullscreenHandler(success, error);
1741         }];
1742         return;
1743     }
1744     m_enterFullscreenNeedsEnterFullscreen = false;
1745
1746     if (m_targetMode.hasPictureInPicture() && !m_currentMode.hasPictureInPicture()) {
1747         m_enterFullscreenNeedsEnterPictureInPicture = true;
1748         if ([m_playerViewController isPictureInPicturePossible])
1749             [m_playerViewController startPictureInPicture];
1750         else
1751             failedToStartPictureInPicture();
1752         return;
1753     }
1754     m_enterFullscreenNeedsEnterPictureInPicture = false;
1755
1756     if (!m_targetMode.hasFullscreen() && m_currentMode.hasFullscreen()) {
1757         m_enterFullscreenNeedsExitFullscreen = true;
1758         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1759             exitFullscreenHandler(success, error);
1760         }];
1761         return;
1762     }
1763     m_enterFullscreenNeedsExitFullscreen = false;
1764
1765     if (!m_targetMode.hasPictureInPicture() && m_currentMode.hasPictureInPicture()) {
1766         m_enterFullscreenNeedsExitPictureInPicture = true;
1767         [m_playerViewController stopPictureInPicture];
1768         return;
1769     }
1770     m_enterFullscreenNeedsExitPictureInPicture = false;
1771
1772     if (m_fullscreenChangeObserver)
1773         m_fullscreenChangeObserver->didEnterFullscreen();
1774 }
1775
1776 void VideoFullscreenInterfaceAVKit::doExitFullscreen()
1777 {
1778     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::doExitFullscreen(%p)", this);
1779
1780     if (m_currentMode.hasVideo() && !m_hasUpdatedInlineRect && m_fullscreenChangeObserver) {
1781         m_exitFullscreenNeedInlineRect = true;
1782         m_fullscreenChangeObserver->requestUpdateInlineRect();
1783         return;
1784     }
1785     m_exitFullscreenNeedInlineRect = false;
1786
1787     if (m_currentMode.hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
1788         m_exitFullscreenNeedsExitFullscreen = true;
1789         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis = makeRefPtr(this), this] (BOOL success, NSError *error) {
1790             exitFullscreenHandler(success, error);
1791         }];
1792         return;
1793     }
1794     m_exitFullscreenNeedsExitFullscreen = false;
1795
1796     if (m_currentMode.hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
1797         m_exitFullscreenNeedsExitPictureInPicture = true;
1798         m_shouldReturnToFullscreenWhenStoppingPiP = false;
1799         [m_window setHidden:NO];
1800         [m_playerViewController stopPictureInPicture];
1801         return;
1802     }
1803     m_exitFullscreenNeedsExitPictureInPicture = false;
1804
1805     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
1806         m_exitFullscreenNeedsReturnContentLayer = true;
1807         m_fullscreenChangeObserver->returnVideoContentLayer();
1808         return;
1809     }
1810     m_exitFullscreenNeedsReturnContentLayer = false;
1811
1812     m_standby = false;
1813
1814     dispatch_async(dispatch_get_main_queue(), [protectedThis = makeRefPtr(this), this] {
1815         if (m_fullscreenChangeObserver)
1816             m_fullscreenChangeObserver->didExitFullscreen();
1817     });
1818 }
1819
1820 void VideoFullscreenInterfaceAVKit::exitFullscreenHandler(BOOL success, NSError* error)
1821 {
1822     if (!success)
1823         WTFLogAlways("-[AVPlayerViewController exitFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1824
1825     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didExitFullscreen(%p) - %d", this, success);
1826
1827     clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1828
1829     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
1830         [m_window setHidden:YES];
1831         [[m_playerViewController view] setHidden:YES];
1832     } else {
1833         [CATransaction begin];
1834         [CATransaction setDisableActions:YES];
1835         [m_playerLayerView setBackgroundColor:clearUIColor()];
1836         [[m_playerViewController view] setBackgroundColor:clearUIColor()];
1837         [CATransaction commit];
1838     }
1839
1840     if (m_enterFullscreenNeedsExitFullscreen)
1841         doEnterFullscreen();
1842     
1843     if (m_exitFullscreenNeedsExitFullscreen)
1844         doExitFullscreen();
1845 }
1846
1847 void VideoFullscreenInterfaceAVKit::enterFullscreenHandler(BOOL success, NSError* error)
1848 {
1849     if (!success)
1850         WTFLogAlways("-[AVPlayerViewController enterFullScreenAnimated:completionHandler:] failed with error %s", [[error localizedDescription] UTF8String]);
1851
1852     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p)", this);
1853     setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
1854     [m_playerViewController setShowsPlaybackControls:YES];
1855
1856     m_restoringFullscreenForPictureInPictureStop = false;
1857
1858     if (m_enterFullscreenNeedsEnterFullscreen)
1859         doEnterFullscreen();
1860 }
1861
1862 void VideoFullscreenInterfaceAVKit::returnToStandby()
1863 {
1864     if (m_hasVideoContentLayer && m_fullscreenChangeObserver) {
1865         m_returnToStandbyNeedsReturnVideoContentLayer = true;
1866         m_fullscreenChangeObserver->returnVideoContentLayer();
1867         return;
1868     }
1869
1870     m_returnToStandbyNeedsReturnVideoContentLayer = false;
1871
1872     [m_window setHidden:YES];
1873     [[m_playerViewController view] setHidden:YES];
1874 }
1875
1876 #endif // ENABLE(FULLSCREEN_API)
1877
1878 NO_RETURN_DUE_TO_ASSERT void VideoFullscreenInterfaceAVKit::watchdogTimerFired()
1879 {
1880     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::watchdogTimerFired(%p) - no exit fullscreen response in %gs; forcing fullscreen hidden.", this, defaultWatchdogTimerInterval.value());
1881     ASSERT_NOT_REACHED();
1882     [m_window setHidden:YES];
1883     [[m_playerViewController view] setHidden:YES];
1884 }
1885
1886 void VideoFullscreenInterfaceAVKit::setMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1887 {
1888     if ((m_currentMode.mode() & mode) == mode)
1889         return;
1890
1891     m_currentMode.setMode(mode);
1892     if (m_videoFullscreenModel)
1893         m_videoFullscreenModel->fullscreenModeChanged(m_currentMode.mode());
1894 }
1895
1896 void VideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1897 {
1898     if ((~m_currentMode.mode() & mode) == mode)
1899         return;
1900
1901     m_currentMode.clearMode(mode);
1902     if (m_videoFullscreenModel)
1903         m_videoFullscreenModel->fullscreenModeChanged(m_currentMode.mode());
1904 }
1905
1906 bool VideoFullscreenInterfaceAVKit::isPlayingVideoInEnhancedFullscreen() const
1907 {
1908     return hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture) && [playerController() isPlaying];
1909 }
1910
1911 #endif // HAVE(AVKIT)
1912
1913 bool WebCore::supportsPictureInPicture()
1914 {
1915 #if PLATFORM(IOS) && HAVE(AVKIT) && !PLATFORM(WATCHOS)
1916     return [getAVPictureInPictureControllerClass() isPictureInPictureSupported];
1917 #else
1918     return false;
1919 #endif
1920 }
1921
1922 #endif // PLATFORM(IOS)