18d9a0f7f56bd98e78b0859c2d928622933e8e41
[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 }
540
541 void WebVideoFullscreenInterfaceAVKit::videoDimensionsChanged(const FloatSize& videoDimensions)
542 {
543     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
544
545     [playerLayer setVideoDimensions:videoDimensions];
546     [playerController() setContentDimensions:videoDimensions];
547     [m_playerLayerView setNeedsLayout];
548
549     WebAVPictureInPicturePlayerLayerView *pipView = (WebAVPictureInPicturePlayerLayerView *)[m_playerLayerView pictureInPicturePlayerLayerView];
550     WebAVPlayerLayer *pipPlayerLayer = (WebAVPlayerLayer *)[pipView layer];
551     [pipPlayerLayer setVideoDimensions:playerLayer.videoDimensions];
552     [pipView setNeedsLayout];    
553 }
554
555 void WebVideoFullscreenInterfaceAVKit::externalPlaybackChanged(bool enabled, WebPlaybackSessionModel::ExternalPlaybackTargetType, const String&)
556 {
557     [m_playerLayerView setHidden:enabled];
558 }
559
560 void WebVideoFullscreenInterfaceAVKit::applicationDidBecomeActive()
561 {
562     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::applicationDidBecomeActive(%p)", this);
563     if (m_shouldReturnToFullscreenAfterEnteringForeground && m_videoFullscreenModel && m_videoFullscreenModel->isVisible()) {
564         [m_playerViewController stopPictureInPicture];
565         return;
566     }
567
568     // 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
569     // because the originating view is not visible, so hide the fullscreen window.
570     if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard | HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
571         RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
572         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis, this] (BOOL, NSError*) {
573             [m_window setHidden:YES];
574             [[m_playerViewController view] setHidden:YES];
575         }];
576     }
577 }
578
579 @interface UIWindow ()
580 - (BOOL)_isHostedInAnotherProcess;
581 @end
582
583 @interface UIViewController ()
584 @property (nonatomic, assign, setter=_setIgnoreAppSupportedOrientations:) BOOL _ignoreAppSupportedOrientations;
585 @end
586
587 void WebVideoFullscreenInterfaceAVKit::setupFullscreen(UIView& videoView, const WebCore::IntRect& initialRect, UIView* parentView, HTMLMediaElementEnums::VideoFullscreenMode mode, bool allowsPictureInPicturePlayback)
588 {
589     ASSERT(mode != HTMLMediaElementEnums::VideoFullscreenModeNone);
590     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::setupFullscreen(%p)", this);
591
592     m_allowsPictureInPicturePlayback = allowsPictureInPicturePlayback;
593
594     [CATransaction begin];
595     [CATransaction setDisableActions:YES];
596     bool isInPictureInPictureMode = hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
597     m_mode = mode;
598     m_parentView = parentView;
599     m_parentWindow = parentView.window;
600
601     if (![[parentView window] _isHostedInAnotherProcess]) {
602         if (!m_window)
603             m_window = adoptNS([allocUIWindowInstance() initWithFrame:[[getUIScreenClass() mainScreen] bounds]]);
604         [m_window setBackgroundColor:[getUIColorClass() clearColor]];
605         if (!m_viewController)
606             m_viewController = adoptNS([allocUIViewControllerInstance() init]);
607         [[m_viewController view] setFrame:[m_window bounds]];
608         [m_viewController _setIgnoreAppSupportedOrientations:YES];
609         [m_window setRootViewController:m_viewController.get()];
610         [m_window setWindowLevel:getUIRemoteKeyboardLevel() + 1];
611         [m_window makeKeyAndVisible];
612     }
613
614     if (!m_playerLayerView)
615         m_playerLayerView = adoptNS([[getWebAVPlayerLayerViewClass() alloc] init]);
616     [m_playerLayerView setHidden:[playerController() isExternalPlaybackActive]];
617     [m_playerLayerView setBackgroundColor:[getUIColorClass() clearColor]];
618
619     if (!isInPictureInPictureMode) {
620         [m_playerLayerView setVideoView:&videoView];
621         [m_playerLayerView addSubview:&videoView];
622     }
623
624     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
625
626     [playerLayer setModelVideoLayerFrame:CGRectMake(0, 0, initialRect.width(), initialRect.height())];
627     [playerLayer setVideoDimensions:[playerController() contentDimensions]];
628     playerLayer.fullscreenInterface = this;
629
630     if (!m_playerViewController)
631         m_playerViewController = adoptNS([allocAVPlayerViewControllerInstance() initWithPlayerLayerView:m_playerLayerView.get()]);
632
633     [m_playerViewController setShowsPlaybackControls:NO];
634     [m_playerViewController setPlayerController:(AVPlayerController *)playerController()];
635     [m_playerViewController setDelegate:m_playerViewControllerDelegate.get()];
636     [m_playerViewController setAllowsPictureInPicturePlayback:m_allowsPictureInPicturePlayback];
637
638     [playerController() setPictureInPicturePossible:m_allowsPictureInPicturePlayback];
639
640     if (m_viewController) {
641         [m_viewController addChildViewController:m_playerViewController.get()];
642         [[m_viewController view] addSubview:[m_playerViewController view]];
643     } else
644         [parentView addSubview:[m_playerViewController view]];
645
646     [m_playerViewController view].frame = [parentView convertRect:initialRect toView:[m_playerViewController view].superview];
647
648     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
649     [[m_playerViewController view] setAutoresizingMask:(UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin)];
650
651     [[m_playerViewController view] setNeedsLayout];
652     [[m_playerViewController view] layoutIfNeeded];
653
654     [CATransaction commit];
655
656     RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
657     dispatch_async(dispatch_get_main_queue(), [protectedThis, this] {
658         if (m_fullscreenChangeObserver)
659             m_fullscreenChangeObserver->didSetupFullscreen();
660     });
661 }
662
663 void WebVideoFullscreenInterfaceAVKit::enterFullscreen()
664 {
665     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreen(%p)", this);
666
667     m_exitCompleted = false;
668     m_exitRequested = false;
669     m_enterRequested = true;
670
671     if (mode() == HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
672         enterPictureInPicture();
673     else if (mode() == HTMLMediaElementEnums::VideoFullscreenModeStandard)
674         enterFullscreenStandard();
675     else
676         ASSERT_NOT_REACHED();
677 }
678
679 void WebVideoFullscreenInterfaceAVKit::enterPictureInPicture()
680 {
681     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterPictureInPicture(%p)", this);
682     
683     if ([m_playerViewController isPictureInPicturePossible])
684         [m_playerViewController startPictureInPicture];
685     else
686         failedToStartPictureInPicture();
687 }
688
689 void WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard()
690 {
691     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard(%p)", this);
692     RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
693
694     if ([m_playerViewController isPictureInPictureActive]) {
695         // NOTE: The fullscreen mode will be restored in prepareForPictureInPictureStopWithCompletionHandler().
696         m_shouldReturnToFullscreenWhenStoppingPiP = true;
697         [m_playerViewController stopPictureInPicture];
698         return;
699     }
700
701     [m_playerViewController enterFullScreenAnimated:YES completionHandler:[this, protectedThis] (BOOL succeeded, NSError*) {
702         UNUSED_PARAM(succeeded);
703         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::enterFullscreenStandard - lambda(%p) - succeeded(%s)", this, boolString(succeeded));
704         [m_playerViewController setShowsPlaybackControls:YES];
705
706         if (m_fullscreenChangeObserver)
707             m_fullscreenChangeObserver->didEnterFullscreen();
708     }];
709 }
710
711 void WebVideoFullscreenInterfaceAVKit::exitFullscreen(const WebCore::IntRect& finalRect)
712 {
713     m_watchdogTimer.stop();
714
715     m_exitRequested = true;
716     if (m_exitCompleted) {
717         if (m_fullscreenChangeObserver)
718             m_fullscreenChangeObserver->didExitFullscreen();
719         return;
720     }
721     
722     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::exitFullscreen(%p)", this);
723     [m_playerViewController setShowsPlaybackControls:NO];
724     
725     [m_playerViewController view].frame = [m_parentView convertRect:finalRect toView:[m_playerViewController view].superview];
726
727     WebAVPlayerLayer *playerLayer = (WebAVPlayerLayer *)[m_playerLayerView playerLayer];
728     if ([playerLayer videoGravity] != getAVLayerVideoGravityResizeAspect())
729         [playerLayer setVideoGravity:getAVLayerVideoGravityResizeAspect()];
730     [[m_playerViewController view] layoutIfNeeded];
731
732     if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)) {
733         m_shouldReturnToFullscreenWhenStoppingPiP = false;
734         [m_window setHidden:NO];
735         [m_playerViewController stopPictureInPicture];
736     } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture | HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
737         RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
738         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[protectedThis, this] (BOOL, NSError*) {
739             clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
740             [m_window setHidden:NO];
741             [m_playerViewController stopPictureInPicture];
742         }];
743     } else if (isMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
744         RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
745         [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis, this] (BOOL, NSError*) {
746             m_exitCompleted = true;
747
748             [CATransaction begin];
749             [CATransaction setDisableActions:YES];
750             [m_playerLayerView setBackgroundColor:[getUIColorClass() clearColor]];
751             [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
752             [CATransaction commit];
753
754             dispatch_async(dispatch_get_main_queue(), [protectedThis, this]() {
755                 if (m_fullscreenChangeObserver)
756                     m_fullscreenChangeObserver->didExitFullscreen();
757             });
758         }];
759     };
760 }
761
762 @interface UIApplication ()
763 - (void)_setStatusBarOrientation:(UIInterfaceOrientation)o;
764 @end
765
766 @interface UIWindow ()
767 - (UIInterfaceOrientation)interfaceOrientation;
768 @end
769
770 void WebVideoFullscreenInterfaceAVKit::cleanupFullscreen()
771 {
772     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::cleanupFullscreen(%p)", this);
773     if (m_window) {
774         [m_window setHidden:YES];
775         [m_window setRootViewController:nil];
776         if (m_parentWindow)
777             [[getUIApplicationClass() sharedApplication] _setStatusBarOrientation:[m_parentWindow interfaceOrientation]];
778     }
779     
780     [m_playerViewController setDelegate:nil];
781     [m_playerViewController setPlayerController:nil];
782     
783     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
784         [m_playerViewController stopPictureInPicture];
785     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
786         [m_playerViewController exitFullScreenAnimated:NO completionHandler:[] (BOOL, NSError *) { }];
787     
788     [[m_playerViewController view] removeFromSuperview];
789     if (m_viewController)
790         [m_playerViewController removeFromParentViewController];
791     
792     [m_playerLayerView removeFromSuperview];
793     [[m_viewController view] removeFromSuperview];
794
795     m_playerLayerView = nil;
796     m_playerViewController = nil;
797     m_window = nil;
798     m_parentView = nil;
799     m_parentWindow = nil;
800     
801     if (m_fullscreenChangeObserver)
802         m_fullscreenChangeObserver->didCleanupFullscreen();
803
804     m_enterRequested = false;
805 }
806
807 void WebVideoFullscreenInterfaceAVKit::invalidate()
808 {
809     m_videoFullscreenModel = nil;
810     m_fullscreenChangeObserver = nil;
811     
812     cleanupFullscreen();
813 }
814
815 void WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen()
816 {
817     if (!m_enterRequested)
818         return;
819     
820     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture))
821         return;
822     
823     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::requestHideAndExitFullscreen(%p)", this);
824
825     [m_window setHidden:YES];
826     [[m_playerViewController view] setHidden:YES];
827
828     if (webPlaybackSessionModel() && m_videoFullscreenModel && !m_exitRequested) {
829         webPlaybackSessionModel()->pause();
830         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
831     }
832 }
833
834 void WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(bool visible, const IntRect& inlineRect)
835 {
836     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::preparedToReturnToInline(%p) - visible(%s)", this, boolString(visible));
837     if (m_prepareToInlineCallback) {
838         
839         [m_playerViewController view].frame = [m_parentView convertRect:inlineRect toView:[m_playerViewController view].superview];
840
841         std::function<void(bool)> callback = WTFMove(m_prepareToInlineCallback);
842         callback(visible);
843     }
844 }
845
846 bool WebVideoFullscreenInterfaceAVKit::mayAutomaticallyShowVideoPictureInPicture() const
847 {
848     return [playerController() isPlaying] && m_mode == HTMLMediaElementEnums::VideoFullscreenModeStandard && supportsPictureInPicture();
849 }
850
851 void WebVideoFullscreenInterfaceAVKit::fullscreenMayReturnToInline(std::function<void(bool)> callback)
852 {
853     m_prepareToInlineCallback = callback;
854     if (m_fullscreenChangeObserver)
855         m_fullscreenChangeObserver->fullscreenMayReturnToInline();
856 }
857
858 void WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture()
859 {
860     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStartPictureInPicture(%p)", this);
861     setMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
862 }
863
864 void WebVideoFullscreenInterfaceAVKit::didStartPictureInPicture()
865 {
866     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStartPictureInPicture(%p)", this);
867     m_shouldReturnToFullscreenAfterEnteringForeground = [m_playerViewController pictureInPictureWasStartedWhenEnteringBackground];
868     [m_playerViewController setShowsPlaybackControls:YES];
869
870     if (m_mode & HTMLMediaElementEnums::VideoFullscreenModeStandard) {
871         if (![m_playerViewController pictureInPictureWasStartedWhenEnteringBackground]) {
872             RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
873             [m_playerViewController exitFullScreenAnimated:YES completionHandler:[protectedThis, this] (BOOL, NSError*) {
874                 [m_window setHidden:YES];
875                 [[m_playerViewController view] setHidden:YES];
876             }];
877         }
878     } else {
879         [m_window setHidden:YES];
880         [[m_playerViewController view] setHidden:YES];
881     }
882
883     if (m_fullscreenChangeObserver)
884         m_fullscreenChangeObserver->didEnterFullscreen();
885 }
886
887 void WebVideoFullscreenInterfaceAVKit::failedToStartPictureInPicture()
888 {
889     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::failedToStartPictureInPicture(%p)", this);
890     [m_playerViewController setShowsPlaybackControls:YES];
891
892     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
893         return;
894
895     m_exitCompleted = true;
896
897     if (m_fullscreenChangeObserver)
898         m_fullscreenChangeObserver->didEnterFullscreen();
899
900     if (m_videoFullscreenModel)
901         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
902 }
903
904 void WebVideoFullscreenInterfaceAVKit::willStopPictureInPicture()
905 {
906     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::willStopPictureInPicture(%p)", this);
907     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard))
908         return;
909
910     [m_window setHidden:NO];
911     [[m_playerViewController view] setHidden:NO];
912
913     if (m_videoFullscreenModel)
914         m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
915 }
916
917 void WebVideoFullscreenInterfaceAVKit::didStopPictureInPicture()
918 {
919     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::didStopPictureInPicture(%p)", this);
920     if (hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard)) {
921         // ASSUMPTION: we are exiting pip because we are entering fullscreen
922         clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
923         [m_playerViewController setShowsPlaybackControls:YES];
924
925         if (m_fullscreenChangeObserver)
926             m_fullscreenChangeObserver->didEnterFullscreen();
927         return;
928     }
929
930     m_exitCompleted = true;
931
932     [m_playerLayerView setBackgroundColor:[getUIColorClass() clearColor]];
933     [[m_playerViewController view] setBackgroundColor:[getUIColorClass() clearColor]];
934
935     clearMode(HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
936     
937     if (m_fullscreenChangeObserver)
938         m_fullscreenChangeObserver->didExitFullscreen();
939 }
940
941 void WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(void (^completionHandler)(BOOL restored))
942 {
943     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler(%p)", this);
944     if (m_shouldReturnToFullscreenWhenStoppingPiP || m_shouldReturnToFullscreenAfterEnteringForeground) {
945         m_shouldReturnToFullscreenWhenStoppingPiP = false;
946         m_shouldReturnToFullscreenAfterEnteringForeground = false;
947
948         // ASSUMPTION: we are exiting pip because we are entering fullscreen
949         [m_window setHidden:NO];
950         [[m_playerViewController view] setHidden:NO];
951
952         [m_playerViewController enterFullScreenAnimated:YES completionHandler:^(BOOL success, NSError*) {
953             setMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
954             completionHandler(success);
955         }];
956         return;
957     }
958
959     RefPtr<WebVideoFullscreenInterfaceAVKit> protectedThis(this);
960     RetainPtr<id> strongCompletionHandler = adoptNS([completionHandler copy]);
961     fullscreenMayReturnToInline([protectedThis, strongCompletionHandler](bool restored)  {
962         LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::prepareForPictureInPictureStopWithCompletionHandler lambda(%p) - restored(%s)", protectedThis.get(), boolString(restored));
963         void (^completionHandler)(BOOL restored) = strongCompletionHandler.get();
964         completionHandler(restored);
965     });
966 }
967
968 bool WebVideoFullscreenInterfaceAVKit::shouldExitFullscreenWithReason(WebVideoFullscreenInterfaceAVKit::ExitFullScreenReason reason)
969 {
970     if (!m_videoFullscreenModel)
971         return true;
972
973     if (reason == ExitFullScreenReason::PictureInPictureStarted) {
974         if ([m_playerViewController pictureInPictureWasStartedWhenEnteringBackground])
975             return false;
976
977         m_shouldReturnToFullscreenWhenStoppingPiP = hasMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
978         clearMode(HTMLMediaElementEnums::VideoFullscreenModeStandard);
979         return true;
980     }
981
982     if (webPlaybackSessionModel() && (reason == ExitFullScreenReason::DoneButtonTapped || reason == ExitFullScreenReason::RemoteControlStopEventReceived))
983         webPlaybackSessionModel()->pause();
984     
985     m_videoFullscreenModel->requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenModeNone, reason == ExitFullScreenReason::DoneButtonTapped);
986
987     if (!m_watchdogTimer.isActive())
988         m_watchdogTimer.startOneShot(DefaultWatchdogTimerInterval);
989
990     return false;
991 }
992
993 NO_RETURN_DUE_TO_ASSERT void WebVideoFullscreenInterfaceAVKit::watchdogTimerFired()
994 {
995     LOG(Fullscreen, "WebVideoFullscreenInterfaceAVKit::watchdogTimerFired(%p) - no exit fullscreen response in %gs; forcing exit", this);
996     ASSERT_NOT_REACHED();
997     exitFullscreen(IntRect());
998 }
999
1000 void WebVideoFullscreenInterfaceAVKit::setMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1001 {
1002     HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode | mode;
1003     if (m_mode == newMode)
1004         return;
1005
1006     m_mode = newMode;
1007     if (m_videoFullscreenModel)
1008         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1009 }
1010
1011 void WebVideoFullscreenInterfaceAVKit::clearMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
1012 {
1013     HTMLMediaElementEnums::VideoFullscreenMode newMode = m_mode & ~mode;
1014     if (m_mode == newMode)
1015         return;
1016
1017     m_mode = newMode;
1018     if (m_videoFullscreenModel)
1019         m_videoFullscreenModel->fullscreenModeChanged(m_mode);
1020 }
1021
1022 #endif // HAVE(AVKIT)
1023
1024 bool WebCore::supportsPictureInPicture()
1025 {
1026 #if PLATFORM(IOS) && HAVE(AVKIT)
1027     return [getAVPictureInPictureControllerClass() isPictureInPictureSupported];
1028 #else
1029     return false;
1030 #endif
1031 }
1032
1033 #endif // PLATFORM(IOS)