Exiting full screen video brings the wrong Safari window to the foreground
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / WKFullScreenWindowController.mm
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)
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/WebCoreFullScreenWindow.h>
43 #import <WebCore/WebWindowAnimation.h>
44 #import <WebKit/WebNSWindowExtras.h>
45 #import <WebKitSystemInterface.h>
46 #import <wtf/UnusedParam.h>
47
48 using namespace WebKit;
49 using namespace WebCore;
50
51 static RetainPtr<NSWindow> createBackgroundFullscreenWindow(NSRect frame);
52
53 static const CFTimeInterval defaultAnimationDuration = 0.5;
54 static const NSTimeInterval DefaultWatchdogTimerInterval = 1;
55
56 @interface WKFullScreenWindowController(Private)<NSAnimationDelegate>
57 - (void)_updateMenuAndDockForFullScreen;
58 - (void)_swapView:(NSView*)view with:(NSView*)otherView;
59 - (WebPageProxy*)_page;
60 - (WebFullScreenManagerProxy*)_manager;
61 - (void)_startEnterFullScreenAnimationWithDuration:(NSTimeInterval)duration;
62 - (void)_startExitFullScreenAnimationWithDuration:(NSTimeInterval)duration;
63 @end
64
65 #if defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD)
66 @interface NSWindow(convertRectToScreenForLeopardAndSnowLeopard)
67 - (NSRect)convertRectToScreen:(NSRect)aRect;
68 @end
69
70 @implementation NSWindow(convertRectToScreenForLeopardAndSnowLeopard)
71 - (NSRect)convertRectToScreen:(NSRect)rect
72 {
73     NSRect frame = [self frame];
74     rect.origin.x += frame.origin.x;
75     rect.origin.y += frame.origin.y;
76     return rect;
77 }
78 @end
79 #endif
80
81 @interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard)
82 - (BOOL)isOnActiveSpace;
83 @end
84
85 @implementation WKFullScreenWindowController
86
87 #pragma mark -
88 #pragma mark Initialization
89 - (id)init
90 {
91     NSWindow *window = [[WebCoreFullScreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
92     self = [super initWithWindow:window];
93     [window release];
94     if (!self)
95         return nil;
96     [self windowDidLoad];
97     
98     return self;
99 }
100
101 - (void)dealloc
102 {
103     [self setWebView:nil];
104     
105     [NSObject cancelPreviousPerformRequestsWithTarget:self];
106     
107     [[NSNotificationCenter defaultCenter] removeObserver:self];
108     [super dealloc];
109 }
110
111 - (void)windowDidLoad
112 {
113     [super windowDidLoad];
114
115     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp];
116     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp];
117 }
118
119 #pragma mark -
120 #pragma mark Accessors
121
122 - (WKView*)webView
123 {
124     return _webView;
125 }
126
127 - (void)setWebView:(WKView *)webView
128 {
129     [webView retain];
130     [_webView release];
131     _webView = webView;
132 }
133
134 - (BOOL)isFullScreen
135 {
136     return _isFullScreen;
137 }
138
139 #pragma mark -
140 #pragma mark NSWindowController overrides
141
142 - (void)cancelOperation:(id)sender
143 {
144     [self _manager]->requestExitFullScreen();
145
146     // If the page doesn't respond in DefaultWatchdogTimerInterval seconds, it could be because
147     // the WebProcess has hung, so exit anyway.
148     if (!_watchdogTimer)
149         _watchdogTimer = adoptNS([NSTimer scheduledTimerWithTimeInterval:DefaultWatchdogTimerInterval target:self selector:@selector(exitFullScreen) userInfo:nil repeats:NO]);
150 }
151
152 #pragma mark -
153 #pragma mark Notifications
154
155 - (void)applicationDidResignActive:(NSNotification*)notification
156 {
157     // Check to see if the fullScreenWindow is on the active space; this function is available
158     // on 10.6 and later, so default to YES if the function is not available:
159     NSWindow* fullScreenWindow = [self window];
160     BOOL isOnActiveSpace = ([fullScreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullScreenWindow isOnActiveSpace] : YES);
161     
162     // Replicate the QuickTime Player (X) behavior when losing active application status:
163     // Is the fullScreen screen the main screen? (Note: this covers the case where only a 
164     // single screen is available.)  Is the fullScreen screen on the current space? IFF so, 
165     // then exit fullScreen mode. 
166     if ([fullScreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace)
167         [self cancelOperation:self];
168 }
169
170 - (void)applicationDidChangeScreenParameters:(NSNotification*)notification
171 {
172     // The user may have changed the main screen by moving the menu bar, or they may have changed
173     // the Dock's size or location, or they may have changed the fullScreen screen's dimensions. 
174     // Update our presentation parameters, and ensure that the full screen window occupies the 
175     // entire screen:
176     [self _updateMenuAndDockForFullScreen];
177     NSWindow* window = [self window];
178     NSRect screenFrame = [[window screen] frame];
179     [window setFrame:screenFrame display:YES];
180     [_backgroundWindow.get() setFrame:screenFrame display:YES];
181 }
182
183 #pragma mark -
184 #pragma mark Exposed Interface
185
186 - (void)enterFullScreen:(NSScreen *)screen
187 {
188     if (_isFullScreen)
189         return;
190     _isFullScreen = YES;
191
192     [self _updateMenuAndDockForFullScreen];   
193
194     if (!screen)
195         screen = [NSScreen mainScreen];
196     NSRect screenFrame = [screen frame];
197
198     NSRect webViewFrame = [[_webView window] convertRectToScreen:
199                            [_webView convertRect:[_webView frame] toView:nil]];
200
201     // Flip coordinate system:
202     webViewFrame.origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(webViewFrame);
203
204     CGWindowID windowID = [[_webView window] windowNumber];
205     RetainPtr<CGImageRef> webViewContents(AdoptCF, CGWindowListCreateImage(NSRectToCGRect(webViewFrame), kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageShouldBeOpaque));
206
207     // Screen updates to be re-enabled in beganEnterFullScreenWithInitialFrame:finalFrame:
208     NSDisableScreenUpdates();
209     [[self window] setAutodisplay:NO];
210
211     NSResponder *webWindowFirstResponder = [[_webView window] firstResponder];
212     [[self window] setFrame:screenFrame display:NO];
213
214     // Swap the webView placeholder into place.
215     if (!_webViewPlaceholder) {
216         _webViewPlaceholder.adoptNS([[NSImageView alloc] init]);
217         [_webViewPlaceholder.get() setLayer:[CALayer layer]];
218         [_webViewPlaceholder.get() setWantsLayer:YES];
219     }
220     [[_webViewPlaceholder.get() layer] setContents:(id)webViewContents.get()];
221     [self _swapView:_webView with:_webViewPlaceholder.get()];
222     
223     // Then insert the WebView into the full screen window
224     NSView* contentView = [[self window] contentView];
225     [contentView addSubview:_webView positioned:NSWindowBelow relativeTo:nil];
226     [_webView setFrame:[contentView bounds]];
227
228     [[self window] makeResponder:webWindowFirstResponder firstResponderIfDescendantOfView:_webView];
229
230     [self _manager]->setAnimatingFullScreen(true);
231     [self _manager]->willEnterFullScreen();
232 }
233
234 - (void)beganEnterFullScreenWithInitialFrame:(const WebCore::IntRect&)initialFrame finalFrame:(const WebCore::IntRect&)finalFrame
235 {
236     if (_isEnteringFullScreen)
237         return;
238     _isEnteringFullScreen = YES;
239
240     _initialFrame = initialFrame;
241     _finalFrame = finalFrame;
242
243     [self _updateMenuAndDockForFullScreen];   
244
245     [self _startEnterFullScreenAnimationWithDuration:defaultAnimationDuration];
246 }
247
248 - (void)finishedEnterFullScreenAnimation:(bool)completed
249 {
250     if (!_isEnteringFullScreen)
251         return;
252     _isEnteringFullScreen = NO;
253
254     if (completed) {
255         // Screen updates to be re-enabled ta the end of the current block.
256         NSDisableScreenUpdates();
257         [self _manager]->didEnterFullScreen();
258         [self _manager]->setAnimatingFullScreen(false);
259
260         NSRect windowBounds = [[self window] frame];
261         windowBounds.origin = NSZeroPoint;
262         WKWindowSetClipRect([self window], windowBounds);
263
264         NSWindow *webWindow = [_webViewPlaceholder.get() window];
265 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
266         // In Lion, NSWindow will animate into and out of orderOut operations. Suppress that
267         // behavior here, making sure to reset the animation behavior afterward.
268         NSWindowAnimationBehavior animationBehavior = [webWindow animationBehavior];
269         [webWindow setAnimationBehavior:NSWindowAnimationBehaviorNone];
270 #endif
271         [webWindow orderOut:self];
272 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
273         [webWindow setAnimationBehavior:animationBehavior];
274 #endif
275
276         [_fadeAnimation.get() stopAnimation];
277         [_fadeAnimation.get() setWindow:nil];
278         _fadeAnimation = nullptr;
279         
280         [_backgroundWindow.get() orderOut:self];
281         [_backgroundWindow.get() setFrame:NSZeroRect display:YES];
282         NSEnableScreenUpdates();
283     } else
284         [_scaleAnimation.get() stopAnimation];
285 }
286
287 - (void)exitFullScreen
288 {
289     if (_watchdogTimer) {
290         [_watchdogTimer.get() invalidate];
291         _watchdogTimer.clear();
292     }
293
294     if (!_isFullScreen)
295         return;
296     _isFullScreen = NO;
297
298     // Screen updates to be re-enabled in beganExitFullScreenWithInitialFrame:finalFrame:
299     NSDisableScreenUpdates();
300     [[self window] setAutodisplay:NO];
301
302     [self _manager]->setAnimatingFullScreen(true);
303     [self _manager]->willExitFullScreen();
304 }
305
306 - (void)beganExitFullScreenWithInitialFrame:(const WebCore::IntRect&)initialFrame finalFrame:(const WebCore::IntRect&)finalFrame
307 {
308     if (_isExitingFullScreen)
309         return;
310     _isExitingFullScreen = YES;
311
312     if (_isEnteringFullScreen)
313         [self finishedEnterFullScreenAnimation:NO];
314
315     [self _updateMenuAndDockForFullScreen];
316     
317     NSWindow* webWindow = [_webViewPlaceholder.get() window];
318 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
319     // In Lion, NSWindow will animate into and out of orderOut operations. Suppress that
320     // behavior here, making sure to reset the animation behavior afterward.
321     NSWindowAnimationBehavior animationBehavior = [webWindow animationBehavior];
322     [webWindow setAnimationBehavior:NSWindowAnimationBehaviorNone];
323 #endif
324     // If the user has moved the fullScreen window into a new space, temporarily change
325     // the collectionBehavior of the webView's window so that it is pulled into the active space:
326     if (![webWindow isOnActiveSpace]) {
327         NSWindowCollectionBehavior behavior = [webWindow collectionBehavior];
328         [webWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
329         [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
330         [webWindow setCollectionBehavior:behavior];
331     } else
332         [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
333     
334 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
335     [webWindow setAnimationBehavior:animationBehavior];
336 #endif
337
338     [self _startExitFullScreenAnimationWithDuration:defaultAnimationDuration];
339 }
340
341 - (void)finishedExitFullScreenAnimation:(bool)completed
342 {
343     if (!_isExitingFullScreen)
344         return;
345     _isExitingFullScreen = NO;
346
347     [self _updateMenuAndDockForFullScreen];
348
349     // Screen updates to be re-enabled ta the end of the current function.
350     NSDisableScreenUpdates();
351
352     [self _manager]->didExitFullScreen();
353     [self _manager]->setAnimatingFullScreen(false);
354
355     NSResponder *firstResponder = [[self window] firstResponder];
356     [self _swapView:_webViewPlaceholder.get() with:_webView];
357     [[_webView window] makeResponder:firstResponder firstResponderIfDescendantOfView:_webView];
358
359     NSRect windowBounds = [[self window] frame];
360     windowBounds.origin = NSZeroPoint;
361     WKWindowSetClipRect([self window], windowBounds);
362
363     [[self window] orderOut:self];
364     [[self window] setFrame:NSZeroRect display:YES];
365
366     [_fadeAnimation.get() stopAnimation];
367     [_fadeAnimation.get() setWindow:nil];
368     _fadeAnimation = nullptr;
369
370     [_backgroundWindow.get() orderOut:self];
371     [_backgroundWindow.get() setFrame:NSZeroRect display:YES];
372
373     [[_webView window] makeKeyAndOrderFront:self];
374
375     NSEnableScreenUpdates();
376 }
377
378 - (void)close
379 {
380     // We are being asked to close rapidly, most likely because the page 
381     // has closed or the web process has crashed.  Just walk through our
382     // normal exit full screen sequence, but don't wait to be called back
383     // in response.
384     if (_isFullScreen)
385         [self exitFullScreen];
386     
387     if (_isExitingFullScreen)
388         [self finishedExitFullScreenAnimation:YES];
389
390     [super close];
391 }
392
393 #pragma mark -
394 #pragma mark NSAnimation delegate
395
396 - (void)animationDidEnd:(NSAnimation*)animation
397 {
398     if (_isFullScreen)
399         [self finishedEnterFullScreenAnimation:YES];
400     else
401         [self finishedExitFullScreenAnimation:YES];
402 }
403
404 #pragma mark -
405 #pragma mark Internal Interface
406
407 - (void)_updateMenuAndDockForFullScreen
408 {
409     // NSApplicationPresentationOptions is available on > 10.6 only:
410 #ifndef BUILDING_ON_LEOPARD
411     NSApplicationPresentationOptions options = NSApplicationPresentationDefault;
412     NSScreen* fullScreenScreen = [[self window] screen];
413     
414     if (_isFullScreen) {
415         // Auto-hide the menu bar if the fullScreenScreen contains the menu bar:
416         // NOTE: if the fullScreenScreen contains the menu bar but not the dock, we must still 
417         // auto-hide the dock, or an exception will be thrown.
418         if ([[NSScreen screens] objectAtIndex:0] == fullScreenScreen)
419             options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock);
420         // Check if the current screen contains the dock by comparing the screen's frame to its
421         // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen
422         // contains the dock, hide it.
423         else if (!NSEqualRects([fullScreenScreen frame], [fullScreenScreen visibleFrame]))
424             options |= NSApplicationPresentationAutoHideDock;
425     }
426     
427     if ([NSApp respondsToSelector:@selector(setPresentationOptions:)])
428         [NSApp setPresentationOptions:options];
429     else
430 #endif
431         SetSystemUIMode(_isFullScreen ? kUIModeNormal : kUIModeAllHidden, 0);
432 }
433
434 - (WebPageProxy*)_page
435 {
436     return toImpl([_webView pageRef]);
437 }
438
439 - (WebFullScreenManagerProxy*)_manager
440 {
441     WebPageProxy* webPage = [self _page];
442     if (!webPage)
443         return 0;
444     return webPage->fullScreenManager();
445 }
446
447 - (void)_swapView:(NSView*)view with:(NSView*)otherView
448 {
449     [CATransaction begin];
450     [CATransaction setDisableActions:YES];
451     [otherView setFrame:[view frame]];        
452     [otherView setAutoresizingMask:[view autoresizingMask]];
453     [otherView removeFromSuperview];
454     [[view superview] replaceSubview:view with:otherView];
455     [CATransaction commit];
456 }
457
458 static RetainPtr<NSWindow> createBackgroundFullscreenWindow(NSRect frame)
459 {
460     NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
461     [window setOpaque:YES];
462     [window setBackgroundColor:[NSColor blackColor]];
463     [window setReleasedWhenClosed:NO];
464     return adoptNS(window);
465 }
466
467 static NSRect windowFrameFromApparentFrames(NSRect screenFrame, NSRect initialFrame, NSRect finalFrame)
468 {
469     NSRect initialWindowFrame;
470     CGFloat xScale = NSWidth(screenFrame) / NSWidth(finalFrame);
471     CGFloat yScale = NSHeight(screenFrame) / NSHeight(finalFrame);
472     CGFloat xTrans = NSMinX(screenFrame) - NSMinX(finalFrame);
473     CGFloat yTrans = NSMinY(screenFrame) - NSMinY(finalFrame);
474     initialWindowFrame.size = NSMakeSize(NSWidth(initialFrame) * xScale, NSHeight(initialFrame) * yScale);
475     initialWindowFrame.origin = NSMakePoint
476         ( NSMinX(initialFrame) + xTrans / (NSWidth(finalFrame) / NSWidth(initialFrame))
477         , NSMinY(initialFrame) + yTrans / (NSHeight(finalFrame) / NSHeight(initialFrame)));
478     return initialWindowFrame;
479 }
480
481 - (void)_startEnterFullScreenAnimationWithDuration:(NSTimeInterval)duration
482 {
483     NSRect screenFrame = [[[self window] screen] frame];
484     NSRect initialWindowFrame = windowFrameFromApparentFrames(screenFrame, _initialFrame, _finalFrame);
485     
486     _scaleAnimation.adoptNS([[WebWindowScaleAnimation alloc] initWithHintedDuration:duration window:[self window] initalFrame:initialWindowFrame finalFrame:screenFrame]);
487     
488     [_scaleAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
489     [_scaleAnimation.get() setDelegate:self];
490     [_scaleAnimation.get() setCurrentProgress:0];
491     [_scaleAnimation.get() startAnimation];
492
493     // WKWindowSetClipRect takes window coordinates, so convert from screen coordinates here:
494     NSRect finalBounds = _finalFrame;
495     finalBounds.origin = [[self window] convertScreenToBase:finalBounds.origin];
496     WKWindowSetClipRect([self window], finalBounds);
497
498     [[self window] makeKeyAndOrderFront:self];
499
500     if (!_backgroundWindow)
501         _backgroundWindow = createBackgroundFullscreenWindow(screenFrame);
502     else
503         [_backgroundWindow.get() setFrame:screenFrame display:NO];
504
505     CGFloat currentAlpha = 0;
506     if (_fadeAnimation) {
507         currentAlpha = [_fadeAnimation.get() currentAlpha];
508         [_fadeAnimation.get() stopAnimation];
509         [_fadeAnimation.get() setWindow:nil];
510     }
511
512     _fadeAnimation.adoptNS([[WebWindowFadeAnimation alloc] initWithDuration:duration 
513                                                                      window:_backgroundWindow.get() 
514                                                                initialAlpha:currentAlpha 
515                                                                  finalAlpha:1]);
516     [_fadeAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
517     [_fadeAnimation.get() setCurrentProgress:0];
518     [_fadeAnimation.get() startAnimation];
519
520     [_backgroundWindow.get() orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
521
522     [[self window] setAutodisplay:YES];
523     [[self window] displayIfNeeded];
524     NSEnableScreenUpdates();
525 }
526
527 - (void)_startExitFullScreenAnimationWithDuration:(NSTimeInterval)duration
528 {
529     NSRect screenFrame = [[[self window] screen] frame];
530     NSRect initialWindowFrame = windowFrameFromApparentFrames(screenFrame, _initialFrame, _finalFrame);
531
532     NSRect currentFrame = _scaleAnimation ? [_scaleAnimation.get() currentFrame] : [[self window] frame];
533     _scaleAnimation.adoptNS([[WebWindowScaleAnimation alloc] initWithHintedDuration:duration window:[self window] initalFrame:currentFrame finalFrame:initialWindowFrame]);
534
535     [_scaleAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
536     [_scaleAnimation.get() setDelegate:self];
537     [_scaleAnimation.get() setCurrentProgress:0];
538     [_scaleAnimation.get() startAnimation];
539
540     if (!_backgroundWindow)
541         _backgroundWindow = createBackgroundFullscreenWindow(screenFrame);
542     else
543         [_backgroundWindow.get() setFrame:screenFrame display:NO];
544
545     CGFloat currentAlpha = 1;
546     if (_fadeAnimation) {
547         currentAlpha = [_fadeAnimation.get() currentAlpha];
548         [_fadeAnimation.get() stopAnimation];
549         [_fadeAnimation.get() setWindow:nil];
550     }
551     _fadeAnimation.adoptNS([[WebWindowFadeAnimation alloc] initWithDuration:duration 
552                                                                      window:_backgroundWindow.get() 
553                                                                initialAlpha:currentAlpha 
554                                                                  finalAlpha:0]);
555     [_fadeAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
556     [_fadeAnimation.get() setCurrentProgress:0];
557     [_fadeAnimation.get() startAnimation];
558
559     [_backgroundWindow.get() orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
560
561     // WKWindowSetClipRect takes window coordinates, so convert from screen coordinates here:
562     NSRect finalBounds = _finalFrame;
563     finalBounds.origin = [[self window] convertScreenToBase:finalBounds.origin];
564     WKWindowSetClipRect([self window], finalBounds);
565
566     [[self window] setAutodisplay:YES];
567     [[self window] displayIfNeeded];
568     NSEnableScreenUpdates();
569 }
570 @end
571
572 #endif