ce62973aed707c0b6dc807eb28131fa75b919f74
[WebKit-https.git] / Source / WebCore / platform / ios / VideoFullscreenInterfaceAVKit.mm
1 /*
2  * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #if PLATFORM(IOS)
29 #import "VideoFullscreenInterfaceAVKit.h"
30
31 #if HAVE(AVKIT)
32
33 #import "GeometryUtilities.h"
34 #import "Logging.h"
35 #import "PlaybackSessionInterfaceAVKit.h"
36 #import "RuntimeApplicationChecks.h"
37 #import "TimeRanges.h"
38 #import "VideoFullscreenChangeObserver.h"
39 #import "VideoFullscreenModel.h"
40 #import "WebAVPlayerController.h"
41 #import "WebCoreSystemInterface.h"
42 #import <AVFoundation/AVTime.h>
43 #import <UIKit/UIKit.h>
44 #import <objc/message.h>
45 #import <objc/runtime.h>
46 #import <pal/spi/cocoa/AVKitSPI.h>
47 #import <wtf/RetainPtr.h>
48 #import <wtf/text/CString.h>
49 #import <wtf/text/WTFString.h>
50
51 using namespace WebCore;
52
53 // Soft-linking headers must be included last since they #define functions, constants, etc.
54 #import "CoreMediaSoftLink.h"
55
56 SOFT_LINK_FRAMEWORK(AVFoundation)
57 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
58 SOFT_LINK_CONSTANT(AVFoundation, AVLayerVideoGravityResize, NSString *)
59 SOFT_LINK_CONSTANT(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *)
60 SOFT_LINK_CONSTANT(AVFoundation, AVLayerVideoGravityResizeAspectFill, NSString *)
61
62 SOFT_LINK_FRAMEWORK_OPTIONAL(AVKit)
63 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPictureInPictureController)
64 SOFT_LINK_CLASS_OPTIONAL(AVKit, AVPlayerViewController)
65 SOFT_LINK_CLASS_OPTIONAL(AVKit, __AVPlayerLayerView)
66
67 SOFT_LINK_FRAMEWORK(UIKit)
68 SOFT_LINK_CLASS(UIKit, UIApplication)
69 SOFT_LINK_CLASS(UIKit, UIScreen)
70 SOFT_LINK_CLASS(UIKit, UIWindow)
71 SOFT_LINK_CLASS(UIKit, UIView)
72 SOFT_LINK_CLASS(UIKit, UIViewController)
73 SOFT_LINK_CLASS(UIKit, UIColor)
74 SOFT_LINK_CONSTANT(UIKit, UITextEffectsBeneathStatusBarWindowLevel, UIWindowLevel)
75
76 static UIColor *clearUIColor()
77 {
78     return (UIColor *)[getUIColorClass() clearColor];
79 }
80
81 #if !LOG_DISABLED
82 static const char* boolString(bool val)
83 {
84     return val ? "true" : "false";
85 }
86 #endif
87
88 static const Seconds defaultWatchdogTimerInterval { 1_s };
89
90 @class WebAVMediaSelectionOption;
91
92 @interface WebAVPlayerViewControllerDelegate : NSObject <AVPlayerViewControllerDelegate_WebKitOnly> {
93     RefPtr<VideoFullscreenInterfaceAVKit> _fullscreenInterface;
94 }
95 @property (assign) VideoFullscreenInterfaceAVKit* fullscreenInterface;
96 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason;
97 @end
98
99 @implementation WebAVPlayerViewControllerDelegate
100 - (VideoFullscreenInterfaceAVKit*)fullscreenInterface
101 {
102     return _fullscreenInterface.get();
103 }
104
105 - (void)setFullscreenInterface:(VideoFullscreenInterfaceAVKit*)fullscreenInterface
106 {
107     _fullscreenInterface = fullscreenInterface;
108 }
109
110 - (void)playerViewControllerWillStartPictureInPicture:(AVPlayerViewController *)playerViewController
111 {
112     UNUSED_PARAM(playerViewController);
113     self.fullscreenInterface->willStartPictureInPicture();
114 }
115
116 - (void)playerViewControllerDidStartPictureInPicture:(AVPlayerViewController *)playerViewController
117 {
118     UNUSED_PARAM(playerViewController);
119     self.fullscreenInterface->didStartPictureInPicture();
120 }
121
122 - (void)playerViewControllerFailedToStartPictureInPicture:(AVPlayerViewController *)playerViewController withError:(NSError *)error
123 {
124     UNUSED_PARAM(playerViewController);
125     UNUSED_PARAM(error);
126     self.fullscreenInterface->failedToStartPictureInPicture();
127 }
128
129 - (void)playerViewControllerWillStopPictureInPicture:(AVPlayerViewController *)playerViewController
130 {
131     UNUSED_PARAM(playerViewController);
132     self.fullscreenInterface->willStopPictureInPicture();
133 }
134
135 - (void)playerViewControllerDidStopPictureInPicture:(AVPlayerViewController *)playerViewController
136 {
137     UNUSED_PARAM(playerViewController);
138     self.fullscreenInterface->didStopPictureInPicture();
139 }
140
141 static VideoFullscreenInterfaceAVKit::ExitFullScreenReason convertToExitFullScreenReason(AVPlayerViewControllerExitFullScreenReason reason)
142 {
143     switch (reason) {
144     case AVPlayerViewControllerExitFullScreenReasonDoneButtonTapped:
145         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::DoneButtonTapped;
146     case AVPlayerViewControllerExitFullScreenReasonFullScreenButtonTapped:
147         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::FullScreenButtonTapped;
148     case AVPlayerViewControllerExitFullScreenReasonPictureInPictureStarted:
149         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::PictureInPictureStarted;
150     case AVPlayerViewControllerExitFullScreenReasonPinchGestureHandled:
151         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::PinchGestureHandled;
152     case AVPlayerViewControllerExitFullScreenReasonRemoteControlStopEventReceived:
153         return VideoFullscreenInterfaceAVKit::ExitFullScreenReason::RemoteControlStopEventReceived;
154     }
155 }
156
157 - (BOOL)playerViewController:(AVPlayerViewController *)playerViewController shouldExitFullScreenWithReason:(AVPlayerViewControllerExitFullScreenReason)reason
158 {
159     UNUSED_PARAM(playerViewController);
160     return self.fullscreenInterface->shouldExitFullscreenWithReason(convertToExitFullScreenReason(reason));
161 }
162
163 - (void)playerViewController:(AVPlayerViewController *)playerViewController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL restored))completionHandler
164 {
165     UNUSED_PARAM(playerViewController);
166     self.fullscreenInterface->prepareForPictureInPictureStopWithCompletionHandler(completionHandler);
167 }
168 @end
169
170 @interface WebAVPlayerLayer : CALayer
171 @property (nonatomic, retain) NSString *videoGravity;
172 @property (nonatomic, getter=isReadyForDisplay) BOOL readyForDisplay;
173 @property (nonatomic, assign) VideoFullscreenInterfaceAVKit* fullscreenInterface;
174 @property (nonatomic, retain) AVPlayerController *playerController;
175 @property (nonatomic, retain) CALayer *videoSublayer;
176 @property (nonatomic, copy, nullable) NSDictionary *pixelBufferAttributes;
177 @property CGSize videoDimensions;
178 @property CGRect modelVideoLayerFrame;
179 @end
180
181 @implementation WebAVPlayerLayer {
182     RefPtr<VideoFullscreenInterfaceAVKit> _fullscreenInterface;
183     RetainPtr<WebAVPlayerController> _avPlayerController;
184     RetainPtr<CALayer> _videoSublayer;
185     RetainPtr<NSString> _videoGravity;
186 }
187
188 - (instancetype)init
189 {
190     self = [super init];
191     if (self) {
192         [self setMasksToBounds:YES];
193         _videoGravity = getAVLayerVideoGravityResizeAspect();
194     }
195     return self;
196 }
197
198 - (void)dealloc
199 {
200     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
201     [_pixelBufferAttributes release];
202     [super dealloc];
203 }
204
205 - (VideoFullscreenInterfaceAVKit*)fullscreenInterface
206 {
207     return _fullscreenInterface.get();
208 }
209
210 - (void)setFullscreenInterface:(VideoFullscreenInterfaceAVKit*)fullscreenInterface
211 {
212     _fullscreenInterface = fullscreenInterface;
213 }
214
215 - (AVPlayerController *)playerController
216 {
217     return (AVPlayerController *)_avPlayerController.get();
218 }
219
220 - (void)setPlayerController:(AVPlayerController *)playerController
221 {
222     ASSERT(!playerController || [playerController isKindOfClass:[WebAVPlayerController class]]);
223     _avPlayerController = (WebAVPlayerController *)playerController;
224 }
225
226 - (void)setVideoSublayer:(CALayer *)videoSublayer
227 {
228     _videoSublayer = videoSublayer;
229 }
230
231 - (CALayer*)videoSublayer
232 {
233     return _videoSublayer.get();
234 }
235
236 - (void)layoutSublayers
237 {
238     if ([_videoSublayer superlayer] != self)
239         return;
240
241     if (![_avPlayerController delegate])
242         return;
243
244     [_videoSublayer setPosition:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];
245
246     if (self.videoDimensions.height <= 0 || self.videoDimensions.width <= 0)
247         return;
248
249     FloatRect sourceVideoFrame;
250     FloatRect targetVideoFrame;
251     float videoAspectRatio = self.videoDimensions.width / self.videoDimensions.height;
252     
253     if ([getAVLayerVideoGravityResize() isEqualToString:self.videoGravity]) {
254         sourceVideoFrame = self.modelVideoLayerFrame;
255         targetVideoFrame = self.bounds;
256     } else if ([getAVLayerVideoGravityResizeAspect() isEqualToString:self.videoGravity]) {
257         sourceVideoFrame = largestRectWithAspectRatioInsideRect(videoAspectRatio, self.modelVideoLayerFrame);
258         targetVideoFrame = largestRectWithAspectRatioInsideRect(videoAspectRatio, self.bounds);
259     } else if ([getAVLayerVideoGravityResizeAspectFill() isEqualToString:self.videoGravity]) {
260         sourceVideoFrame = smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.modelVideoLayerFrame);
261         self.modelVideoLayerFrame = CGRectMake(0, 0, sourceVideoFrame.width(), sourceVideoFrame.height());
262         ASSERT(_fullscreenInterface->model());
263         _fullscreenInterface->model()->setVideoLayerFrame(self.modelVideoLayerFrame);
264         targetVideoFrame = smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.bounds);
265     } else
266         ASSERT_NOT_REACHED();
267
268     UIView *view = (UIView *)[_videoSublayer delegate];
269     CGAffineTransform transform = CGAffineTransformMakeScale(targetVideoFrame.width() / sourceVideoFrame.width(), targetVideoFrame.height() / sourceVideoFrame.height());
270     [view setTransform:transform];
271     
272     NSTimeInterval animationDuration = [CATransaction animationDuration];
273     dispatch_async(dispatch_get_main_queue(), ^ {
274         [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
275
276         [self performSelector:@selector(resolveBounds) withObject:nil afterDelay:animationDuration + 0.1];
277     });
278 }
279
280 - (void)resolveBounds
281 {
282     [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resolveBounds) object:nil];
283     if (![_avPlayerController delegate])
284         return;
285     
286     if ([_videoSublayer superlayer] != self)
287         return;
288     
289     if (CGRectEqualToRect(self.modelVideoLayerFrame, [self bounds]) && CGAffineTransformIsIdentity([(UIView *)[_videoSublayer delegate] transform]))
290         return;
291     
292     [CATransaction begin];
293     [CATransaction setAnimationDuration:0];
294     [CATransaction setDisableActions:YES];
295     
296     if (!CGRectEqualToRect(self.modelVideoLayerFrame, [self bounds])) {
297         self.modelVideoLayerFrame = [self bounds];
298         ASSERT(_fullscreenInterface->model());
299         _fullscreenInterface->model()->setVideoLayerFrame(self.modelVideoLayerFrame);
300     }
301     [(UIView *)[_videoSublayer delegate] setTransform:CGAffineTransformIdentity];
302     
303     [CATransaction commit];
304 }
305
306 - (void)setVideoGravity:(NSString *)videoGravity
307 {
308     _videoGravity = videoGravity;
309     
310     if (![_avPlayerController delegate])
311         return;
312
313     WebCore::VideoFullscreenModel::VideoGravity gravity = WebCore::VideoFullscreenModel::VideoGravityResizeAspect;
314     if (videoGravity == getAVLayerVideoGravityResize())
315         gravity = WebCore::VideoFullscreenModel::VideoGravityResize;
316     if (videoGravity == getAVLayerVideoGravityResizeAspect())
317         gravity = WebCore::VideoFullscreenModel::VideoGravityResizeAspect;
318     else if (videoGravity == getAVLayerVideoGravityResizeAspectFill())
319         gravity = WebCore::VideoFullscreenModel::VideoGravityResizeAspectFill;
320     else
321         ASSERT_NOT_REACHED();
322     
323     ASSERT(_fullscreenInterface->model());
324     _fullscreenInterface->model()->setVideoLayerGravity(gravity);
325 }
326
327 - (NSString *)videoGravity
328 {
329     return _videoGravity.get();
330 }
331
332 - (CGRect)videoRect
333 {
334     if (self.videoDimensions.width <= 0 || self.videoDimensions.height <= 0)
335         return self.bounds;
336     
337     float videoAspectRatio = self.videoDimensions.width / self.videoDimensions.height;
338
339     if ([getAVLayerVideoGravityResizeAspect() isEqualToString:self.videoGravity])
340         return largestRectWithAspectRatioInsideRect(videoAspectRatio, self.bounds);
341     if ([getAVLayerVideoGravityResizeAspectFill() isEqualToString:self.videoGravity])
342         return smallestRectWithAspectRatioAroundRect(videoAspectRatio, self.bounds);
343
344     return self.bounds;
345 }
346
347 + (NSSet *)keyPathsForValuesAffectingVideoRect
348 {
349     return [NSSet setWithObjects:@"videoDimensions", @"videoGravity", nil];
350 }
351
352 @end
353
354 @interface WebAVPictureInPicturePlayerLayerView : UIView
355 @end
356
357 static Class WebAVPictureInPicturePlayerLayerView_layerClass(id, SEL)
358 {
359     return [WebAVPlayerLayer class];
360 }
361
362 static WebAVPictureInPicturePlayerLayerView *allocWebAVPictureInPicturePlayerLayerViewInstance()
363 {
364     static Class theClass = nil;
365     static dispatch_once_t onceToken;
366     dispatch_once(&onceToken, ^ {
367         theClass = objc_allocateClassPair(getUIViewClass(), "WebAVPictureInPicturePlayerLayerView", 0);
368         objc_registerClassPair(theClass);
369         Class metaClass = objc_getMetaClass("WebAVPictureInPicturePlayerLayerView");
370         class_addMethod(metaClass, @selector(layerClass), (IMP)WebAVPictureInPicturePlayerLayerView_layerClass, "@@:");
371     });
372     
373     return (WebAVPictureInPicturePlayerLayerView *)[theClass alloc];
374 }
375
376 @interface WebAVPlayerLayerView : __AVPlayerLayerView
377 @property (retain) UIView* videoView;
378 @end
379
380 static Class WebAVPlayerLayerView_layerClass(id, SEL)
381 {
382     return [WebAVPlayerLayer class];
383 }
384
385 static AVPlayerController *WebAVPlayerLayerView_playerController(id aSelf, SEL)
386 {
387     __AVPlayerLayerView *playerLayer = aSelf;
388     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayer playerLayer];
389     return [webAVPlayerLayer playerController];
390 }
391
392 static void WebAVPlayerLayerView_setPlayerController(id aSelf, SEL, AVPlayerController *playerController)
393 {
394     __AVPlayerLayerView *playerLayerView = aSelf;
395     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
396     [webAVPlayerLayer setPlayerController: playerController];
397 }
398
399 static UIView *WebAVPlayerLayerView_videoView(id aSelf, SEL)
400 {
401     __AVPlayerLayerView *playerLayer = aSelf;
402     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayer playerLayer];
403     CALayer* videoLayer = [webAVPlayerLayer videoSublayer];
404     if (!videoLayer || !videoLayer.delegate)
405         return nil;
406     ASSERT([[videoLayer delegate] isKindOfClass:getUIViewClass()]);
407     return (UIView *)[videoLayer delegate];
408 }
409
410 static void WebAVPlayerLayerView_setVideoView(id aSelf, SEL, UIView *videoView)
411 {
412     __AVPlayerLayerView *playerLayerView = aSelf;
413     WebAVPlayerLayer *webAVPlayerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
414     [webAVPlayerLayer setVideoSublayer:[videoView layer]];
415 }
416
417 static void WebAVPlayerLayerView_startRoutingVideoToPictureInPicturePlayerLayerView(id aSelf, SEL)
418 {
419     WebAVPlayerLayerView *playerLayerView = aSelf;
420     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[playerLayerView pictureInPicturePlayerLayerView];
421
422     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
423     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
424     [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()];
425     [pipPlayerLayer setVideoSublayer:playerLayer.videoSublayer];
426     [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions];
427     [pipPlayerLayer setVideoGravity:playerLayer.videoGravity];
428     [pipPlayerLayer setModelVideoLayerFrame:playerLayer.modelVideoLayerFrame];
429     [pipPlayerLayer setPlayerController:playerLayer.playerController];
430     [pipPlayerLayer setFullscreenInterface:playerLayer.fullscreenInterface];
431     [pipView addSubview:playerLayerView.videoView];
432 }
433
434 static void WebAVPlayerLayerView_stopRoutingVideoToPictureInPicturePlayerLayerView(id aSelf, SEL)
435 {
436     WebAVPlayerLayerView *playerLayerView = aSelf;
437     if (UIView *videoView = playerLayerView.videoView)
438         [playerLayerView addSubview:videoView];
439     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[playerLayerView pictureInPicturePlayerLayerView];
440     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[playerLayerView playerLayer];
441     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
442     [playerLayer setModelVideoLayerFrame:pipPlayerLayer.modelVideoLayerFrame];
443 }
444
445 static WebAVPictureInPicturePlayerLayerView *WebAVPlayerLayerView_pictureInPicturePlayerLayerView(id aSelf, SEL)
446 {
447     WebAVPlayerLayerView *playerLayerView = aSelf;
448     WebAVPictureInPicturePlayerLayerView *pipView = [playerLayerView valueForKey:@"_pictureInPicturePlayerLayerView"];
449     if (!pipView) {
450         pipView = [allocWebAVPictureInPicturePlayerLayerViewInstance() initWithFrame:CGRectZero];
451         [playerLayerView setValue:pipView forKey:@"_pictureInPicturePlayerLayerView"];
452     }
453     return pipView;
454 }
455
456 static void WebAVPlayerLayerView_dealloc(id aSelf, SEL)
457 {
458     WebAVPlayerLayerView *playerLayerView = aSelf;
459     RetainPtr<WebAVPictureInPicturePlayerLayerView> pipView = adoptNS([playerLayerView valueForKey:@"_pictureInPicturePlayerLayerView"]);
460     [playerLayerView setValue:nil forKey:@"_pictureInPicturePlayerLayerView"];
461     objc_super superClass { playerLayerView, get__AVPlayerLayerViewClass() };
462     auto super_dealloc = reinterpret_cast<void(*)(objc_super*, SEL)>(objc_msgSendSuper);
463     super_dealloc(&superClass, @selector(dealloc));
464 }
465
466 #pragma mark - Methods
467
468 static WebAVPlayerLayerView *allocWebAVPlayerLayerViewInstance()
469 {
470     static Class theClass = nil;
471     static dispatch_once_t onceToken;
472     dispatch_once(&onceToken, ^ {
473         theClass = objc_allocateClassPair(get__AVPlayerLayerViewClass(), "WebAVPlayerLayerView", 0);
474         class_addMethod(theClass, @selector(dealloc), (IMP)WebAVPlayerLayerView_dealloc, "v@:");
475         class_addMethod(theClass, @selector(setPlayerController:), (IMP)WebAVPlayerLayerView_setPlayerController, "v@:@");
476         class_addMethod(theClass, @selector(playerController), (IMP)WebAVPlayerLayerView_playerController, "@@:");
477         class_addMethod(theClass, @selector(setVideoView:), (IMP)WebAVPlayerLayerView_setVideoView, "v@:@");
478         class_addMethod(theClass, @selector(videoView), (IMP)WebAVPlayerLayerView_videoView, "@@:");
479         class_addMethod(theClass, @selector(startRoutingVideoToPictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_startRoutingVideoToPictureInPicturePlayerLayerView, "v@:");
480         class_addMethod(theClass, @selector(stopRoutingVideoToPictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_stopRoutingVideoToPictureInPicturePlayerLayerView, "v@:");
481         class_addMethod(theClass, @selector(pictureInPicturePlayerLayerView), (IMP)WebAVPlayerLayerView_pictureInPicturePlayerLayerView, "@@:");
482         
483         class_addIvar(theClass, "_pictureInPicturePlayerLayerView", sizeof(WebAVPictureInPicturePlayerLayerView *), log2(sizeof(WebAVPictureInPicturePlayerLayerView *)), "@");
484         
485         objc_registerClassPair(theClass);
486         Class metaClass = objc_getMetaClass("WebAVPlayerLayerView");
487         class_addMethod(metaClass, @selector(layerClass), (IMP)WebAVPlayerLayerView_layerClass, "@@:");
488     });
489     return (WebAVPlayerLayerView *)[theClass alloc];
490 }
491
492 Ref<VideoFullscreenInterfaceAVKit> VideoFullscreenInterfaceAVKit::create(PlaybackSessionInterfaceAVKit& playbackSessionInterface)
493 {
494     Ref<VideoFullscreenInterfaceAVKit> interface = adoptRef(*new VideoFullscreenInterfaceAVKit(playbackSessionInterface));
495     [interface->m_playerViewControllerDelegate setFullscreenInterface:interface.ptr()];
496     return interface;
497 }
498
499 VideoFullscreenInterfaceAVKit::VideoFullscreenInterfaceAVKit(PlaybackSessionInterfaceAVKit& playbackSessionInterface)
500     : m_playbackSessionInterface(playbackSessionInterface)
501     , m_playerViewControllerDelegate(adoptNS([[WebAVPlayerViewControllerDelegate alloc] init]))
502     , m_watchdogTimer(RunLoop::main(), this, &VideoFullscreenInterfaceAVKit::watchdogTimerFired)
503 {
504 }
505
506 VideoFullscreenInterfaceAVKit::~VideoFullscreenInterfaceAVKit()
507 {
508     WebAVPlayerController* playerController = this->playerController();
509     if (playerController && playerController.externalPlaybackActive)
510         externalPlaybackChanged(false, PlaybackSessionModel::TargetTypeNone, "");
511     if (m_videoFullscreenModel)
512         m_videoFullscreenModel->removeClient(*this);
513 }
514
515 WebAVPlayerController *VideoFullscreenInterfaceAVKit::playerController() const
516 {
517     return m_playbackSessionInterface->playerController();
518 }
519
520 void VideoFullscreenInterfaceAVKit::setVideoFullscreenModel(VideoFullscreenModel* model)
521 {
522     if (m_videoFullscreenModel)
523         m_videoFullscreenModel->removeClient(*this);
524
525     m_videoFullscreenModel = model;
526
527     if (m_videoFullscreenModel)
528         m_videoFullscreenModel->addClient(*this);
529
530     hasVideoChanged(m_videoFullscreenModel ? m_videoFullscreenModel->hasVideo() : false);
531     videoDimensionsChanged(m_videoFullscreenModel ? m_videoFullscreenModel->videoDimensions() : FloatSize());
532 }
533
534 void VideoFullscreenInterfaceAVKit::setVideoFullscreenChangeObserver(VideoFullscreenChangeObserver* observer)
535 {
536     m_fullscreenChangeObserver = observer;
537 }
538
539 void VideoFullscreenInterfaceAVKit::hasVideoChanged(bool hasVideo)
540 {
541     [playerController() setHasEnabledVideo:hasVideo];
542     [playerController() setHasVideo:hasVideo];
543 }
544
545 void VideoFullscreenInterfaceAVKit::videoDimensionsChanged(const FloatSize& videoDimensions)
546 {
547     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
548
549     [playerLayer setVideoDimensions:videoDimensions];
550     [playerController() setContentDimensions:videoDimensions];
551     [m_playerLayerView setNeedsLayout];
552
553     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[m_playerLayerView pictureInPicturePlayerLayerView];
554     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
555     [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions];
556     [pipView setNeedsLayout];
557 }
558
559 void VideoFullscreenInterfaceAVKit::externalPlaybackChanged(bool enabled, PlaybackSessionModel::ExternalPlaybackTargetType, const String&)
560 {
561     [m_playerLayerView setHidden:enabled];
562 }
563
564 void VideoFullscreenInterfaceAVKit::applicationDidBecomeActive()
565 {
566     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this);
567     if (m_shouldReturnToFullscreenAfterEnteringForeground && m_videoFullscreenModel && m_videoFullscreenModel->isVisible()) {
568         [m_playerViewController stopPictureInPicture];
569         return;
570     }
571
572     // If we are both in PiP and in Fullscreen (i.e., via auto-PiP), and we did not stop fullscreen upon returning, it must be
573     // because the originating view is not visible, so hide the fullscreen window.
574     if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard | HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
575         RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
576         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis, this] (BOOL, NSError*) {
577             [m_window setHidden:YES];
578             [[m_playerViewController view] setHidden:YES];
579         }];
580     }
581 }
582
583 @interface UIWindow ()
584 - (BOOL)_isHostedInAnotherProcess;
585 @end
586
587 @interface UIViewController ()
588 @property (nonatomic, assign, setter=_setIgnoreAppSupportedOrientations:) BOOL _ignoreAppSupportedOrientations;
589 @end
590
591 void VideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback)
592 {
593     ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
594     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
595
596     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
597
598     [CATransaction begin];
599     [CATransaction setDisableActions:YES];
600     bool isInPictureInPictureMode = hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
601     m_mode = mode;
602     m_parentView = parentView;
603     m_parentWindow = parentView.window;
604
605     if (![[parentView window] _isHostedInAnotherProcess]) {
606         if (!m_window)
607             m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
608         [m_window setBackgroundColor:clearUIColor()];
609         if (!m_viewController)
610             m_viewController = adoptNS([allocUIViewControllerInstance() init]);
611         [[m_viewController view] setFrame:[m_window bounds]];
612         [m_viewController _setIgnoreAppSupportedOrientations:YES];
613         [m_window setRootViewController:m_viewController.get()];
614         [m_window setWindowLevel:getUITextEffectsBeneathStatusBarWindowLevel() + 1];
615         [m_window makeKeyAndVisible];
616     }
617
618     if (!m_playerLayerView)
619         m_playerLayerView = adoptNS([allocWebAVPlayerLayerViewInstance() init]);
620     [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]];
621     [m_playerLayerView setBackgroundColor:clearUIColor()];
622
623     if (!isInPictureInPictureMode) {
624         [m_playerLayerView setVideoView:&videoView];
625         [m_playerLayerView addSubview:&videoView];
626     }
627
628     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
629
630     [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, initialRect.width(), initialRect.height())];
631     [playerLayer setVideoDimensions:[playerController() contentDimensions]];
632     playerLayer.fullscreenInterface = this;
633
634     if (!m_playerViewController)
635         m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithPlayerLayerView:m_playerLayerView.get()]);
636
637     [m_playerViewController setShowsPlaybackControls:NO];
638     [m_playerViewController setPlayerController:(AVPlayerController *)playerController()];
639     [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()];
640     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
641
642     [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback];
643
644     if (m_viewController) {
645         [m_viewController addChildViewController:m_playerViewController.get()];
646         [[m_viewController view] addSubview:[m_playerViewController view]];
647     } else
648         [parentView addSubview:[m_playerViewController view]];
649
650     [m_playerViewController view].frame = [parentView convertRect:initialRect toView:[m_playerViewController view].superview];
651
652     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
653     [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)];
654
655     [[m_playerViewController view] setNeedsLayout];
656     [[m_playerViewController view] layoutIfNeeded];
657
658     [CATransaction commit];
659
660     RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
661     dispatch_async(dispatch_get_main_queue(), [protectedThis, this] {
662         if (m_fullscreenChangeObserver)
663             m_fullscreenChangeObserver->didSetupFullscreen();
664     });
665 }
666
667 void VideoFullscreenInterfaceAVKit::enterFullscreen()
668 {
669     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreen(%p)", this);
670
671     m_exitCompleted = false;
672     m_exitRequested = false;
673     m_enterRequested = true;
674
675     if (mode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
676         enterPictureInPicture();
677     else if (mode() == HTMLMediaElementEnums::VideoFullscreenModeStandard)
678         enterFullscreenStandard();
679     else
680         ASSERT_NOT_REACHED();
681 }
682
683 void VideoFullscreenInterfaceAVKit::enterPictureInPicture()
684 {
685     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterPictureInPicture(%p)", this);
686     
687     if ([m_playerViewController isPictureInPicturePossible])
688         [m_playerViewController startPictureInPicture];
689     else
690         failedToStartPictureInPicture();
691 }
692
693 void VideoFullscreenInterfaceAVKit::enterFullscreenStandard()
694 {
695     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
696     RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
697
698     if ([m_playerViewController isPictureInPictureActive]) {
699         // NOTE: The fullscreen mode will be restored in prepareForPictureInPictureStopWithCompletionHandler().
700         m_shouldReturnToFullscreenWhenStoppingPiP = true;
701         [m_playerViewController stopPictureInPicture];
702         return;
703     }
704
705     [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, protectedThis] (BOOL succeeded, NSError*) {
706         UNUSED_PARAM(succeeded);
707         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
708         [m_playerViewController setShowsPlaybackControls:YES];
709
710         if (m_fullscreenChangeObserver)
711             m_fullscreenChangeObserver->didEnterFullscreen();
712     }];
713 }
714
715 void VideoFullscreenInterfaceAVKit::exitFullscreen(const WebCore::IntRect& finalRect)
716 {
717     m_watchdogTimer.stop();
718
719     m_exitRequested = true;
720     if (m_exitCompleted) {
721         if (m_fullscreenChangeObserver)
722             m_fullscreenChangeObserver->didExitFullscreen();
723         return;
724     }
725     
726     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::exitFullscreen(%p)", this);
727     [m_playerViewController setShowsPlaybackControls:NO];
728     
729     [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:[m_playerViewController view].superview];
730
731     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
732     if ([playerLayer videoGravity] != getAVLayerVideoGravityResizeAspect())
733         [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()];
734     [[m_playerViewController view] layoutIfNeeded];
735
736     if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
737         m_shouldReturnToFullscreenWhenStoppingPiP = false;
738         [m_window setHidden:NO];
739         [m_playerViewController stopPictureInPicture];
740     } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture | HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
741         RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
742         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis, this] (BOOL, NSError*) {
743             clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
744             [m_window setHidden:NO];
745             [m_playerViewController stopPictureInPicture];
746         }];
747     } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
748         RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
749         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis, this] (BOOL, NSError*) {
750             m_exitCompleted = true;
751
752             [CATransaction begin];
753             [CATransaction setDisableActions:YES];
754             [m_playerLayerView setBackgroundColor:clearUIColor()];
755             [[m_playerViewController view] setBackgroundColor:clearUIColor()];
756             [CATransaction commit];
757
758             dispatch_async(dispatch_get_main_queue(), [protectedThis, this]() {
759                 if (m_fullscreenChangeObserver)
760                     m_fullscreenChangeObserver->didExitFullscreen();
761             });
762         }];
763     };
764 }
765
766 void VideoFullscreenInterfaceAVKit::cleanupFullscreen()
767 {
768     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
769     if (m_window) {
770         [m_window setHidden:YES];
771         [m_window setRootViewController:nil];
772     }
773     
774     [m_playerViewController setDelegate:nil];
775     [m_playerViewController setPlayerController:nil];
776     
777     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
778         [m_playerViewController stopPictureInPicture];
779     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
780         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL, NSError *) { }];
781     
782     [[m_playerViewController view] removeFromSuperview];
783     if (m_viewController)
784         [m_playerViewController removeFromParentViewController];
785     
786     [m_playerLayerView removeFromSuperview];
787     [[m_viewController view] removeFromSuperview];
788
789     m_playerLayerView = nil;
790     m_playerViewController = nil;
791     m_window = nil;
792     m_parentView = nil;
793     m_parentWindow = nil;
794     
795     if (m_fullscreenChangeObserver)
796         m_fullscreenChangeObserver->didCleanupFullscreen();
797
798     m_enterRequested = false;
799 }
800
801 void VideoFullscreenInterfaceAVKit::invalidate()
802 {
803     m_videoFullscreenModel = nil;
804     m_fullscreenChangeObserver = nil;
805     
806     cleanupFullscreen();
807 }
808
809 void VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
810 {
811     if (!m_enterRequested)
812         return;
813     
814     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
815         return;
816     
817     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
818
819     [m_window setHidden:YES];
820     [[m_playerViewController view] setHidden:YES];
821
822     if (playbackSessionModel() && m_videoFullscreenModel && !m_exitRequested) {
823         playbackSessionModel()->pause();
824         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
825     }
826 }
827
828 void VideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
829 {
830     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
831     if (m_prepareToInlineCallback) {
832         
833         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview];
834
835         WTF::Function<void(bool)> callback = WTFMove(m_prepareToInlineCallback);
836         callback(visible);
837     }
838 }
839
840 bool VideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
841 {
842     return [playerController() isPlaying] && m_mode == HTMLMediaElementEnums::VideoFullscreenModeStandard && supportsPictureInPicture();
843 }
844
845 void VideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(WTF::Function<void(bool)>&& callback)
846 {
847     m_prepareToInlineCallback = WTFMove(callback);
848     if (m_fullscreenChangeObserver)
849         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
850 }
851
852 void VideoFullscreenInterfaceAVKit::willStartPictureInPicture()
853 {
854     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
855     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
856 }
857
858 void VideoFullscreenInterfaceAVKit::didStartPictureInPicture()
859 {
860     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
861     m_shouldReturnToFullscreenAfterEnteringForeground = [m_playerViewController pictureInPictureWasStartedWhenEnteringBackground];
862     [m_playerViewController setShowsPlaybackControls:YES];
863
864     if (m_mode & HTMLMediaElementEnums::VideoFullscreenModeStandard) {
865         if (![m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]) {
866             RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
867             [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis, this] (BOOL, NSError*) {
868                 [m_window setHidden:YES];
869                 [[m_playerViewController view] setHidden:YES];
870             }];
871         }
872     } else {
873         [m_window setHidden:YES];
874         [[m_playerViewController view] setHidden:YES];
875     }
876
877     if (m_fullscreenChangeObserver)
878         m_fullscreenChangeObserver->didEnterFullscreen();
879 }
880
881 void VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
882 {
883     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
884     [m_playerViewController setShowsPlaybackControls:YES];
885
886     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
887         return;
888
889     m_exitCompleted = true;
890
891     if (m_fullscreenChangeObserver)
892         m_fullscreenChangeObserver->didEnterFullscreen();
893
894     if (m_videoFullscreenModel)
895         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
896 }
897
898 void VideoFullscreenInterfaceAVKit::willStopPictureInPicture()
899 {
900     m_shouldReturnToFullscreenWhenStoppingPiP = false;
901     m_shouldReturnToFullscreenAfterEnteringForeground = false;
902
903     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
904     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard) || m_restoringFullscreenForPictureInPictureStop)
905         return;
906
907     [m_window setHidden:NO];
908     [[m_playerViewController view] setHidden:NO];
909
910     if (m_videoFullscreenModel)
911         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
912 }
913
914 void VideoFullscreenInterfaceAVKit::didStopPictureInPicture()
915 {
916     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
917     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard) || m_restoringFullscreenForPictureInPictureStop) {
918         // ASSUMPTION: we are exiting pip because we are entering fullscreen
919         clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
920         [m_playerViewController setShowsPlaybackControls:YES];
921         return;
922     }
923
924     m_exitCompleted = true;
925
926     [m_playerLayerView setBackgroundColor:clearUIColor()];
927     [[m_playerViewController view] setBackgroundColor:clearUIColor()];
928
929     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
930     
931     if (m_fullscreenChangeObserver)
932         m_fullscreenChangeObserver->didExitFullscreen();
933 }
934
935 void VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
936 {
937     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
938     if (m_shouldReturnToFullscreenWhenStoppingPiP || m_shouldReturnToFullscreenAfterEnteringForeground) {
939         m_shouldReturnToFullscreenWhenStoppingPiP = false;
940         m_shouldReturnToFullscreenAfterEnteringForeground = false;
941         m_restoringFullscreenForPictureInPictureStop = true;
942
943         [m_window setHidden:NO];
944         [[m_playerViewController view] setHidden:NO];
945
946         [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL success, NSError*) {
947             m_restoringFullscreenForPictureInPictureStop = false;
948             setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
949             completionHandler(success);
950             if (m_fullscreenChangeObserver)
951                 m_fullscreenChangeObserver->didEnterFullscreen();
952         }];
953         return;
954     }
955
956     RefPtr<VideoFullscreenInterfaceAVKit> protectedThis(this);
957     RetainPtr<id> strongCompletionHandler = adoptNS([completionHandler copy]);
958     fullscreenMayReturnToInline([protectedThis, strongCompletionHandler](bool restored)  {
959         LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", protectedThis.get(), boolString(restored));
960         void (^completionHandler)(BOOL restored) = strongCompletionHandler.get();
961         completionHandler(restored);
962     });
963 }
964
965 bool VideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(VideoFullscreenInterfaceAVKit::ExitFullScreenReason reason)
966 {
967     if (!m_videoFullscreenModel)
968         return true;
969
970     if (reason == ExitFullScreenReason::PictureInPictureStarted) {
971         if ([m_playerViewController pictureInPictureWasStartedWhenEnteringBackground])
972             return false;
973
974         m_shouldReturnToFullscreenWhenStoppingPiP = hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
975         clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
976         return true;
977     }
978
979     if (playbackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived))
980         playbackSessionModel()->pause();
981
982     BOOL finished = reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::PinchGestureHandled;
983     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, finished);
984
985     if (!m_watchdogTimer.isActive())
986         m_watchdogTimer.startOneShot(defaultWatchdogTimerInterval);
987
988     return false;
989 }
990
991 NO_RETURN_DUE_TO_ASSERT void VideoFullscreenInterfaceAVKit::watchdogTimerFired()
992 {
993     LOG(Fullscreen, "VideoFullscreenInterfaceAVKit::watchdogTimerFired(%p) - no exit fullscreen response in %gs; forcing fullscreen hidden.", this, defaultWatchdogTimerInterval.value());
994     ASSERT_NOT_REACHED();
995     [m_window setHidden:YES];
996     [[m_playerViewController view] setHidden:YES];
997 }
998
999 void VideoFullscreenInterfaceAVKit::setMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1000 {
1001     HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode | mode;
1002     if (m_mode == newMode)
1003         return;
1004
1005     m_mode = newMode;
1006     if (m_videoFullscreenModel)
1007         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1008 }
1009
1010 void VideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1011 {
1012     HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode & ~mode;
1013     if (m_mode == newMode)
1014         return;
1015
1016     m_mode = newMode;
1017     if (m_videoFullscreenModel)
1018         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1019 }
1020
1021 #endif // HAVE(AVKIT)
1022
1023 bool WebCore::supportsPictureInPicture()
1024 {
1025 #if PLATFORM(IOS) && HAVE(AVKIT)
1026     return [getAVPictureInPictureControllerClass() isPictureInPictureSupported];
1027 #else
1028     return false;
1029 #endif
1030 }
1031
1032 #endif // PLATFORM(IOS)