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