Modified: trunk/Source/WebKit2/ChangeLog (181769 => 181770)
--- trunk/Source/WebKit2/ChangeLog 2015-03-20 00:18:15 UTC (rev 181769)
+++ trunk/Source/WebKit2/ChangeLog 2015-03-20 00:20:52 UTC (rev 181770)
@@ -1,3 +1,58 @@
+2015-03-19 Jer Noble <[email protected]>
+
+ [WK2][Mac] Fullscreen animations with mismatched aspect ratios are "squished".
+ https://bugs.webkit.org/show_bug.cgi?id=142132
+
+ Reviewed by Tim Horton.
+
+ Use CALayer animations for the transition into and out of fullscreen.
+
+ The fullscreen transition consists of three separate animations:
+ 1. An opacity animation for the black backdrop behind the fullscreen content
+ 2. A scale/translation animation from fullscreen element's initial screen to its final one.
+ 3. A clip animation from the fullscreen element's initial aspect ratio to its final one.
+
+ The opacity animation will apply to the fullscreen window's content view's layer's
+ background color. To separately animate the transform and mask of the web view's content, a
+ layer-backed subview is added to the content view, and the scale/translation & clip
+ animations are applied to its transform property and mask layer respectively.
+
+ Utility methods to create each animation have been added, and each includes a parameter for
+ the direction of the animation, so that the same methods can be used for entering and
+ exiting fullscreen transitions.
+
+ The user-visible changes to this new transition are when the aspect ratios of the initial
+ and final screen locations are different: previously the transition would use a scale
+ transform to "squish" the fullscreen content into the initial aspect ratio. The new
+ transition instead "clips" the fullscreen content to the initial aspect ratio. For common
+ operations such a <video> element with a different aspect ratio than the screen, this makes
+ the black letterbox "grow" during the transition, and makes the transition overall much
+ smoother.
+
+ * UIProcess/mac/WKFullScreenWindowController.h:
+ * UIProcess/mac/WKFullScreenWindowController.mm:
+ (-[WKFullScreenWindowController initWithWindow:webView:]): Create and initialze the clipping view.
+ (-[WKFullScreenWindowController applicationDidChangeScreenParameters:]): _backgroundWindow was removed.
+ (-[WKFullScreenWindowController enterFullScreen:]): Add the webView to the _clipView, not the contentView.
+ (-[WKFullScreenWindowController beganEnterFullScreenWithInitialFrame:finalFrame:]): _backgroundWindow,
+ _fadeAnimation, and _scaleAnimation are all removed.
+ (-[WKFullScreenWindowController finishedEnterFullScreenAnimation:]): Ditto.
+ (-[WKFullScreenWindowController finishedExitFullScreenAnimation:]): Ditto. Hide the contentView's
+ layer. Pause visibility updates.
+ (-[WKFullScreenWindowController completeFinishExitFullScreenAnimationAfterRepaint]): Resume visibility updates.
+ (-[WKFullScreenWindowController close]): _fadeAnimation and _scaleAnimation are removed.
+ (-[WKFullScreenWindowController customWindowsToEnterFullScreenForWindow:]): Return only the fullscreen
+ window.
+ (-[WKFullScreenWindowController customWindowsToExitFullScreenForWindow:]): Ditto.
+ (zoomAnimation): Added.
+ (maskAnimation): Added.
+ (fadeAnimation): Added.
+ (-[WKFullScreenWindowController _startEnterFullScreenAnimationWithDuration:]): Use the utility
+ methods above to set up the animation.
+ (-[WKFullScreenWindowController _startExitFullScreenAnimationWithDuration:]): Ditto.
+ (createBackgroundFullscreenWindow): Deleted.
+ (windowFrameFromApparentFrames): Deleted.
+
2015-03-19 Enrica Casucci <[email protected]>
<attachment> should put URLs on the pasteboard so that Finder can accept drops.
Modified: trunk/Source/WebKit2/UIProcess/mac/WKFullScreenWindowController.mm (181769 => 181770)
--- trunk/Source/WebKit2/UIProcess/mac/WKFullScreenWindowController.mm 2015-03-20 00:18:15 UTC (rev 181769)
+++ trunk/Source/WebKit2/UIProcess/mac/WKFullScreenWindowController.mm 2015-03-20 00:20:52 UTC (rev 181770)
@@ -38,18 +38,15 @@
#import <QuartzCore/QuartzCore.h>
#import <WebCore/DisplaySleepDisabler.h>
#import <WebCore/FloatRect.h>
+#import <WebCore/GeometryUtilities.h>
#import <WebCore/IntRect.h>
#import <WebCore/LocalizedStrings.h>
#import <WebCore/WebCoreFullScreenPlaceholderView.h>
#import <WebCore/WebCoreFullScreenWindow.h>
-#import <WebCore/WebWindowAnimation.h>
-#import <WebKitSystemInterface.h>
using namespace WebKit;
using namespace WebCore;
-static RetainPtr<NSWindow> createBackgroundFullscreenWindow(NSRect frame);
-
static const NSTimeInterval DefaultWatchdogTimerInterval = 1;
enum FullScreenState : NSInteger {
@@ -96,6 +93,22 @@
return nil;
[window setDelegate:self];
[window setCollectionBehavior:([window collectionBehavior] | NSWindowCollectionBehaviorFullScreenPrimary)];
+
+ NSView *contentView = [window contentView];
+ contentView.wantsLayer = YES;
+ contentView.layer.hidden = YES;
+ contentView.autoresizesSubviews = YES;
+
+ _clipView = adoptNS([[NSView alloc] initWithFrame:contentView.bounds]);
+ [_clipView setWantsLayer:YES];
+ [_clipView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+ CALayer *maskLayer = [CALayer layer];
+ maskLayer.anchorPoint = CGPointZero;
+ maskLayer.frame = contentView.bounds;
+ maskLayer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
+ [_clipView layer].mask = maskLayer;
+ [contentView addSubview:_clipView.get()];
+
[self windowDidLoad];
_webView = webView;
@@ -171,7 +184,6 @@
NSWindow* window = [self window];
NSRect screenFrame = [[window screen] frame];
[window setFrame:screenFrame display:YES];
- [_backgroundWindow setFrame:screenFrame display:YES];
}
#pragma mark -
@@ -247,7 +259,7 @@
// Then insert the WebView into the full screen window
NSView* contentView = [[self window] contentView];
- [contentView addSubview:_webView positioned:NSWindowBelow relativeTo:nil];
+ [_clipView addSubview:_webView positioned:NSWindowBelow relativeTo:nil];
[_webView setFrame:[contentView bounds]];
makeResponderFirstResponderIfDescendantOfView(self.window, webWindowFirstResponder, _webView);
@@ -267,15 +279,6 @@
_initialFrame = initialFrame;
_finalFrame = finalFrame;
- if (!_backgroundWindow)
- _backgroundWindow = createBackgroundFullscreenWindow(NSZeroRect);
-
- // The -orderBack: call below can cause the full screen window's contents to draw on top of
- // all other visible windows on the screen, despite NSDisableScreenUpdates having been set, and
- // despite being explicitly ordered behind all other windows. Set the initial scaled frame here
- // before ordering the window on-screen to avoid this flash. <rdar://problem/18325063>
- WKWindowSetScaledFrame(self.window, initialFrame, finalFrame);
-
[self.window orderBack: self]; // Make sure the full screen window is part of the correct Space.
[[self window] enterFullScreenMode:self];
}
@@ -293,28 +296,12 @@
[self _manager]->didEnterFullScreen();
[self _manager]->setAnimatingFullScreen(false);
- NSRect windowBounds = [[self window] frame];
- windowBounds.origin = NSZeroPoint;
- WKWindowSetClipRect([self window], windowBounds);
-
- [_fadeAnimation stopAnimation];
- [_fadeAnimation setWindow:nil];
- _fadeAnimation = nullptr;
-
- [_backgroundWindow orderOut:self];
- [_backgroundWindow setFrame:NSZeroRect display:YES];
-
[_webViewPlaceholder setExitWarningVisible:YES];
[_webViewPlaceholder setTarget:self];
} else {
// Transition to fullscreen failed. Clean up.
_fullScreenState = NotInFullScreen;
- [_scaleAnimation stopAnimation];
-
- [_backgroundWindow orderOut:self];
- [_backgroundWindow setFrame:NSZeroRect display:YES];
-
[[self window] setAutodisplay:YES];
[_webView _setSuppressVisibilityUpdates:NO];
@@ -386,30 +373,16 @@
// Screen updates to be re-enabled in completeFinishExitFullScreenAnimationAfterRepaint.
NSDisableScreenUpdates();
+ [_webView _setSuppressVisibilityUpdates:YES];
+ [[self window] orderOut:self];
+ NSView *contentView = [[self window] contentView];
+ contentView.layer.hidden = YES;
[[_webViewPlaceholder window] setAutodisplay:NO];
NSResponder *firstResponder = [[self window] firstResponder];
[self _replaceView:_webViewPlaceholder.get() with:_webView];
makeResponderFirstResponderIfDescendantOfView(_webView.window, firstResponder, _webView);
- [[self window] orderOut:self];
-
- NSRect windowBounds = [[self window] frame];
- windowBounds.origin = NSZeroPoint;
- WKWindowSetClipRect([self window], windowBounds);
- [[self window] setFrame:NSZeroRect display:YES];
-
- [_scaleAnimation stopAnimation];
- [_scaleAnimation setWindow:nil];
- _scaleAnimation = nullptr;
-
- [_fadeAnimation stopAnimation];
- [_fadeAnimation setWindow:nil];
- _fadeAnimation = nullptr;
-
- [_backgroundWindow orderOut:self];
- [_backgroundWindow setFrame:NSZeroRect display:YES];
-
[[_webView window] makeKeyAndOrderFront:self];
// These messages must be sent after the swap or flashing will occur during forceRepaint:
@@ -435,6 +408,7 @@
_repaintCallback = nullptr;
[[_webView window] setAutodisplay:YES];
[[_webView window] displayIfNeeded];
+ [_webView _setSuppressVisibilityUpdates:NO];
NSEnableScreenUpdates();
}
@@ -456,11 +430,6 @@
if (_fullScreenState == ExitingFullScreen)
[self finishedExitFullScreenAnimation:YES];
- [_scaleAnimation stopAnimation];
- [_scaleAnimation setWindow:nil];
- [_fadeAnimation stopAnimation];
- [_fadeAnimation setWindow:nil];
-
_webView = nil;
[super close];
@@ -471,12 +440,12 @@
- (NSArray *)customWindowsToEnterFullScreenForWindow:(NSWindow *)window
{
- return [NSArray arrayWithObjects:[self window], _backgroundWindow.get(), nil];
+ return @[self.window];
}
- (NSArray *)customWindowsToExitFullScreenForWindow:(NSWindow *)window
{
- return [NSArray arrayWithObjects:[self window], _backgroundWindow.get(), nil];
+ return @[self.window];
}
- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration
@@ -537,50 +506,77 @@
[CATransaction commit];
}
-static RetainPtr<NSWindow> createBackgroundFullscreenWindow(NSRect frame)
+enum AnimationDirection { AnimateIn, AnimateOut };
+static CAAnimation *zoomAnimation(const FloatRect& initialFrame, const FloatRect& finalFrame, CFTimeInterval duration, AnimationDirection direction)
{
- NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
- [window setOpaque:YES];
- [window setBackgroundColor:[NSColor blackColor]];
- [window setReleasedWhenClosed:NO];
- return adoptNS(window);
+ CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
+ FloatRect scaleRect = smallestRectWithAspectRatioAroundRect(finalFrame.size().aspectRatio(), initialFrame);
+ CGAffineTransform resetOriginTransform = CGAffineTransformMakeTranslation(-finalFrame.x(), -finalFrame.y());
+ CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scaleRect.width() / finalFrame.width(), scaleRect.height() / finalFrame.height());
+ CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(scaleRect.x(), scaleRect.y());
+
+ CGAffineTransform finalTransform = CGAffineTransformConcat(CGAffineTransformConcat(resetOriginTransform, scaleTransform), translateTransform);
+ NSValue *scaleValue = [NSValue valueWithCATransform3D:CATransform3DMakeAffineTransform(finalTransform)];
+ if (direction == AnimateIn)
+ scaleAnimation.fromValue = scaleValue;
+ else
+ scaleAnimation.toValue = scaleValue;
+
+ scaleAnimation.duration = duration;
+ scaleAnimation.removedOnCompletion = NO;
+ scaleAnimation.fillMode = kCAFillModeBoth;
+ scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ return scaleAnimation;
}
-static NSRect windowFrameFromApparentFrames(NSRect screenFrame, NSRect initialFrame, NSRect finalFrame)
+static CAAnimation *maskAnimation(const FloatRect& initialFrame, const FloatRect& finalFrame, CFTimeInterval duration, AnimationDirection direction)
{
- NSRect initialWindowFrame;
- if (!NSWidth(initialFrame) || !NSWidth(finalFrame) || !NSHeight(initialFrame) || !NSHeight(finalFrame))
- return screenFrame;
+ CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
+ FloatRect boundsRect = largestRectWithAspectRatioInsideRect(initialFrame.size().aspectRatio(), finalFrame);
+ NSValue *boundsValue = [NSValue valueWithRect:FloatRect(FloatPoint(), boundsRect.size())];
+ if (direction == AnimateIn)
+ boundsAnimation.fromValue = boundsValue;
+ else
+ boundsAnimation.toValue = boundsValue;
- CGFloat xScale = NSWidth(screenFrame) / NSWidth(finalFrame);
- CGFloat yScale = NSHeight(screenFrame) / NSHeight(finalFrame);
- CGFloat xTrans = NSMinX(screenFrame) - NSMinX(finalFrame);
- CGFloat yTrans = NSMinY(screenFrame) - NSMinY(finalFrame);
- initialWindowFrame.size = NSMakeSize(NSWidth(initialFrame) * xScale, NSHeight(initialFrame) * yScale);
- initialWindowFrame.origin = NSMakePoint
- ( NSMinX(initialFrame) + xTrans / (NSWidth(finalFrame) / NSWidth(initialFrame))
- , NSMinY(initialFrame) + yTrans / (NSHeight(finalFrame) / NSHeight(initialFrame)));
- return initialWindowFrame;
+ CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
+ NSValue *positionValue = [NSValue valueWithPoint:boundsRect.location()];
+ if (direction == AnimateIn)
+ positionAnimation.fromValue = positionValue;
+ else
+ positionAnimation.toValue = positionValue;
+
+ CAAnimationGroup *animation = [CAAnimationGroup animation];
+ animation.animations = @[boundsAnimation, positionAnimation];
+ animation.duration = duration;
+ animation.removedOnCompletion = NO;
+ animation.fillMode = kCAFillModeBoth;
+ animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ return animation;
}
+static CAAnimation *fadeAnimation(CFTimeInterval duration, AnimationDirection direction)
+{
+ CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
+ if (direction == AnimateIn)
+ fadeAnimation.toValue = (id)CGColorGetConstantColor(kCGColorBlack);
+ else
+ fadeAnimation.fromValue = (id)CGColorGetConstantColor(kCGColorBlack);
+ fadeAnimation.duration = duration;
+ fadeAnimation.removedOnCompletion = NO;
+ fadeAnimation.fillMode = kCAFillModeBoth;
+ fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ return fadeAnimation;
+}
+
- (void)_startEnterFullScreenAnimationWithDuration:(NSTimeInterval)duration
{
- NSRect screenFrame = [[[self window] screen] frame];
- NSRect initialWindowFrame = windowFrameFromApparentFrames(screenFrame, _initialFrame, _finalFrame);
-
- _scaleAnimation = adoptNS([[WebWindowScaleAnimation alloc] initWithHintedDuration:duration window:[self window] initalFrame:initialWindowFrame finalFrame:screenFrame]);
-
- [_scaleAnimation setAnimationBlockingMode:NSAnimationNonblocking];
- [_scaleAnimation setCurrentProgress:0];
- [_scaleAnimation startAnimation];
+ [[_clipView layer] addAnimation:zoomAnimation(_initialFrame, _finalFrame, duration, AnimateIn) forKey:@"fullscreen"];
+ [[_clipView layer].mask addAnimation:maskAnimation(_initialFrame, _finalFrame, duration, AnimateIn) forKey:@"fullscreen"];
- // WKWindowSetClipRect takes window coordinates, so convert from screen coordinates here:
- NSRect finalBounds = _finalFrame;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- finalBounds.origin = [[self window] convertScreenToBase:finalBounds.origin];
-#pragma clang diagnostic pop
- WKWindowSetClipRect([self window], finalBounds);
+ NSView* contentView = [[self window] contentView];
+ contentView.layer.hidden = NO;
+ [contentView.layer addAnimation:fadeAnimation(duration, AnimateIn) forKey:@"fullscreen"];
NSWindow* window = [self window];
NSWindowCollectionBehavior behavior = [window collectionBehavior];
@@ -588,29 +584,6 @@
[window makeKeyAndOrderFront:self];
[window setCollectionBehavior:behavior];
-
- if (!_backgroundWindow)
- _backgroundWindow = createBackgroundFullscreenWindow(screenFrame);
- else
- [_backgroundWindow setFrame:screenFrame display:NO];
-
- CGFloat currentAlpha = 0;
- if (_fadeAnimation) {
- currentAlpha = [_fadeAnimation currentAlpha];
- [_fadeAnimation stopAnimation];
- [_fadeAnimation setWindow:nil];
- }
-
- _fadeAnimation = adoptNS([[WebWindowFadeAnimation alloc] initWithDuration:duration
- window:_backgroundWindow.get()
- initialAlpha:currentAlpha
- finalAlpha:1]);
- [_fadeAnimation setAnimationBlockingMode:NSAnimationNonblocking];
- [_fadeAnimation setCurrentProgress:0];
- [_fadeAnimation startAnimation];
-
- [_backgroundWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
-
[_webView _setSuppressVisibilityUpdates:NO];
[[self window] setAutodisplay:YES];
[[self window] displayIfNeeded];
@@ -627,45 +600,13 @@
_fullScreenState = ExitingFullScreen;
}
- NSRect screenFrame = [[[self window] screen] frame];
- NSRect initialWindowFrame = windowFrameFromApparentFrames(screenFrame, _initialFrame, _finalFrame);
+ [[_clipView layer] addAnimation:zoomAnimation(_initialFrame, _finalFrame, duration, AnimateOut) forKey:@"fullscreen"];
+ [[_clipView layer].mask addAnimation:maskAnimation(_initialFrame, _finalFrame, duration, AnimateOut) forKey:@"fullscreen"];
- NSRect currentFrame = _scaleAnimation ? [_scaleAnimation currentFrame] : [[self window] frame];
- _scaleAnimation = adoptNS([[WebWindowScaleAnimation alloc] initWithHintedDuration:duration window:[self window] initalFrame:currentFrame finalFrame:initialWindowFrame]);
+ NSView* contentView = [[self window] contentView];
+ contentView.layer.hidden = NO;
+ [contentView.layer addAnimation:fadeAnimation(duration, AnimateOut) forKey:@"fullscreen"];
- [_scaleAnimation setAnimationBlockingMode:NSAnimationNonblocking];
- [_scaleAnimation setCurrentProgress:0];
- [_scaleAnimation startAnimation];
-
- if (!_backgroundWindow)
- _backgroundWindow = createBackgroundFullscreenWindow(screenFrame);
- else
- [_backgroundWindow setFrame:screenFrame display:NO];
-
- CGFloat currentAlpha = 1;
- if (_fadeAnimation) {
- currentAlpha = [_fadeAnimation currentAlpha];
- [_fadeAnimation stopAnimation];
- [_fadeAnimation setWindow:nil];
- }
- _fadeAnimation = adoptNS([[WebWindowFadeAnimation alloc] initWithDuration:duration
- window:_backgroundWindow.get()
- initialAlpha:currentAlpha
- finalAlpha:0]);
- [_fadeAnimation setAnimationBlockingMode:NSAnimationNonblocking];
- [_fadeAnimation setCurrentProgress:0];
- [_fadeAnimation startAnimation];
-
- [_backgroundWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]];
-
- // WKWindowSetClipRect takes window coordinates, so convert from screen coordinates here:
- NSRect finalBounds = _finalFrame;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- finalBounds.origin = [[self window] convertScreenToBase:finalBounds.origin];
-#pragma clang diagnostic pop
- WKWindowSetClipRect([self window], finalBounds);
-
[_webView _setSuppressVisibilityUpdates:NO];
[[self window] setAutodisplay:YES];
[[self window] displayIfNeeded];