summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/mac/sdk_forward_declarations.h1
-rw-r--r--chrome/browser/ui/cocoa/browser_window_controller.h3
-rw-r--r--chrome/browser/ui/cocoa/browser_window_controller_private.h9
-rw-r--r--chrome/browser/ui/cocoa/browser_window_controller_private.mm60
-rw-r--r--chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h110
-rw-r--r--chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm302
-rw-r--r--chrome/chrome_browser_ui.gypi2
7 files changed, 481 insertions, 6 deletions
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index f606dab..faa36ea 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -105,6 +105,7 @@ typedef NSUInteger NSWindowButton;
- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
- (void)toggleFullScreen:(id)sender;
- (void)setRestorable:(BOOL)flag;
+- (NSRect)convertRectFromScreen:(NSRect)aRect;
@end
@interface NSCursor (LionSDKDeclarations)
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.h b/chrome/browser/ui/cocoa/browser_window_controller.h
index 83d2078..16ba21ee 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller.h
@@ -32,6 +32,7 @@
class Browser;
class BrowserWindow;
class BrowserWindowCocoa;
+@class BrowserWindowEnterFullscreenTransition;
@class DevToolsController;
@class DownloadShelfController;
class ExtensionKeybindingRegistryCocoa;
@@ -83,6 +84,8 @@ class Command;
base::scoped_nsobject<PresentationModeController> presentationModeController_;
base::scoped_nsobject<ExclusiveAccessBubbleWindowController>
exclusiveAccessBubbleWindowController_;
+ base::scoped_nsobject<BrowserWindowEnterFullscreenTransition>
+ enterFullscreenTransition_;
// Strong. StatusBubble is a special case of a strong reference that
// we don't wrap in a scoped_ptr because it is acting the same
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.h b/chrome/browser/ui/cocoa/browser_window_controller_private.h
index ddb2138..341c428 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.h
@@ -148,12 +148,17 @@
// There is a bug in Mavericks for applications linked against OSX 10.8 and
// earlier. The bug requires Screens Have Separate Spaces to be enabled, and
// for the window to be on a secondary screen. When AppKit Fullscreen is
-// invoked on the window, its final frame is 22pt too short. This method
-// detects when the relevant conditions have been met so that a hack can be
+// invoked on the window, its final frame is 22pt too short. These methods
+// detect when the relevant conditions have been met so that a hack can be
// applied to fix the size of the window.
// http://crbug.com/396980
++ (BOOL)systemSettingsRequireMavericksAppKitFullscreenHack;
- (BOOL)shouldUseMavericksAppKitFullscreenHack;
+// Whether the instance should use a custom transition when animating into and
+// out of AppKit Fullscreen.
+- (BOOL)shouldUseCustomAppKitFullscreenTransition;
+
@end // @interface BrowserWindowController(Private)
#endif // CHROME_BROWSER_UI_COCOA_BROWSER_WINDOW_CONTROLLER_PRIVATE_H_
diff --git a/chrome/browser/ui/cocoa/browser_window_controller_private.mm b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
index 36a9ccd..f2c086b 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller_private.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller_private.mm
@@ -22,6 +22,7 @@
#include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window_state.h"
+#import "chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h"
#import "chrome/browser/ui/cocoa/browser_window_layout.h"
#import "chrome/browser/ui/cocoa/dev_tools_controller.h"
#import "chrome/browser/ui/cocoa/fast_resize_view.h"
@@ -682,6 +683,8 @@ willPositionSheet:(NSWindow*)sheet
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
+ enterFullscreenTransition_.reset();
+
// In Yosemite, some combination of the titlebar and toolbar always show in
// full-screen mode. We do not want either to show. Search for the window that
// contains the views, and hide it. There is no need to ever unhide the view.
@@ -1002,13 +1005,16 @@ willPositionSheet:(NSWindow*)sheet
}
}
-- (BOOL)shouldUseMavericksAppKitFullscreenHack {
++ (BOOL)systemSettingsRequireMavericksAppKitFullscreenHack {
if (!base::mac::IsOSMavericks())
return NO;
- if (![NSScreen respondsToSelector:@selector(screensHaveSeparateSpaces)] ||
- ![NSScreen screensHaveSeparateSpaces]) {
+ return [NSScreen respondsToSelector:@selector(screensHaveSeparateSpaces)] &&
+ [NSScreen screensHaveSeparateSpaces];
+}
+
+- (BOOL)shouldUseMavericksAppKitFullscreenHack {
+ if (![[self class] systemSettingsRequireMavericksAppKitFullscreenHack])
return NO;
- }
if (!enteringAppKitFullscreen_)
return NO;
if (enteringAppKitFullscreenOnPrimaryScreen_)
@@ -1017,4 +1023,50 @@ willPositionSheet:(NSWindow*)sheet
return YES;
}
+- (BOOL)shouldUseCustomAppKitFullscreenTransition {
+ if (base::mac::IsOSMountainLionOrEarlier())
+ return NO;
+
+ NSView* root = [[self.window contentView] superview];
+ if (!root.layer)
+ return NO;
+
+ // AppKit on OSX 10.9 has a bug for applications linked against OSX 10.8 SDK
+ // and earlier. Under specific circumstances, it prevents the custom AppKit
+ // transition from working well. See http://crbug.com/396980 for more
+ // details.
+ if ([[self class] systemSettingsRequireMavericksAppKitFullscreenHack] &&
+ ![[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
+ return NO;
+ }
+
+ return YES;
+}
+
+- (NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow*)window {
+ DCHECK([window isEqual:self.window]);
+
+ if (![self shouldUseCustomAppKitFullscreenTransition])
+ return nil;
+
+ enterFullscreenTransition_.reset(
+ [[BrowserWindowEnterFullscreenTransition alloc]
+ initWithWindow:self.window]);
+ return [enterFullscreenTransition_ customWindowsToEnterFullScreen];
+}
+
+- (void)window:(NSWindow*)window
+ startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration {
+ DCHECK([window isEqual:self.window]);
+ [enterFullscreenTransition_
+ startCustomAnimationToEnterFullScreenWithDuration:duration];
+}
+
+- (BOOL)shouldConstrainFrameRect {
+ if ([enterFullscreenTransition_ shouldWindowBeUnconstrained])
+ return NO;
+
+ return [super shouldConstrainFrameRect];
+}
+
@end // @implementation BrowserWindowController(Private)
diff --git a/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h b/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h
new file mode 100644
index 0000000..d947200
--- /dev/null
+++ b/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_BROWSER_WINDOW_FULLSCREEN_TRANSITION_H_
+#define CHROME_BROWSER_UI_COCOA_BROWSER_WINDOW_FULLSCREEN_TRANSITION_H_
+
+#import <Cocoa/Cocoa.h>
+
+// This class is responsible for managing the custom transition of a
+// BrowserWindow from its normal state into an AppKit Fullscreen state.
+//
+// By default, when AppKit Fullscreens a window, it creates a new virtual
+// desktop and slides it in from the right of the screen. At the same time, the
+// old virtual desktop slides off to the left. This animation takes one second,
+// and the time is not customizable without elevated privileges or a
+// self-modifying binary
+// (https://code.google.com/p/chromium/issues/detail?id=414527). During that
+// second, no user interaction is possible.
+//
+// The default implementation of the AppKit transition smoothly animates a
+// window from its original size to the full size of the screen. At the
+// beginning of the animation, it takes a snapshot of the window's current
+// state. Then it resizes the window, calls drawRect: (theorized, not tested),
+// and takes a snapshot of the window's final state. The animation is a simple
+// crossfade between the two snapshots. This has a flaw. Frequently, the
+// renderer has not yet drawn content for the resized window by the time
+// drawRect: is called. As a result, the animation is effectively a no-op. When
+// the animation is finished, the new web content flashes in.
+//
+// The window's delegate can override two methods to customize the transition.
+// -customWindowsToEnterFullScreenForWindow:
+// The return of this method is an array of NSWindows. Each window that is
+// returned will be added to the new virtual desktop after the animation is
+// finished, but will not be a part of the animation itself.
+// -window:startCustomAnimationToEnterFullScreenWithDuration:
+// In this method, the window's delegate adds animations to the windows
+// returned in the above method.
+//
+// The goal of this class is to mimic the default animation, but instead of
+// taking a snapshot of the final web content, it uses the live web content
+// during the animation.
+//
+// See https://code.google.com/p/chromium/issues/detail?id=414527#c22 and its
+// preceding comments for a more detailed description of the implementation,
+// and the reasoning behind the decisions made.
+//
+// Recommended usage:
+// (Override method on NSWindow's delegate)
+// - (NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow*)window {
+// self.transition = [[[BrowserWindowEnterFullscreenTransition alloc]
+// initWithWindow:window] autorelease];
+// return [self.transition customWindowsToEnterFullScreen];
+// }
+//
+// (Override method on NSWindow's delegate)
+// - (void)window:(NSWindow*)window
+// startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration {
+// [self.transition
+// startCustomAnimationToEnterFullScreenWithDuration:duration];
+// }
+//
+// (Override method on NSWindow's delegate)
+// - (void)windowDidEnterFullScreen:(NSNotification*)notification {
+// self.transition = nil;
+// }
+//
+// (Override method on NSWindow)
+// - (NSRect)constrainFrameRect:(NSRect)frame toScreen:(NSScreen*)screen {
+// if (self.transition && ![self.transition shouldWindowBeConstrained])
+// return frame;
+// return [super constrainFrameRect:frame toScreen:screen];
+// }
+
+@interface BrowserWindowEnterFullscreenTransition : NSObject
+
+// Designated initializer. |window| is the NSWindow that is going to be moved
+// into a fullscreen Space (virtual desktop), and resized to have the same size
+// as the screen. |window|'s root view must be layer backed.
+- (instancetype)initWithWindow:(NSWindow*)window;
+
+// Returns the windows to be used in the custom transition.
+// - Takes a snapshot of the current window.
+// - Makes a new snapshot window which shows the snapshot in the same
+// location as the current window.
+// - Adds the style mask NSFullScreenWindowMask to the current window.
+// - Makes the current window transparent, and resizes the current window to
+// be the same size as the screen.
+- (NSArray*)customWindowsToEnterFullScreen;
+
+// Begins the animations used for the custom fullscreen transition.
+// - Animates the snapshot to the full size of the screen while fading it out.
+// - Animates the current window from it's original location to its final
+// location, while fading it in.
+// Note: The two animations are added to different layers in different windows.
+// There is no explicit logic to keep the two animations in sync. If this
+// proves to be a problem, the relevant layers should attempt to sync up their
+// time offsets with CACurrentMediaTime().
+- (void)startCustomAnimationToEnterFullScreenWithDuration:
+ (NSTimeInterval)duration;
+
+// When this method returns true, the NSWindow method
+// -constrainFrameRect:toScreen: must return the frame rect without
+// constraining it. The owner of the instance of this class is responsible for
+// hooking up this logic.
+- (BOOL)shouldWindowBeUnconstrained;
+
+@end
+
+#endif // CHROME_BROWSER_UI_COCOA_BROWSER_WINDOW_FULLSCREEN_TRANSITION_H_
diff --git a/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm b/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
new file mode 100644
index 0000000..cd947bd
--- /dev/null
+++ b/chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm
@@ -0,0 +1,302 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/ui/cocoa/browser_window_enter_fullscreen_transition.h"
+
+#include <QuartzCore/QuartzCore.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/mac/sdk_forward_declarations.h"
+
+namespace {
+
+NSString* const kPrimaryWindowAnimationID = @"PrimaryWindowAnimationID";
+NSString* const kSnapshotWindowAnimationID = @"SnapshotWindowAnimationID";
+NSString* const kAnimationIDKey = @"AnimationIDKey";
+
+// This class has two simultaneous animations to resize and reposition layers.
+// These animations must use the same timing function, otherwise there will be
+// visual discordance.
+NSString* const kTransformAnimationTimingFunction =
+ kCAMediaTimingFunctionEaseInEaseOut;
+
+} // namespace
+
+@interface BrowserWindowEnterFullscreenTransition () {
+ // The window which is undergoing the fullscreen transition.
+ base::scoped_nsobject<NSWindow> primaryWindow_;
+
+ // A layer that holds a snapshot of the original state of |primaryWindow_|.
+ base::scoped_nsobject<CALayer> snapshotLayer_;
+
+ // A temporary window that holds |snapshotLayer_|.
+ base::scoped_nsobject<NSWindow> snapshotWindow_;
+
+ // The animation applied to |snapshotLayer_|.
+ base::scoped_nsobject<CAAnimationGroup> snapshotAnimation_;
+
+ // The animation applied to the root layer of |primaryWindow_|.
+ base::scoped_nsobject<CAAnimationGroup> primaryWindowAnimation_;
+
+ // The frame of the |primaryWindow_| before the transition began.
+ NSRect primaryWindowInitialFrame_;
+
+ // The background color of |primaryWindow_| before the transition began.
+ base::scoped_nsobject<NSColor> primaryWindowInitialBackgroundColor_;
+
+ // Whether |primaryWindow_| was opaque before the transition began.
+ BOOL primaryWindowInitialOpaque_;
+
+ // Whether the instance is in the process of changing the size of
+ // |primaryWindow_|.
+ BOOL changingPrimaryWindowSize_;
+
+ // The frame that |primaryWindow_| is expected to have after the transition
+ // is finished.
+ NSRect primaryWindowFinalFrame_;
+}
+
+// Takes a snapshot of |primaryWindow_| and puts it in |snapshotLayer_|.
+- (void)takeSnapshot;
+
+// Creates |snapshotWindow_| and adds |snapshotLayer_| to it.
+- (void)makeAndPrepareSnapshotWindow;
+
+// This method has several effects on |primaryWindow_|:
+// - Saves current state.
+// - Makes window transparent, with clear background.
+// - Adds NSFullScreenWindowMask style mask.
+// - Sets the size to the screen's size.
+- (void)preparePrimaryWindowForAnimation;
+
+// Applies the fullscreen animation to |snapshotLayer_|.
+- (void)animateSnapshotWindowWithDuration:(CGFloat)duration;
+
+// Applies the fullscreen animation to the root layer of |primaryWindow_|.
+- (void)animatePrimaryWindowWithDuration:(CGFloat)duration;
+
+// Override of CAAnimation delegate method.
+- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag;
+
+// Returns the layer of the root view of |window|.
+- (CALayer*)rootLayerOfWindow:(NSWindow*)window;
+
+@end
+
+@implementation BrowserWindowEnterFullscreenTransition
+
+// -------------------------Public Methods----------------------------
+
+- (instancetype)initWithWindow:(NSWindow*)window {
+ DCHECK(window);
+ DCHECK([self rootLayerOfWindow:window]);
+ if ((self = [super init])) {
+ primaryWindow_.reset([window retain]);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [snapshotAnimation_ setDelegate:nil];
+ [primaryWindowAnimation_ setDelegate:nil];
+ [super dealloc];
+}
+
+- (NSArray*)customWindowsToEnterFullScreen {
+ [self takeSnapshot];
+ [self makeAndPrepareSnapshotWindow];
+ [self preparePrimaryWindowForAnimation];
+ return @[ primaryWindow_.get(), snapshotWindow_.get() ];
+}
+
+- (void)startCustomAnimationToEnterFullScreenWithDuration:
+ (NSTimeInterval)duration {
+ [self animateSnapshotWindowWithDuration:duration];
+ [self animatePrimaryWindowWithDuration:duration];
+}
+
+- (BOOL)shouldWindowBeUnconstrained {
+ return changingPrimaryWindowSize_;
+}
+
+// -------------------------Private Methods----------------------------
+
+- (void)takeSnapshot {
+ base::ScopedCFTypeRef<CGImageRef> windowSnapshot(CGWindowListCreateImage(
+ CGRectNull, kCGWindowListOptionIncludingWindow,
+ [primaryWindow_ windowNumber], kCGWindowImageBoundsIgnoreFraming));
+ snapshotLayer_.reset([[CALayer alloc] init]);
+ [snapshotLayer_ setFrame:NSRectToCGRect([primaryWindow_ frame])];
+ [snapshotLayer_ setContents:static_cast<id>(windowSnapshot.get())];
+ [snapshotLayer_ setAnchorPoint:CGPointMake(0, 0)];
+ [snapshotLayer_ setBackgroundColor:CGColorCreateGenericRGB(0, 0, 0, 0)];
+}
+
+- (void)makeAndPrepareSnapshotWindow {
+ DCHECK(snapshotLayer_);
+
+ snapshotWindow_.reset(
+ [[NSWindow alloc] initWithContentRect:[[primaryWindow_ screen] frame]
+ styleMask:0
+ backing:NSBackingStoreBuffered
+ defer:NO]);
+ [[snapshotWindow_ contentView] setWantsLayer:YES];
+ [snapshotWindow_ setOpaque:NO];
+ [snapshotWindow_ setBackgroundColor:[NSColor clearColor]];
+ [snapshotWindow_ setAnimationBehavior:NSWindowAnimationBehaviorNone];
+
+ [snapshotWindow_ orderFront:nil];
+ [[[snapshotWindow_ contentView] layer] addSublayer:snapshotLayer_];
+
+ // Compute the frame of the snapshot layer such that the snapshot is
+ // positioned exactly on top of the original position of |primaryWindow_|.
+ NSRect snapshotLayerFrame =
+ [snapshotWindow_ convertRectFromScreen:[primaryWindow_ frame]];
+ [snapshotLayer_ setFrame:snapshotLayerFrame];
+}
+
+- (void)preparePrimaryWindowForAnimation {
+ // Save initial state of |primaryWindow_|.
+ primaryWindowInitialFrame_ = [primaryWindow_ frame];
+ primaryWindowInitialBackgroundColor_.reset(
+ [[primaryWindow_ backgroundColor] copy]);
+ primaryWindowInitialOpaque_ = [primaryWindow_ isOpaque];
+
+ primaryWindowFinalFrame_ = [[primaryWindow_ screen] frame];
+
+ // Make |primaryWindow_| invisible. This must happen before the window is
+ // resized, since resizing the window will call drawRect: and cause content
+ // to flash over the entire screen.
+ [primaryWindow_ setOpaque:NO];
+ [primaryWindow_ setBackgroundColor:[NSColor clearColor]];
+ CALayer* rootLayer = [self rootLayerOfWindow:primaryWindow_];
+ rootLayer.opacity = 0;
+
+ // As soon as the style mask includes the flag NSFullScreenWindowMask, the
+ // window is expected to receive fullscreen layout. This must be set before
+ // the window is resized, as that causes a relayout.
+ [primaryWindow_
+ setStyleMask:[primaryWindow_ styleMask] | NSFullScreenWindowMask];
+
+ // Resize |primaryWindow_|.
+ changingPrimaryWindowSize_ = YES;
+ [primaryWindow_ setFrame:primaryWindowFinalFrame_ display:YES];
+ changingPrimaryWindowSize_ = NO;
+}
+
+- (void)animateSnapshotWindowWithDuration:(CGFloat)duration {
+ // Move the snapshot layer until it's bottom-left corner is at the
+ // bottom-left corner of the screen.
+ CABasicAnimation* positionAnimation =
+ [CABasicAnimation animationWithKeyPath:@"position"];
+ positionAnimation.toValue = [NSValue valueWithPoint:NSZeroPoint];
+ positionAnimation.timingFunction = [CAMediaTimingFunction
+ functionWithName:kTransformAnimationTimingFunction];
+
+ // Expand the bounds until it covers the screen.
+ NSRect finalBounds = NSMakeRect(0, 0, NSWidth(primaryWindowFinalFrame_),
+ NSHeight(primaryWindowFinalFrame_));
+ CABasicAnimation* boundsAnimation =
+ [CABasicAnimation animationWithKeyPath:@"bounds"];
+ boundsAnimation.toValue = [NSValue valueWithRect:finalBounds];
+ boundsAnimation.timingFunction = [CAMediaTimingFunction
+ functionWithName:kTransformAnimationTimingFunction];
+
+ // Fade out the snapshot layer.
+ CABasicAnimation* opacityAnimation =
+ [CABasicAnimation animationWithKeyPath:@"opacity"];
+ opacityAnimation.toValue = @(0.0);
+ opacityAnimation.timingFunction =
+ [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
+
+ // Fill forwards, and don't remove the animation. When the animation
+ // completes, the entire window will be removed.
+ CAAnimationGroup* group = [CAAnimationGroup animation];
+ group.removedOnCompletion = NO;
+ group.fillMode = kCAFillModeForwards;
+ group.animations = @[ positionAnimation, boundsAnimation, opacityAnimation ];
+ group.duration = duration;
+ [group setValue:kSnapshotWindowAnimationID forKey:kAnimationIDKey];
+ group.delegate = self;
+
+ snapshotAnimation_.reset([group retain]);
+ [snapshotLayer_ addAnimation:group forKey:nil];
+}
+
+- (void)animatePrimaryWindowWithDuration:(CGFloat)duration {
+ // As soon as the window's root layer is scaled down, the opacity should be
+ // set back to 1. There are a couple of ways to do this. The easiest is to
+ // just have a dummy animation as part of the same animation group.
+ CABasicAnimation* opacityAnimation =
+ [CABasicAnimation animationWithKeyPath:@"opacity"];
+ opacityAnimation.fromValue = @(1.0);
+ opacityAnimation.toValue = @(1.0);
+
+ // The root layer's size should start scaled down to the initial size of
+ // |primaryWindow_|. The animation increases the size until the root layer
+ // fills the screen.
+ NSRect initialFrame = primaryWindowInitialFrame_;
+ NSRect endFrame = primaryWindowFinalFrame_;
+ CGFloat xScale = NSWidth(initialFrame) / NSWidth(endFrame);
+ CGFloat yScale = NSHeight(initialFrame) / NSHeight(endFrame);
+ CATransform3D initial = CATransform3DMakeScale(xScale, yScale, 1);
+ CABasicAnimation* transformAnimation =
+ [CABasicAnimation animationWithKeyPath:@"transform"];
+ transformAnimation.fromValue = [NSValue valueWithCATransform3D:initial];
+
+ CALayer* root = [self rootLayerOfWindow:primaryWindow_];
+
+ // Calculate the initial position of the root layer. This calculation is
+ // agnostic of the anchorPoint.
+ CGFloat layerStartPositionDeltaX = NSMidX(initialFrame) - NSMidX(endFrame);
+ CGFloat layerStartPositionDeltaY = NSMidY(initialFrame) - NSMidY(endFrame);
+ NSPoint layerStartPosition =
+ NSMakePoint(root.position.x + layerStartPositionDeltaX,
+ root.position.y + layerStartPositionDeltaY);
+
+ // Animate the primary window from its initial position.
+ CABasicAnimation* positionAnimation =
+ [CABasicAnimation animationWithKeyPath:@"position"];
+ positionAnimation.fromValue = [NSValue valueWithPoint:layerStartPosition];
+
+ CAAnimationGroup* group = [CAAnimationGroup animation];
+ group.removedOnCompletion = NO;
+ group.fillMode = kCAFillModeForwards;
+ group.animations =
+ @[ transformAnimation, opacityAnimation, positionAnimation ];
+ group.timingFunction = [CAMediaTimingFunction
+ functionWithName:kTransformAnimationTimingFunction];
+ group.duration = duration;
+ [group setValue:kPrimaryWindowAnimationID forKey:kAnimationIDKey];
+ group.delegate = self;
+
+ primaryWindowAnimation_.reset([group retain]);
+
+ [root addAnimation:group forKey:kPrimaryWindowAnimationID];
+}
+
+- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)flag {
+ NSString* animationID = [theAnimation valueForKey:kAnimationIDKey];
+ if ([animationID isEqual:kSnapshotWindowAnimationID]) {
+ [snapshotWindow_ orderOut:nil];
+ snapshotWindow_.reset();
+ snapshotLayer_.reset();
+ return;
+ }
+
+ if ([animationID isEqual:kPrimaryWindowAnimationID]) {
+ [primaryWindow_ setOpaque:YES];
+ [primaryWindow_ setBackgroundColor:primaryWindowInitialBackgroundColor_];
+ CALayer* root = [self rootLayerOfWindow:primaryWindow_];
+ root.opacity = 1;
+ [root removeAnimationForKey:kPrimaryWindowAnimationID];
+ }
+}
+
+- (CALayer*)rootLayerOfWindow:(NSWindow*)window {
+ return [[[window contentView] superview] layer];
+}
+
+@end
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 82a8e3a..048aa98 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -302,6 +302,8 @@
'browser/ui/cocoa/browser_window_layout.mm',
'browser/ui/cocoa/browser_window_controller_private.h',
'browser/ui/cocoa/browser_window_controller_private.mm',
+ 'browser/ui/cocoa/browser_window_enter_fullscreen_transition.h',
+ 'browser/ui/cocoa/browser_window_enter_fullscreen_transition.mm',
'browser/ui/cocoa/browser_window_factory_cocoa.mm',
'browser/ui/cocoa/browser_window_utils.h',
'browser/ui/cocoa/browser_window_utils.mm',