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