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