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