1/*
2 * Copyright (C) 2009, 2010, 2011 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27
28#if ENABLE(FULLSCREEN_API) && !PLATFORM(IOS)
29
30#import "WKFullScreenWindowController.h"
31
32#import "LayerTreeContext.h"
33#import "WKAPICast.h"
34#import "WKViewInternal.h"
35#import "WKViewPrivate.h"
36#import "WebFullScreenManagerProxy.h"
37#import "WebPageProxy.h"
38#import <QuartzCore/QuartzCore.h>
39#import <WebCore/DisplaySleepDisabler.h>
40#import <WebCore/FloatRect.h>
41#import <WebCore/IntRect.h>
42#import <WebCore/LocalizedStrings.h>
43#import <WebCore/WebCoreFullScreenPlaceholderView.h>
44#import <WebCore/WebCoreFullScreenWindow.h>
45#import <WebCore/WebWindowAnimation.h>
46#import <WebKitSystemInterface.h>
47
48using namespace WebKit;
49using namespace WebCore;
50
51static RetainPtr<NSWindow> createBackgroundFullscreenWindow(NSRect frame);
52
53static const NSTimeInterval DefaultWatchdogTimerInterval = 1;
54
55enum FullScreenState : NSInteger {
56    NotInFullScreen,
57    WaitingToEnterFullScreen,
58    EnteringFullScreen,
59    InFullScreen,
60    WaitingToExitFullScreen,
61    ExitingFullScreen,
62};
63
64@interface NSWindow (WebNSWindowDetails)
65- (void)exitFullScreenMode:(id)sender;
66- (void)enterFullScreenMode:(id)sender;
67@end
68
69@interface WKFullScreenWindowController(Private)<NSAnimationDelegate>
70- (void)_replaceView:(NSView*)view with:(NSView*)otherView;
71- (WebPageProxy*)_page;
72- (WebFullScreenManagerProxy*)_manager;
73- (void)_startEnterFullScreenAnimationWithDuration:(NSTimeInterval)duration;
74- (void)_startExitFullScreenAnimationWithDuration:(NSTimeInterval)duration;
75@end
76
77static NSRect convertRectToScreen(NSWindow *window, NSRect rect)
78{
79    return [window convertRectToScreen:rect];
80}
81
82static void makeResponderFirstResponderIfDescendantOfView(NSWindow *window, NSResponder *responder, NSView *view)
83{
84    if ([responder isKindOfClass:[NSView class]] && [(NSView *)responder isDescendantOf:view])
85        [window makeFirstResponder:responder];
86}
87
88@implementation WKFullScreenWindowController
89
90#pragma mark -
91#pragma mark Initialization
92- (id)initWithWindow:(NSWindow *)window webView:(WKView *)webView
93{
94    self = [super initWithWindow:window];
95    if (!self)
96        return nil;
97    [window setDelegate:self];
98    [window setCollectionBehavior:([window collectionBehavior] | NSWindowCollectionBehaviorFullScreenPrimary)];
99    [self windowDidLoad];
100    _webView = webView;
101
102    return self;
103}
104
105- (void)dealloc
106{
107    [[self window] setDelegate:nil];
108
109    [NSObject cancelPreviousPerformRequestsWithTarget:self];
110
111    [[NSNotificationCenter defaultCenter] removeObserver:self];
112
113    if (_repaintCallback) {
114        _repaintCallback->invalidate(CallbackBase::Error::OwnerWasInvalidated);
115        // invalidate() calls completeFinishExitFullScreenAnimationAfterRepaint, which
116        // clears _repaintCallback.
117        ASSERT(!_repaintCallback);
118    }
119
120    [super dealloc];
121}
122
123- (void)windowDidLoad
124{
125    [super windowDidLoad];
126
127    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp];
128}
129
130#pragma mark -
131#pragma mark Accessors
132
133- (BOOL)isFullScreen
134{
135    return _fullScreenState == WaitingToEnterFullScreen
136        || _fullScreenState == EnteringFullScreen
137        || _fullScreenState == InFullScreen;
138}
139
140- (WebCoreFullScreenPlaceholderView*)webViewPlaceholder
141{
142    return _webViewPlaceholder.get();
143}
144
145#pragma mark -
146#pragma mark NSWindowController overrides
147
148- (void)cancelOperation:(id)sender
149{
150    // If the page doesn't respond in DefaultWatchdogTimerInterval seconds, it could be because
151    // the WebProcess has hung, so exit anyway.
152    if (!_watchdogTimer) {
153        [self _manager]->requestExitFullScreen();
154        _watchdogTimer = adoptNS([[NSTimer alloc] initWithFireDate:nil interval:DefaultWatchdogTimerInterval target:self selector:@selector(exitFullScreen) userInfo:nil repeats:NO]);
155        [[NSRunLoop mainRunLoop] addTimer:_watchdogTimer.get() forMode:NSDefaultRunLoopMode];
156    }
157}
158
159#pragma mark -
160#pragma mark Notifications
161
162- (void)applicationDidChangeScreenParameters:(NSNotification*)notification
163{
164    // The user may have changed the main screen by moving the menu bar, or they may have changed
165    // the Dock's size or location, or they may have changed the fullScreen screen's dimensions.
166    // Update our presentation parameters, and ensure that the full screen window occupies the
167    // entire screen:
168    NSWindow* window = [self window];
169    NSRect screenFrame = [[window screen] frame];
170    [window setFrame:screenFrame display:YES];
171    [_backgroundWindow setFrame:screenFrame display:YES];
172}
173
174#pragma mark -
175#pragma mark Exposed Interface
176
177static RetainPtr<CGDataProviderRef> createImageProviderWithCopiedData(CGDataProviderRef sourceProvider)
178{
179    RetainPtr<CFDataRef> data = adoptCF(CGDataProviderCopyData(sourceProvider));
180    return adoptCF(CGDataProviderCreateWithCFData(data.get()));
181}
182
183static RetainPtr<CGImageRef> createImageWithCopiedData(CGImageRef sourceImage)
184{
185    size_t width = CGImageGetWidth(sourceImage);
186    size_t height = CGImageGetHeight(sourceImage);
187    size_t bitsPerComponent = CGImageGetBitsPerComponent(sourceImage);
188    size_t bitsPerPixel = CGImageGetBitsPerPixel(sourceImage);
189    size_t bytesPerRow = CGImageGetBytesPerRow(sourceImage);
190    CGColorSpaceRef colorSpace = CGImageGetColorSpace(sourceImage);
191    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(sourceImage);
192    RetainPtr<CGDataProviderRef> provider = createImageProviderWithCopiedData(CGImageGetDataProvider(sourceImage));
193    bool shouldInterpolate = CGImageGetShouldInterpolate(sourceImage);
194    CGColorRenderingIntent intent = CGImageGetRenderingIntent(sourceImage);
195
196    return adoptCF(CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, bitmapInfo, provider.get(), 0, shouldInterpolate, intent));
197}
198
199- (void)enterFullScreen:(NSScreen *)screen
200{
201    if ([self isFullScreen])
202        return;
203    _fullScreenState = WaitingToEnterFullScreen;
204
205    if (!screen)
206        screen = [NSScreen mainScreen];
207    NSRect screenFrame = [screen frame];
208
209    NSRect webViewFrame = convertRectToScreen([_webView window], [_webView convertRect:[_webView frame] toView:nil]);
210
211    // Flip coordinate system:
212    webViewFrame.origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(webViewFrame);
213
214    CGWindowID windowID = [[_webView window] windowNumber];
215    RetainPtr<CGImageRef> webViewContents = adoptCF(CGWindowListCreateImage(NSRectToCGRect(webViewFrame), kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageShouldBeOpaque));
216
217    // Using the returned CGImage directly would result in calls to the WindowServer every time
218    // the image was painted. Instead, copy the image data into our own process to eliminate that
219    // future overhead.
220    webViewContents = createImageWithCopiedData(webViewContents.get());
221
222    // Screen updates to be re-enabled in _startEnterFullScreenAnimationWithDuration:
223    NSDisableScreenUpdates();
224    [[self window] setAutodisplay:NO];
225
226    NSResponder *webWindowFirstResponder = [[_webView window] firstResponder];
227    [self _manager]->saveScrollPosition();
228    [[self window] setFrame:screenFrame display:NO];
229
230    // Painting is normally suspended when the WKView is removed from the window, but this is
231    // unnecessary in the full-screen animation case, and can cause bugs; see
232    // https://bugs.webkit.org/show_bug.cgi?id=88940 and https://bugs.webkit.org/show_bug.cgi?id=88374
233    // We will resume the normal behavior in _startEnterFullScreenAnimationWithDuration:
234    [_webView _setSuppressVisibilityUpdates:YES];
235
236    // Swap the webView placeholder into place.
237    if (!_webViewPlaceholder) {
238        _webViewPlaceholder = adoptNS([[WebCoreFullScreenPlaceholderView alloc] initWithFrame:[_webView frame]]);
239        [_webViewPlaceholder setAction:@selector(cancelOperation:)];
240    }
241    [_webViewPlaceholder setTarget:nil];
242    [_webViewPlaceholder setContents:(id)webViewContents.get()];
243    [self _replaceView:_webView with:_webViewPlaceholder.get()];
244
245    // Then insert the WebView into the full screen window
246    NSView* contentView = [[self window] contentView];
247    [contentView addSubview:_webView positioned:NSWindowBelow relativeTo:nil];
248    [_webView setFrame:[contentView bounds]];
249
250    makeResponderFirstResponderIfDescendantOfView(self.window, webWindowFirstResponder, _webView);
251
252    [self _manager]->setAnimatingFullScreen(true);
253    [self _manager]->willEnterFullScreen();
254    _savedScale = [self _page]->pageScaleFactor();
255    [self _page]->scalePage(1, IntPoint());
256}
257
258- (void)beganEnterFullScreenWithInitialFrame:(const WebCore::IntRect&)initialFrame finalFrame:(const WebCore::IntRect&)finalFrame
259{
260    if (_fullScreenState != WaitingToEnterFullScreen)
261        return;
262    _fullScreenState = EnteringFullScreen;
263
264    _initialFrame = initialFrame;
265    _finalFrame = finalFrame;
266
267    if (!_backgroundWindow)
268        _backgroundWindow = createBackgroundFullscreenWindow(NSZeroRect);
269
270    [self.window orderBack: self]; // Make sure the full screen window is part of the correct Space.
271    [[self window] enterFullScreenMode:self];
272}
273
274- (void)finishedEnterFullScreenAnimation:(bool)completed
275{
276    if (_fullScreenState != EnteringFullScreen)
277        return;
278
279    if (completed) {
280        _fullScreenState = InFullScreen;
281
282        // Screen updates to be re-enabled ta the end of the current block.
283        NSDisableScreenUpdates();
284        [self _manager]->didEnterFullScreen();
285        [self _manager]->setAnimatingFullScreen(false);
286
287        NSRect windowBounds = [[self window] frame];
288        windowBounds.origin = NSZeroPoint;
289        WKWindowSetClipRect([self window], windowBounds);
290
291        [_fadeAnimation stopAnimation];
292        [_fadeAnimation setWindow:nil];
293        _fadeAnimation = nullptr;
294
295        [_backgroundWindow orderOut:self];
296        [_backgroundWindow setFrame:NSZeroRect display:YES];
297
298        [_webViewPlaceholder setExitWarningVisible:YES];
299        [_webViewPlaceholder setTarget:self];
300    } else {
301        // Transition to fullscreen failed. Clean up.
302        _fullScreenState = NotInFullScreen;
303
304        [_scaleAnimation stopAnimation];
305
306        [_backgroundWindow orderOut:self];
307        [_backgroundWindow setFrame:NSZeroRect display:YES];
308
309        [[self window] setAutodisplay:YES];
310        [_webView _setSuppressVisibilityUpdates:NO];
311
312        NSResponder *firstResponder = [[self window] firstResponder];
313        [self _replaceView:_webViewPlaceholder.get() with:_webView];
314        makeResponderFirstResponderIfDescendantOfView(_webView.window, firstResponder, _webView);
315        [[_webView window] makeKeyAndOrderFront:self];
316
317        [self _manager]->didExitFullScreen();
318        [self _manager]->setAnimatingFullScreen(false);
319        [self _page]->scalePage(_savedScale, IntPoint());
320        [self _manager]->restoreScrollPosition();
321    }
322
323    NSEnableScreenUpdates();
324}
325
326- (void)exitFullScreen
327{
328    if (_watchdogTimer) {
329        [_watchdogTimer invalidate];
330        _watchdogTimer.clear();
331    }
332
333    if (![self isFullScreen])
334        return;
335    _fullScreenState = WaitingToExitFullScreen;
336
337    [_webViewPlaceholder setExitWarningVisible:NO];
338
339    // Screen updates to be re-enabled in _startExitFullScreenAnimationWithDuration: or beganExitFullScreenWithInitialFrame:finalFrame:
340    NSDisableScreenUpdates();
341    [[self window] setAutodisplay:NO];
342
343    // See the related comment in enterFullScreen:
344    // We will resume the normal behavior in _startExitFullScreenAnimationWithDuration:
345    [_webView _setSuppressVisibilityUpdates:YES];
346    [_webViewPlaceholder setTarget:nil];
347
348    [self _manager]->setAnimatingFullScreen(true);
349    [self _manager]->willExitFullScreen();
350}
351
352- (void)beganExitFullScreenWithInitialFrame:(const WebCore::IntRect&)initialFrame finalFrame:(const WebCore::IntRect&)finalFrame
353{
354    if (_fullScreenState != WaitingToExitFullScreen)
355        return;
356    _fullScreenState = ExitingFullScreen;
357
358    if (![[self window] isOnActiveSpace]) {
359        // If the full screen window is not in the active space, the NSWindow full screen animation delegate methods
360        // will never be called. So call finishedExitFullScreenAnimation explicitly.
361        [self finishedExitFullScreenAnimation:YES];
362
363        // Because we are breaking the normal animation pattern, re-enable screen updates
364        // as exitFullScreen has disabled them, but _startExitFullScreenAnimationWithDuration:
365        // will never be called.
366        NSEnableScreenUpdates();
367    }
368
369    [[self window] exitFullScreenMode:self];
370}
371
372- (void)finishedExitFullScreenAnimation:(bool)completed
373{
374    if (_fullScreenState != ExitingFullScreen)
375        return;
376    _fullScreenState = NotInFullScreen;
377
378    // Screen updates to be re-enabled in completeFinishExitFullScreenAnimationAfterRepaint.
379    NSDisableScreenUpdates();
380    [[_webViewPlaceholder window] setAutodisplay:NO];
381
382    NSResponder *firstResponder = [[self window] firstResponder];
383    [self _replaceView:_webViewPlaceholder.get() with:_webView];
384    makeResponderFirstResponderIfDescendantOfView(_webView.window, firstResponder, _webView);
385
386    NSRect windowBounds = [[self window] frame];
387    windowBounds.origin = NSZeroPoint;
388    WKWindowSetClipRect([self window], windowBounds);
389
390    [[self window] orderOut:self];
391    [[self window] setFrame:NSZeroRect display:YES];
392
393    [_scaleAnimation stopAnimation];
394    [_scaleAnimation setWindow:nil];
395    _scaleAnimation = nullptr;
396
397    [_fadeAnimation stopAnimation];
398    [_fadeAnimation setWindow:nil];
399    _fadeAnimation = nullptr;
400
401    [_backgroundWindow orderOut:self];
402    [_backgroundWindow setFrame:NSZeroRect display:YES];
403
404    [[_webView window] makeKeyAndOrderFront:self];
405
406    // These messages must be sent after the swap or flashing will occur during forceRepaint:
407    [self _manager]->didExitFullScreen();
408    [self _manager]->setAnimatingFullScreen(false);
409    [self _page]->scalePage(_savedScale, IntPoint());
410    [self _manager]->restoreScrollPosition();
411
412    if (_repaintCallback) {
413        _repaintCallback->invalidate(CallbackBase::Error::OwnerWasInvalidated);
414        // invalidate() calls completeFinishExitFullScreenAnimationAfterRepaint, which
415        // clears _repaintCallback.
416        ASSERT(!_repaintCallback);
417    }
418    _repaintCallback = VoidCallback::create([self](CallbackBase::Error) {
419        [self completeFinishExitFullScreenAnimationAfterRepaint];
420    });
421    [self _page]->forceRepaint(_repaintCallback);
422}
423
424- (void)completeFinishExitFullScreenAnimationAfterRepaint
425{
426    _repaintCallback = nullptr;
427    [[_webView window] setAutodisplay:YES];
428    [[_webView window] displayIfNeeded];
429    NSEnableScreenUpdates();
430}
431
432- (void)performClose:(id)sender
433{
434    if ([self isFullScreen])
435        [self cancelOperation:sender];
436}
437
438- (void)close
439{
440    // We are being asked to close rapidly, most likely because the page
441    // has closed or the web process has crashed.  Just walk through our
442    // normal exit full screen sequence, but don't wait to be called back
443    // in response.
444    if ([self isFullScreen])
445        [self exitFullScreen];
446
447    if (_fullScreenState == ExitingFullScreen)
448        [self finishedExitFullScreenAnimation:YES];
449
450    [_scaleAnimation stopAnimation];
451    [_scaleAnimation setWindow:nil];
452    [_fadeAnimation stopAnimation];
453    [_fadeAnimation setWindow:nil];
454
455    _webView = nil;
456
457    [super close];
458}
459
460#pragma mark -
461#pragma mark Custom NSWindow Full Screen Animation
462
463- (NSArray *)customWindowsToEnterFullScreenForWindow:(NSWindow *)window
464{
465    return [NSArray arrayWithObjects:[self window], _backgroundWindow.get(), nil];
466}
467
468- (NSArray *)customWindowsToExitFullScreenForWindow:(NSWindow *)window
469{
470    return [NSArray arrayWithObjects:[self window], _backgroundWindow.get(), nil];
471}
472
473- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration
474{
475    [self _startEnterFullScreenAnimationWithDuration:duration];
476}
477
478- (void)window:(NSWindow *)window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration
479{
480    [self _startExitFullScreenAnimationWithDuration:duration];
481}
482
483- (void)windowDidFailToEnterFullScreen:(NSWindow *)window
484{
485    [self finishedEnterFullScreenAnimation:NO];
486}
487
488- (void)windowDidEnterFullScreen:(NSNotification*)notification
489{
490    [self finishedEnterFullScreenAnimation:YES];
491}
492
493- (void)windowDidFailToExitFullScreen:(NSWindow *)window
494{
495    [self finishedExitFullScreenAnimation:NO];
496}
497
498- (void)windowDidExitFullScreen:(NSNotification*)notification
499{
500    [self finishedExitFullScreenAnimation:YES];
501}
502
503#pragma mark -
504#pragma mark Internal Interface
505
506- (WebPageProxy*)_page
507{
508    return toImpl([_webView pageRef]);
509}
510
511- (WebFullScreenManagerProxy*)_manager
512{
513    WebPageProxy* webPage = [self _page];
514    if (!webPage)
515        return 0;
516    return webPage->fullScreenManager();
517}
518
519- (void)_replaceView:(NSView*)view with:(NSView*)otherView
520{
521    [CATransaction begin];
522    [CATransaction setDisableActions:YES];
523    [otherView setFrame:[view frame]];
524    [otherView setAutoresizingMask:[view autoresizingMask]];
525    [otherView removeFromSuperview];
526    [[view superview] addSubview:otherView positioned:NSWindowAbove relativeTo:view];
527    [view removeFromSuperview];
528    [CATransaction commit];
529}
530
531static RetainPtr<NSWindow> createBackgroundFullscreenWindow(NSRect frame)
532{
533    NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
534    [window setOpaque:YES];
535    [window setBackgroundColor:[NSColor blackColor]];
536    [window setReleasedWhenClosed:NO];
537    return adoptNS(window);
538}
539
540static NSRect windowFrameFromApparentFrames(NSRect screenFrame, NSRect initialFrame, NSRect finalFrame)
541{
542    NSRect initialWindowFrame;
543    if (!NSWidth(initialFrame) || !NSWidth(finalFrame) || !NSHeight(initialFrame) || !NSHeight(finalFrame))
544        return screenFrame;
545
546    CGFloat xScale = NSWidth(screenFrame) / NSWidth(finalFrame);
547    CGFloat yScale = NSHeight(screenFrame) / NSHeight(finalFrame);
548    CGFloat xTrans = NSMinX(screenFrame) - NSMinX(finalFrame);
549    CGFloat yTrans = NSMinY(screenFrame) - NSMinY(finalFrame);
550    initialWindowFrame.size = NSMakeSize(NSWidth(initialFrame) * xScale, NSHeight(initialFrame) * yScale);
551    initialWindowFrame.origin = NSMakePoint
552        ( NSMinX(initialFrame) + xTrans / (NSWidth(finalFrame) / NSWidth(initialFrame))
553        , NSMinY(initialFrame) + yTrans / (NSHeight(finalFrame) / NSHeight(initialFrame)));
554    return initialWindowFrame;
555}
556
557- (void)_startEnterFullScreenAnimationWithDuration:(NSTimeInterval)duration
558{
559    NSRect screenFrame = [[[self window] screen] frame];
560    NSRect initialWindowFrame = windowFrameFromApparentFrames(screenFrame, _initialFrame, _finalFrame);
561
562    _scaleAnimation = adoptNS([[WebWindowScaleAnimation alloc] initWithHintedDuration:duration window:[self window] initalFrame:initialWindowFrame finalFrame:screenFrame]);
563
564    [_scaleAnimation setAnimationBlockingMode:NSAnimationNonblocking];
565    [_scaleAnimation setCurrentProgress:0];
566    [_scaleAnimation startAnimation];
567
568    // WKWindowSetClipRect takes window coordinates, so convert from screen coordinates here:
569    NSRect finalBounds = _finalFrame;
570#pragma clang diagnostic push
571#pragma clang diagnostic ignored "-Wdeprecated-declarations"
572    finalBounds.origin = [[self window] convertScreenToBase:finalBounds.origin];
573#pragma clang diagnostic pop
574    WKWindowSetClipRect([self window], finalBounds);
575
576    NSWindow* window = [self window];
577    NSWindowCollectionBehavior behavior = [window collectionBehavior];
578    [window setCollectionBehavior:(behavior | NSWindowCollectionBehaviorCanJoinAllSpaces)];
579    [window makeKeyAndOrderFront:self];
580    [window setCollectionBehavior:behavior];
581
582
583    if (!_backgroundWindow)
584        _backgroundWindow = createBackgroundFullscreenWindow(screenFrame);
585    else
586        [_backgroundWindow setFrame:screenFrame display:NO];
587
588    CGFloat currentAlpha = 0;
589    if (_fadeAnimation) {
590        currentAlpha = [_fadeAnimation currentAlpha];
591        [_fadeAnimation stopAnimation];
592        [_fadeAnimation setWindow:nil];
593    }
594
595    _fadeAnimation = adoptNS([[WebWindowFadeAnimation alloc] initWithDuration:duration
596                                                                     window:_backgroundWindow.get()
597                                                               initialAlpha:currentAlpha
598                                                                 finalAlpha:1]);
599    [_fadeAnimation setAnimationBlockingMode:NSAnimationNonblocking];
600    [_fadeAnimation setCurrentProgress:0];
601    [_fadeAnimation startAnimation];
602
603    [_backgroundWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
604
605    [_webView _setSuppressVisibilityUpdates:NO];
606    [[self window] setAutodisplay:YES];
607    [[self window] displayIfNeeded];
608    NSEnableScreenUpdates();
609}
610
611- (void)_startExitFullScreenAnimationWithDuration:(NSTimeInterval)duration
612{
613    if ([self isFullScreen]) {
614        // We still believe we're in full screen mode, so we must have been asked to exit full
615        // screen by the system full screen button.
616        [self _manager]->requestExitFullScreen();
617        [self exitFullScreen];
618        _fullScreenState = ExitingFullScreen;
619    }
620
621    NSRect screenFrame = [[[self window] screen] frame];
622    NSRect initialWindowFrame = windowFrameFromApparentFrames(screenFrame, _initialFrame, _finalFrame);
623
624    NSRect currentFrame = _scaleAnimation ? [_scaleAnimation currentFrame] : [[self window] frame];
625    _scaleAnimation = adoptNS([[WebWindowScaleAnimation alloc] initWithHintedDuration:duration window:[self window] initalFrame:currentFrame finalFrame:initialWindowFrame]);
626
627    [_scaleAnimation setAnimationBlockingMode:NSAnimationNonblocking];
628    [_scaleAnimation setCurrentProgress:0];
629    [_scaleAnimation startAnimation];
630
631    if (!_backgroundWindow)
632        _backgroundWindow = createBackgroundFullscreenWindow(screenFrame);
633    else
634        [_backgroundWindow setFrame:screenFrame display:NO];
635
636    CGFloat currentAlpha = 1;
637    if (_fadeAnimation) {
638        currentAlpha = [_fadeAnimation currentAlpha];
639        [_fadeAnimation stopAnimation];
640        [_fadeAnimation setWindow:nil];
641    }
642    _fadeAnimation = adoptNS([[WebWindowFadeAnimation alloc] initWithDuration:duration
643                                                                     window:_backgroundWindow.get()
644                                                               initialAlpha:currentAlpha
645                                                                 finalAlpha:0]);
646    [_fadeAnimation setAnimationBlockingMode:NSAnimationNonblocking];
647    [_fadeAnimation setCurrentProgress:0];
648    [_fadeAnimation startAnimation];
649
650    [_backgroundWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
651
652    // WKWindowSetClipRect takes window coordinates, so convert from screen coordinates here:
653    NSRect finalBounds = _finalFrame;
654#pragma clang diagnostic push
655#pragma clang diagnostic ignored "-Wdeprecated-declarations"
656    finalBounds.origin = [[self window] convertScreenToBase:finalBounds.origin];
657#pragma clang diagnostic pop
658    WKWindowSetClipRect([self window], finalBounds);
659
660    [_webView _setSuppressVisibilityUpdates:NO];
661    [[self window] setAutodisplay:YES];
662    [[self window] displayIfNeeded];
663    NSEnableScreenUpdates();
664}
665@end
666
667#endif // ENABLE(FULLSCREEN_API) && !PLATFORM(IOS)
668