Merge from Chromium at DEPS revision 240154

This commit was generated by merge_to_master.py.

Change-Id: I8f2ba858cf0e7f413dddedc2ae91dc37f7136c2e
diff --git a/ash/OWNERS b/ash/OWNERS
index a3e7809..e7dd72d 100644
--- a/ash/OWNERS
+++ b/ash/OWNERS
@@ -3,6 +3,9 @@
 oshima@chromium.org
 sky@chromium.org
 
+# These are for the common case of adding or renaming a few items. If you're
+# doing structural changes, please get a review from a reviewer above.
+per-file ash.gyp=*
 per-file ash_strings.grd=*
 per-file ash_chromeos_strings.grdp=*
 per-file ash_switches.*=*
diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc
index 003562e..b50959f 100644
--- a/ash/accelerators/accelerator_commands.cc
+++ b/ash/accelerators/accelerator_commands.cc
@@ -9,6 +9,7 @@
 #include "ash/wm/window_cycle_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "content/public/browser/user_metrics.h"
 
 namespace ash {
 namespace accelerators {
@@ -25,8 +26,6 @@
   wm::WindowState* window_state = wm::GetWindowState(window);
   if (!window_state->CanMinimize())
     return false;
-  ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
-      ash::UMA_MINIMIZE_PER_KEY);
   window_state->Minimize();
   return true;
 }
@@ -35,6 +34,8 @@
   wm::WindowState* window_state = wm::GetActiveWindowState();
   if (!window_state)
     return;
+  content::RecordAction(
+      content::UserMetricsAction("Accel_Toggle_Maximized"));
   // Get out of fullscreen when in fullscreen mode.
   if (window_state->IsFullscreen())
     ToggleFullscreen();
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index d9c2d79..0ac3f7e 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -20,7 +20,6 @@
 #include "ash/focus_cycler.h"
 #include "ash/ime_control_delegate.h"
 #include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_delegate.h"
 #include "ash/magnifier/magnification_controller.h"
 #include "ash/magnifier/partial_magnification_controller.h"
 #include "ash/media_delegate.h"
@@ -30,6 +29,7 @@
 #include "ash/rotator/screen_rotation.h"
 #include "ash/screenshot_delegate.h"
 #include "ash/session_state_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -82,6 +82,7 @@
 namespace {
 
 using internal::DisplayInfo;
+using content::UserMetricsAction;
 
 bool DebugShortcutsEnabled() {
 #if defined(NDEBUG)
@@ -92,52 +93,13 @@
 #endif
 }
 
-void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
-  Shell* shell = Shell::GetInstance();
-
-  if (accelerator.key_code() == ui::VKEY_TAB)
-    content::RecordAction(content::UserMetricsAction("Accel_PrevWindow_Tab"));
-
-  if (switches::UseOverviewMode()) {
-    shell->window_selector_controller()->HandleCycleWindow(
-        WindowSelector::BACKWARD);
-    return;
-  }
-  shell->window_cycle_controller()->HandleCycleWindow(
-      WindowCycleController::BACKWARD, accelerator.IsAltDown());
-}
-
-void HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
-  Shell* shell = Shell::GetInstance();
-
-  if (accelerator.key_code() == ui::VKEY_TAB)
-    content::RecordAction(content::UserMetricsAction("Accel_NextWindow_Tab"));
-
-  if (switches::UseOverviewMode()) {
-    shell->window_selector_controller()->HandleCycleWindow(
-        WindowSelector::FORWARD);
-    return;
-  }
-  shell->window_cycle_controller()->HandleCycleWindow(
-      WindowCycleController::FORWARD, accelerator.IsAltDown());
-}
-
-void HandleCycleLinear(const ui::Accelerator& accelerator) {
-  Shell* shell = Shell::GetInstance();
-
-  // TODO(jamescook): When overview becomes the default the AcceleratorAction
-  // should be renamed from CYCLE_LINEAR to TOGGLE_OVERVIEW.
-  if (switches::UseOverviewMode()) {
-    content::RecordAction(content::UserMetricsAction("Accel_Overview_F5"));
-    shell->window_selector_controller()->ToggleOverview();
-    return;
-  }
-  if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1)
-    content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5"));
-  shell->window_cycle_controller()->HandleLinearCycleWindow();
-}
-
 bool HandleAccessibleFocusCycle(bool reverse) {
+  if (reverse) {
+    content::RecordAction(UserMetricsAction("Accel_Accessible_Focus_Previous"));
+  } else {
+    content::RecordAction(UserMetricsAction("Accel_Accessible_Focus_Next"));
+  }
+
   if (!Shell::GetInstance()->accessibility_delegate()->
       IsSpokenFeedbackEnabled()) {
     return false;
@@ -162,132 +124,91 @@
   return true;
 }
 
-void HandleSilenceSpokenFeedback() {
-  AccessibilityDelegate* delegate =
-      Shell::GetInstance()->accessibility_delegate();
-  if (!delegate->IsSpokenFeedbackEnabled())
-    return;
-  delegate->SilenceSpokenFeedback();
-}
-
-#if defined(OS_CHROMEOS)
-bool HandleLock() {
-  Shell::GetInstance()->session_state_delegate()->LockScreen();
-  return true;
-}
-
-bool HandleFileManager() {
-  Shell::GetInstance()->new_window_delegate()->OpenFileManager();
-  return true;
-}
-
-bool HandleCrosh() {
-  Shell::GetInstance()->new_window_delegate()->OpenCrosh();
-  return true;
-}
-
-bool HandleToggleSpokenFeedback() {
-  Shell::GetInstance()->accessibility_delegate()->
-      ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
-  return true;
-}
-
-bool SwitchToNextUser() {
-  if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled())
-    return false;
-  ash::SessionStateDelegate* delegate =
-      ash::Shell::GetInstance()->session_state_delegate();
-  if (delegate->NumberOfLoggedInUsers() <= 1)
-    return false;
-  MultiProfileUMA::RecordSwitchActiveUser(
-      MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR);
-  delegate->SwitchActiveUserToNext();
-  return true;
-}
-
-#endif  // defined(OS_CHROMEOS)
-
-bool HandleRotatePaneFocus(Shell::Direction direction) {
+bool HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
   Shell* shell = Shell::GetInstance();
-  switch (direction) {
-    case Shell::FORWARD:
-      shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
-      break;
-    case Shell::BACKWARD:
-      shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
-      break;
+
+  if (accelerator.key_code() == ui::VKEY_TAB)
+    content::RecordAction(content::UserMetricsAction("Accel_PrevWindow_Tab"));
+
+  if (switches::UseOverviewMode()) {
+    shell->window_selector_controller()->HandleCycleWindow(
+        WindowSelector::BACKWARD);
+    return true;
   }
+  shell->window_cycle_controller()->HandleCycleWindow(
+      WindowCycleController::BACKWARD, accelerator.IsAltDown());
   return true;
 }
 
-// Rotate the active window.
-bool HandleRotateActiveWindow() {
-  aura::Window* active_window = wm::GetActiveWindow();
-  if (active_window) {
-    // The rotation animation bases its target transform on the current
-    // rotation and position. Since there could be an animation in progress
-    // right now, queue this animation so when it starts it picks up a neutral
-    // rotation and position. Use replace so we only enqueue one at a time.
-    active_window->layer()->GetAnimator()->
-        set_preemption_strategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
-    active_window->layer()->GetAnimator()->StartAnimation(
-        new ui::LayerAnimationSequence(
-            new ash::ScreenRotation(360, active_window->layer())));
+bool HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
+  Shell* shell = Shell::GetInstance();
+
+  if (accelerator.key_code() == ui::VKEY_TAB)
+    content::RecordAction(content::UserMetricsAction("Accel_NextWindow_Tab"));
+
+  if (switches::UseOverviewMode()) {
+    shell->window_selector_controller()->HandleCycleWindow(
+        WindowSelector::FORWARD);
+    return true;
   }
+  shell->window_cycle_controller()->HandleCycleWindow(
+      WindowCycleController::FORWARD, accelerator.IsAltDown());
   return true;
 }
 
-gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) {
-  switch (current) {
-    case gfx::Display::ROTATE_0:
-      return gfx::Display::ROTATE_90;
-    case gfx::Display::ROTATE_90:
-      return gfx::Display::ROTATE_180;
-    case gfx::Display::ROTATE_180:
-      return gfx::Display::ROTATE_270;
-    case gfx::Display::ROTATE_270:
-      return gfx::Display::ROTATE_0;
+bool HandleCycleLinear(const ui::Accelerator& accelerator) {
+  Shell* shell = Shell::GetInstance();
+
+  // TODO(jamescook): When overview becomes the default the AcceleratorAction
+  // should be renamed from CYCLE_LINEAR to TOGGLE_OVERVIEW.
+  if (switches::UseOverviewMode()) {
+    content::RecordAction(content::UserMetricsAction("Accel_Overview_F5"));
+    shell->window_selector_controller()->ToggleOverview();
+    return true;
   }
-  NOTREACHED() << "Unknown rotation:" << current;
-  return gfx::Display::ROTATE_0;
+  if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1)
+    content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5"));
+  shell->window_cycle_controller()->HandleLinearCycleWindow();
+  return true;
 }
 
-bool HandleScaleUI(bool up) {
-  internal::DisplayManager* display_manager =
-      Shell::GetInstance()->display_manager();
-  int64 display_id = display_manager->GetDisplayIdForUIScaling();
-  if (display_id == gfx::Display::kInvalidDisplayID)
+bool HandleDisableCapsLock(ui::KeyboardCode key_code,
+                           ui::EventType previous_event_type,
+                           ui::KeyboardCode previous_key_code) {
+  Shell* shell = Shell::GetInstance();
+
+  if (previous_event_type == ui::ET_KEY_RELEASED ||
+      (previous_key_code != ui::VKEY_LSHIFT &&
+       previous_key_code != ui::VKEY_SHIFT &&
+       previous_key_code != ui::VKEY_RSHIFT)) {
+    // If something else was pressed between the Shift key being pressed
+    // and released, then ignore the release of the Shift key.
     return false;
-  const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id);
-  float next_scale =
-      internal::DisplayManager::GetNextUIScale(display_info, up);
-  display_manager->SetDisplayUIScale(display_id, next_scale);
+  }
+  content::RecordAction(UserMetricsAction("Accel_Disable_Caps_Lock"));
+  if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
+    shell->caps_lock_delegate()->SetCapsLockEnabled(false);
+    return true;
+  }
+  return false;
+}
+
+bool HandleFocusLauncher() {
+  Shell* shell = Shell::GetInstance();
+  content::RecordAction(content::UserMetricsAction("Accel_Focus_Launcher"));
+  return shell->focus_cycler()->FocusWidget(
+      Launcher::ForPrimaryDisplay()->shelf_widget());
+}
+
+bool HandleLaunchAppN(int n) {
+  content::RecordAction(UserMetricsAction("Accel_Launch_App"));
+  Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(n);
   return true;
 }
 
-bool HandleScaleReset() {
-  internal::DisplayManager* display_manager =
-      Shell::GetInstance()->display_manager();
-  int64 display_id = display_manager->GetDisplayIdForUIScaling();
-  if (display_id == gfx::Display::kInvalidDisplayID)
-    return false;
-  display_manager->SetDisplayUIScale(display_id, 1.0f);
-  return true;
-}
-
-// Rotates the screen.
-bool HandleRotateScreen() {
-  gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
-  gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point);
-  const DisplayInfo& display_info =
-      Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
-  Shell::GetInstance()->display_manager()->SetDisplayRotation(
-      display.id(), GetNextRotation(display_info.rotation()));
-  return true;
-}
-
-bool HandleToggleRootWindowFullScreen() {
-  Shell::GetPrimaryRootWindow()->GetDispatcher()->host()->ToggleFullScreen();
+bool HandleLaunchLastApp() {
+  content::RecordAction(UserMetricsAction("Accel_Launch_Last_App"));
+  Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
   return true;
 }
 
@@ -330,6 +251,451 @@
   return true;
 }
 
+bool HandleNewIncognitoWindow() {
+  content::RecordAction(UserMetricsAction("Accel_New_Incognito_Window"));
+  bool incognito_allowed =
+    Shell::GetInstance()->delegate()->IsIncognitoAllowed();
+  if (incognito_allowed)
+    Shell::GetInstance()->new_window_delegate()->NewWindow(
+        true /* is_incognito */);
+  return incognito_allowed;
+}
+
+bool HandleNewTab(ui::KeyboardCode key_code) {
+  if (key_code == ui::VKEY_T)
+    content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
+  Shell::GetInstance()->new_window_delegate()->NewTab();
+  return true;
+}
+
+bool HandleNewWindow() {
+  content::RecordAction(content::UserMetricsAction("Accel_New_Window"));
+  Shell::GetInstance()->new_window_delegate()->NewWindow(
+      false /* is_incognito */);
+  return true;
+}
+
+bool HandleNextIme(ImeControlDelegate* ime_control_delegate,
+                   ui::EventType previous_event_type,
+                   ui::KeyboardCode previous_key_code) {
+  // This check is necessary e.g. not to process the Shift+Alt+
+  // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/
+  // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
+  // is released.
+  if (previous_event_type == ui::ET_KEY_RELEASED &&
+      // Workaround for crbug.com/139556. CJK IME users tend to press
+      // Enter (or Space) and Shift+Alt almost at the same time to commit
+      // an IME string and then switch from the IME to the English layout.
+      // This workaround allows the user to trigger NEXT_IME even if the
+      // user presses Shift+Alt before releasing Enter.
+      // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
+      previous_key_code != ui::VKEY_RETURN &&
+      previous_key_code != ui::VKEY_SPACE) {
+    // We totally ignore this accelerator.
+    // TODO(mazda): Fix crbug.com/158217
+    return false;
+  }
+  content::RecordAction(UserMetricsAction("Accel_Next_Ime"));
+  if (ime_control_delegate)
+    return ime_control_delegate->HandleNextIme();
+  return false;
+}
+
+bool HandleOpenFeedbackPage() {
+  content::RecordAction(UserMetricsAction("Accel_Open_Feedback_Page"));
+  ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage();
+  return true;
+}
+
+bool HandlePositionCenter() {
+  content::RecordAction(UserMetricsAction("Accel_Window_Position_Center"));
+  aura::Window* window = wm::GetActiveWindow();
+  // Docked windows do not support centering and ignore accelerator.
+  if (window && !wm::GetWindowState(window)->IsDocked()) {
+    wm::CenterWindow(window);
+    return true;
+  }
+  return false;
+}
+
+bool HandlePreviousIme(ImeControlDelegate* ime_control_delegate,
+                       const ui::Accelerator& accelerator) {
+  content::RecordAction(UserMetricsAction("Accel_Previous_Ime"));
+  if (ime_control_delegate)
+    return ime_control_delegate->HandlePreviousIme(accelerator);
+  return false;
+}
+
+bool HandleRestoreTab() {
+  content::RecordAction(content::UserMetricsAction("Accel_Restore_Tab"));
+  Shell::GetInstance()->new_window_delegate()->RestoreTab();
+  return true;
+}
+
+bool HandleRotatePaneFocus(Shell::Direction direction) {
+  Shell* shell = Shell::GetInstance();
+  switch (direction) {
+    // TODO(stevet): Not sure if this is the same as IDC_FOCUS_NEXT_PANE.
+    case Shell::FORWARD: {
+      content::RecordAction(UserMetricsAction("Accel_Focus_Next_Pane"));
+      shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
+      break;
+    }
+    case Shell::BACKWARD: {
+      content::RecordAction(UserMetricsAction("Accel_Focus_Previous_Pane"));
+      shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
+      break;
+    }
+  }
+  return true;
+}
+
+// Rotate the active window.
+bool HandleRotateActiveWindow() {
+  content::RecordAction(UserMetricsAction("Accel_Rotate_Window"));
+  aura::Window* active_window = wm::GetActiveWindow();
+  if (active_window) {
+    // The rotation animation bases its target transform on the current
+    // rotation and position. Since there could be an animation in progress
+    // right now, queue this animation so when it starts it picks up a neutral
+    // rotation and position. Use replace so we only enqueue one at a time.
+    active_window->layer()->GetAnimator()->
+        set_preemption_strategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
+    active_window->layer()->GetAnimator()->StartAnimation(
+        new ui::LayerAnimationSequence(
+            new ash::ScreenRotation(360, active_window->layer())));
+  }
+  return true;
+}
+
+gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) {
+  switch (current) {
+    case gfx::Display::ROTATE_0:
+      return gfx::Display::ROTATE_90;
+    case gfx::Display::ROTATE_90:
+      return gfx::Display::ROTATE_180;
+    case gfx::Display::ROTATE_180:
+      return gfx::Display::ROTATE_270;
+    case gfx::Display::ROTATE_270:
+      return gfx::Display::ROTATE_0;
+  }
+  NOTREACHED() << "Unknown rotation:" << current;
+  return gfx::Display::ROTATE_0;
+}
+
+// Rotates the screen.
+bool HandleRotateScreen() {
+  content::RecordAction(UserMetricsAction("Accel_Rotate_Window"));
+  gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
+  gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point);
+  const DisplayInfo& display_info =
+      Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
+  Shell::GetInstance()->display_manager()->SetDisplayRotation(
+      display.id(), GetNextRotation(display_info.rotation()));
+  return true;
+}
+
+bool HandleScaleReset() {
+  internal::DisplayManager* display_manager =
+      Shell::GetInstance()->display_manager();
+  int64 display_id = display_manager->GetDisplayIdForUIScaling();
+  if (display_id == gfx::Display::kInvalidDisplayID)
+    return false;
+
+  content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Reset"));
+
+  display_manager->SetDisplayUIScale(display_id, 1.0f);
+  return true;
+}
+
+bool HandleScaleUI(bool up) {
+  internal::DisplayManager* display_manager =
+      Shell::GetInstance()->display_manager();
+  int64 display_id = display_manager->GetDisplayIdForUIScaling();
+  if (display_id == gfx::Display::kInvalidDisplayID)
+    return false;
+
+  if (up) {
+    content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Up"));
+  } else {
+    content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Down"));
+  }
+
+  const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id);
+  float next_scale =
+      internal::DisplayManager::GetNextUIScale(display_info, up);
+  display_manager->SetDisplayUIScale(display_id, next_scale);
+  return true;
+}
+
+bool HandleSwapPrimaryDisplay() {
+  content::RecordAction(UserMetricsAction("Accel_Swap_Primary_Display"));
+  Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
+  return true;
+}
+
+bool HandleShowKeyboardOverlay() {
+  content::RecordAction(UserMetricsAction("Accel_Show_Keyboard_Overlay"));
+  ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay();
+
+  return true;
+}
+
+void HandleShowMessageCenterBubble() {
+  content::RecordAction(UserMetricsAction("Accel_Show_Message_Center_Bubble"));
+  internal::RootWindowController* controller =
+    internal::RootWindowController::ForTargetRootWindow();
+  internal::StatusAreaWidget* status_area_widget =
+    controller->shelf()->status_area_widget();
+  if (status_area_widget) {
+    WebNotificationTray* notification_tray =
+      status_area_widget->web_notification_tray();
+    if (notification_tray->visible())
+      notification_tray->ShowMessageCenterBubble();
+  }
+}
+
+bool HandleShowOak() {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+        switches::kAshEnableOak)) {
+    oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow());
+    return true;
+  }
+  return false;
+}
+
+bool HandleShowSystemTrayBubble() {
+  content::RecordAction(UserMetricsAction("Accel_Show_System_Tray_Bubble"));
+  internal::RootWindowController* controller =
+    internal::RootWindowController::ForTargetRootWindow();
+  if (!controller->GetSystemTray()->HasSystemBubble()) {
+    controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
+    return true;
+  }
+  return false;
+}
+
+bool HandleShowTaskManager() {
+  content::RecordAction(UserMetricsAction("Accel_Show_Task_Manager"));
+  Shell::GetInstance()->new_window_delegate()->ShowTaskManager();
+  return true;
+}
+
+void HandleSilenceSpokenFeedback() {
+  content::RecordAction(UserMetricsAction("Accel_Silence_Spoken_Feedback"));
+
+  AccessibilityDelegate* delegate =
+      Shell::GetInstance()->accessibility_delegate();
+  if (!delegate->IsSpokenFeedbackEnabled())
+    return;
+  delegate->SilenceSpokenFeedback();
+}
+
+bool HandleSwitchIme(ImeControlDelegate* ime_control_delegate,
+                     const ui::Accelerator& accelerator) {
+  content::RecordAction(UserMetricsAction("Accel_Switch_Ime"));
+  if (ime_control_delegate)
+    return ime_control_delegate->HandleSwitchIme(accelerator);
+  return false;
+}
+
+bool HandleTakePartialScreenshot(ScreenshotDelegate* screenshot_delegate) {
+  content::RecordAction(UserMetricsAction("Accel_Take_Partial_Screenshot"));
+  if (screenshot_delegate) {
+    ash::PartialScreenshotView::StartPartialScreenshot(
+        screenshot_delegate);
+  }
+  // Return true to prevent propagation of the key event because
+  // this key combination is reserved for partial screenshot.
+  return true;
+}
+
+bool HandleTakeScreenshot(ScreenshotDelegate* screenshot_delegate) {
+  content::RecordAction(UserMetricsAction("Accel_Take_Screenshot"));
+  if (screenshot_delegate &&
+      screenshot_delegate->CanTakeScreenshot()) {
+    screenshot_delegate->HandleTakeScreenshotForAllRootWindows();
+  }
+  // Return true to prevent propagation of the key event.
+  return true;
+}
+
+bool HandleToggleAppList(ui::KeyboardCode key_code,
+                         ui::EventType previous_event_type,
+                         ui::KeyboardCode previous_key_code,
+                         const ui::Accelerator& accelerator) {
+  // If something else was pressed between the Search key (LWIN)
+  // being pressed and released, then ignore the release of the
+  // Search key.
+  if (key_code == ui::VKEY_LWIN &&
+      (previous_event_type == ui::ET_KEY_RELEASED ||
+       previous_key_code != ui::VKEY_LWIN))
+    return false;
+  if (key_code == ui::VKEY_LWIN)
+    content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
+  // When spoken feedback is enabled, we should neither toggle the list nor
+  // consume the key since Search+Shift is one of the shortcuts the a11y
+  // feature uses. crbug.com/132296
+  DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
+  if (Shell::GetInstance()->accessibility_delegate()->
+      IsSpokenFeedbackEnabled())
+    return false;
+  ash::Shell::GetInstance()->ToggleAppList(NULL);
+  return true;
+}
+
+bool HandleToggleCapsLock(ui::KeyboardCode key_code,
+                          ui::EventType previous_event_type,
+                          ui::KeyboardCode previous_key_code) {
+  Shell* shell = Shell::GetInstance();
+  if (key_code == ui::VKEY_LWIN) {
+    // If something else was pressed between the Search key (LWIN)
+    // being pressed and released, then ignore the release of the
+    // Search key.
+    // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
+    if (previous_event_type == ui::ET_KEY_RELEASED ||
+        previous_key_code != ui::VKEY_LWIN)
+      return false;
+  }
+  content::RecordAction(UserMetricsAction("Accel_Toggle_Caps_Lock"));
+  shell->caps_lock_delegate()->ToggleCapsLock();
+  return true;
+}
+
+bool HandleToggleFullscreen(ui::KeyboardCode key_code) {
+  if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
+    content::RecordAction(UserMetricsAction("Accel_Fullscreen_F4"));
+  }
+  accelerators::ToggleFullscreen();
+  return true;
+}
+
+bool HandleToggleRootWindowFullScreen() {
+  Shell::GetPrimaryRootWindow()->GetDispatcher()->host()->ToggleFullScreen();
+  return true;
+}
+
+bool HandleWindowSnap(int action) {
+  wm::WindowState* window_state = wm::GetActiveWindowState();
+  // Disable window snapping shortcut key for full screen window due to
+  // http://crbug.com/135487.
+  if (!window_state ||
+      window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL ||
+      window_state->IsFullscreen()) {
+    return false;
+  }
+
+  if (action == WINDOW_SNAP_LEFT) {
+    content::RecordAction(UserMetricsAction("Accel_Window_Snap_Left"));
+  } else {
+    content::RecordAction(UserMetricsAction("Accel_Window_Snap_Right"));
+  }
+
+  internal::SnapSizer::SnapWindow(window_state,
+      action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
+      internal::SnapSizer::RIGHT_EDGE);
+  return true;
+}
+
+bool HandleWindowMinimize() {
+  content::RecordAction(
+      content::UserMetricsAction("Accel_Toggle_Minimized_Minus"));
+  return accelerators::ToggleMinimized();
+}
+
+#if defined(OS_CHROMEOS)
+bool HandleAddRemoveDisplay() {
+  content::RecordAction(UserMetricsAction("Accel_Add_Remove_Display"));
+  Shell::GetInstance()->display_manager()->AddRemoveDisplay();
+  return true;
+}
+
+bool HandleCrosh() {
+  content::RecordAction(UserMetricsAction("Accel_Open_Crosh"));
+
+  Shell::GetInstance()->new_window_delegate()->OpenCrosh();
+  return true;
+}
+
+bool HandleFileManager() {
+  content::RecordAction(UserMetricsAction("Accel_Open_File_Manager"));
+
+  Shell::GetInstance()->new_window_delegate()->OpenFileManager();
+  return true;
+}
+
+bool HandleLock(ui::KeyboardCode key_code) {
+  content::RecordAction(UserMetricsAction("Accel_LockScreen_L"));
+  Shell::GetInstance()->session_state_delegate()->LockScreen();
+  return true;
+}
+
+bool HandleCycleUser(SessionStateDelegate::CycleUser cycle_user) {
+  if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled())
+    return false;
+  ash::SessionStateDelegate* delegate =
+      ash::Shell::GetInstance()->session_state_delegate();
+  if (delegate->NumberOfLoggedInUsers() <= 1)
+    return false;
+  MultiProfileUMA::RecordSwitchActiveUser(
+      MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR);
+  switch (cycle_user) {
+    case SessionStateDelegate::CYCLE_TO_NEXT_USER:
+      content::RecordAction(UserMetricsAction("Accel_Switch_To_Next_User"));
+      break;
+    case SessionStateDelegate::CYCLE_TO_PREVIOUS_USER:
+      content::RecordAction(UserMetricsAction("Accel_Switch_To_Previous_User"));
+      break;
+  }
+  delegate->CycleActiveUser(cycle_user);
+  return true;
+}
+
+bool HandleToggleMirrorMode() {
+  content::RecordAction(UserMetricsAction("Accel_Toggle_Mirror_Mode"));
+  Shell::GetInstance()->display_controller()->ToggleMirrorMode();
+  return true;
+}
+
+bool HandleToggleSpokenFeedback() {
+  content::RecordAction(UserMetricsAction("Accel_Toggle_Spoken_Feedback"));
+
+  Shell::GetInstance()->accessibility_delegate()->
+      ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
+  return true;
+}
+
+bool HandleTouchHudClear() {
+  internal::RootWindowController* controller =
+      internal::RootWindowController::ForTargetRootWindow();
+  if (controller->touch_hud_debug()) {
+    controller->touch_hud_debug()->Clear();
+    return true;
+  }
+  return false;
+}
+
+bool HandleTouchHudModeChange() {
+  internal::RootWindowController* controller =
+      internal::RootWindowController::ForTargetRootWindow();
+  if (controller->touch_hud_debug()) {
+    controller->touch_hud_debug()->ChangeToNextMode();
+    return true;
+  }
+  return false;
+}
+
+bool HandleTouchHudProjectToggle() {
+  content::RecordAction(UserMetricsAction("Accel_Touch_Hud_Clear"));
+  bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
+  Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
+  return true;
+}
+
+#endif  // defined(OS_CHROMEOS)
+
+// Debug print methods.
+
 bool HandlePrintLayerHierarchy() {
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
   for (size_t i = 0; i < root_windows.size(); ++i) {
@@ -565,25 +931,18 @@
     case ACCESSIBLE_FOCUS_PREVIOUS:
       return HandleAccessibleFocusCycle(true);
     case CYCLE_BACKWARD_MRU:
-      HandleCycleBackwardMRU(accelerator);
-      return true;
+      return HandleCycleBackwardMRU(accelerator);
     case CYCLE_FORWARD_MRU:
-      HandleCycleForwardMRU(accelerator);
-      return true;
+      return HandleCycleForwardMRU(accelerator);
     case CYCLE_LINEAR:
-      HandleCycleLinear(accelerator);
-      return true;
+      return HandleCycleLinear(accelerator);
 #if defined(OS_CHROMEOS)
     case ADD_REMOVE_DISPLAY:
-      Shell::GetInstance()->display_manager()->AddRemoveDisplay();
-      return true;
+      return HandleAddRemoveDisplay();
     case TOGGLE_MIRROR_MODE:
-      Shell::GetInstance()->display_controller()->ToggleMirrorMode();
-      return true;
+      return HandleToggleMirrorMode();
     case LOCK_SCREEN:
-      if (key_code == ui::VKEY_L)
-        content::RecordAction(content::UserMetricsAction("Accel_LockScreen_L"));
-      return HandleLock();
+      return HandleLock(key_code);
     case OPEN_FILE_MANAGER:
       return HandleFileManager();
     case OPEN_CROSH:
@@ -592,129 +951,53 @@
       HandleSilenceSpokenFeedback();
       break;
     case SWAP_PRIMARY_DISPLAY:
-      Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
-      return true;
+      return HandleSwapPrimaryDisplay();
     case SWITCH_TO_NEXT_USER:
-      return SwitchToNextUser();
+      return HandleCycleUser(SessionStateDelegate::CYCLE_TO_NEXT_USER);
+    case SWITCH_TO_PREVIOUS_USER:
+      return HandleCycleUser(SessionStateDelegate::CYCLE_TO_PREVIOUS_USER);
     case TOGGLE_SPOKEN_FEEDBACK:
       return HandleToggleSpokenFeedback();
     case TOGGLE_WIFI:
       Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi();
       return true;
-    case TOUCH_HUD_CLEAR: {
-      internal::RootWindowController* controller =
-          internal::RootWindowController::ForTargetRootWindow();
-      if (controller->touch_hud_debug()) {
-        controller->touch_hud_debug()->Clear();
-        return true;
-      }
-      return false;
-    }
-    case TOUCH_HUD_MODE_CHANGE: {
-      internal::RootWindowController* controller =
-          internal::RootWindowController::ForTargetRootWindow();
-      if (controller->touch_hud_debug()) {
-        controller->touch_hud_debug()->ChangeToNextMode();
-        return true;
-      }
-      return false;
-    }
-    case TOUCH_HUD_PROJECTION_TOGGLE: {
-      bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
-      Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
-      return true;
-    }
+    case TOUCH_HUD_CLEAR:
+      return HandleTouchHudClear();
+    case TOUCH_HUD_MODE_CHANGE:
+      return HandleTouchHudModeChange();
+    case TOUCH_HUD_PROJECTION_TOGGLE:
+      return HandleTouchHudProjectToggle();
     case DISABLE_GPU_WATCHDOG:
       content::GpuDataManager::GetInstance()->DisableGpuWatchdog();
       return true;
-#endif
+#endif  // OS_CHROMEOS
     case OPEN_FEEDBACK_PAGE:
-      ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage();
-      return true;
+      return HandleOpenFeedbackPage();
     case EXIT:
       // UMA metrics are recorded in the handler.
       exit_warning_handler_.HandleAccelerator();
       return true;
-    case NEW_INCOGNITO_WINDOW: {
-        bool incognito_allowed =
-            Shell::GetInstance()->delegate()->IsIncognitoAllowed();
-        if (incognito_allowed)
-          Shell::GetInstance()->new_window_delegate()->NewWindow(
-              true /* is_incognito */);
-        return incognito_allowed;
-    }
+    case NEW_INCOGNITO_WINDOW:
+      return HandleNewIncognitoWindow();
     case NEW_TAB:
-      if (key_code == ui::VKEY_T)
-        content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
-      Shell::GetInstance()->new_window_delegate()->NewTab();
-      return true;
+      return HandleNewTab(key_code);
     case NEW_WINDOW:
-      Shell::GetInstance()->new_window_delegate()->NewWindow(
-          false /* is_incognito */);
-      return true;
+      return HandleNewWindow();
     case RESTORE_TAB:
-      Shell::GetInstance()->new_window_delegate()->RestoreTab();
-      return true;
+      return HandleRestoreTab();
     case TAKE_SCREENSHOT:
-      if (screenshot_delegate_.get() &&
-          screenshot_delegate_->CanTakeScreenshot()) {
-        screenshot_delegate_->HandleTakeScreenshotForAllRootWindows();
-      }
-      // Return true to prevent propagation of the key event.
-      return true;
+      return HandleTakeScreenshot(screenshot_delegate_.get());
     case TAKE_PARTIAL_SCREENSHOT:
-      if (screenshot_delegate_) {
-        ash::PartialScreenshotView::StartPartialScreenshot(
-            screenshot_delegate_.get());
-      }
-      // Return true to prevent propagation of the key event because
-      // this key combination is reserved for partial screenshot.
-      return true;
+      return HandleTakePartialScreenshot(screenshot_delegate_.get());
     case TOGGLE_APP_LIST:
-      // If something else was pressed between the Search key (LWIN)
-      // being pressed and released, then ignore the release of the
-      // Search key.
-      if (key_code == ui::VKEY_LWIN &&
-          (previous_event_type == ui::ET_KEY_RELEASED ||
-           previous_key_code != ui::VKEY_LWIN))
-        return false;
-      if (key_code == ui::VKEY_LWIN)
-        content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
-      // When spoken feedback is enabled, we should neither toggle the list nor
-      // consume the key since Search+Shift is one of the shortcuts the a11y
-      // feature uses. crbug.com/132296
-      DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
-      if (Shell::GetInstance()->accessibility_delegate()->
-          IsSpokenFeedbackEnabled())
-        return false;
-      ash::Shell::GetInstance()->ToggleAppList(NULL);
-      return true;
+      return HandleToggleAppList(
+          key_code, previous_event_type, previous_key_code, accelerator);
     case DISABLE_CAPS_LOCK:
-      if (previous_event_type == ui::ET_KEY_RELEASED ||
-          (previous_key_code != ui::VKEY_LSHIFT &&
-           previous_key_code != ui::VKEY_SHIFT &&
-           previous_key_code != ui::VKEY_RSHIFT)) {
-        // If something else was pressed between the Shift key being pressed
-        // and released, then ignore the release of the Shift key.
-        return false;
-      }
-      if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
-        shell->caps_lock_delegate()->SetCapsLockEnabled(false);
-        return true;
-      }
-      return false;
+      return HandleDisableCapsLock(
+          key_code, previous_event_type, previous_key_code);
     case TOGGLE_CAPS_LOCK:
-      if (key_code == ui::VKEY_LWIN) {
-        // If something else was pressed between the Search key (LWIN)
-        // being pressed and released, then ignore the release of the
-        // Search key.
-        // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
-        if (previous_event_type == ui::ET_KEY_RELEASED ||
-            previous_key_code != ui::VKEY_LWIN)
-          return false;
-      }
-      shell->caps_lock_delegate()->ToggleCapsLock();
-      return true;
+      return HandleToggleCapsLock(
+          key_code, previous_event_type, previous_key_code);
     case BRIGHTNESS_DOWN:
       if (brightness_control_delegate_)
         return brightness_control_delegate_->HandleBrightnessDown(accelerator);
@@ -749,145 +1032,61 @@
       return volume_delegate && volume_delegate->HandleVolumeUp(accelerator);
     }
     case FOCUS_LAUNCHER:
-      return shell->focus_cycler()->FocusWidget(
-          Launcher::ForPrimaryDisplay()->shelf_widget());
+      return HandleFocusLauncher();
     case FOCUS_NEXT_PANE:
       return HandleRotatePaneFocus(Shell::FORWARD);
     case FOCUS_PREVIOUS_PANE:
       return HandleRotatePaneFocus(Shell::BACKWARD);
     case SHOW_KEYBOARD_OVERLAY:
-      ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay();
-      return true;
+      return HandleShowKeyboardOverlay();
     case SHOW_OAK:
-      if (CommandLine::ForCurrentProcess()->HasSwitch(
-              switches::kAshEnableOak)) {
-        oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow());
-        return true;
-      }
+      return HandleShowOak();
+    case SHOW_SYSTEM_TRAY_BUBBLE:
+      return HandleShowSystemTrayBubble();
+    case SHOW_MESSAGE_CENTER_BUBBLE:
+      HandleShowMessageCenterBubble();
       break;
-    case SHOW_SYSTEM_TRAY_BUBBLE: {
-      internal::RootWindowController* controller =
-          internal::RootWindowController::ForTargetRootWindow();
-      if (!controller->GetSystemTray()->HasSystemBubble()) {
-        controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
-        return true;
-      }
-      break;
-    }
-    case SHOW_MESSAGE_CENTER_BUBBLE: {
-      internal::RootWindowController* controller =
-          internal::RootWindowController::ForTargetRootWindow();
-      internal::StatusAreaWidget* status_area_widget =
-          controller->shelf()->status_area_widget();
-      if (status_area_widget) {
-        WebNotificationTray* notification_tray =
-            status_area_widget->web_notification_tray();
-        if (notification_tray->visible())
-          notification_tray->ShowMessageCenterBubble();
-      }
-      break;
-    }
     case SHOW_TASK_MANAGER:
-      Shell::GetInstance()->new_window_delegate()->ShowTaskManager();
-      return true;
+      return HandleShowTaskManager();
     case NEXT_IME:
-      // This check is necessary e.g. not to process the Shift+Alt+
-      // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/
-      // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
-      // is released.
-      if (previous_event_type == ui::ET_KEY_RELEASED &&
-          // Workaround for crbug.com/139556. CJK IME users tend to press
-          // Enter (or Space) and Shift+Alt almost at the same time to commit
-          // an IME string and then switch from the IME to the English layout.
-          // This workaround allows the user to trigger NEXT_IME even if the
-          // user presses Shift+Alt before releasing Enter.
-          // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
-          previous_key_code != ui::VKEY_RETURN &&
-          previous_key_code != ui::VKEY_SPACE) {
-        // We totally ignore this accelerator.
-        // TODO(mazda): Fix crbug.com/158217
-        return false;
-      }
-      if (ime_control_delegate_)
-        return ime_control_delegate_->HandleNextIme();
-      break;
+      return HandleNextIme(
+          ime_control_delegate_.get(), previous_event_type, previous_key_code);
     case PREVIOUS_IME:
-      if (ime_control_delegate_)
-        return ime_control_delegate_->HandlePreviousIme(accelerator);
-      break;
+      return HandlePreviousIme(ime_control_delegate_.get(), accelerator);
     case PRINT_UI_HIERARCHIES:
       return HandlePrintUIHierarchies();
     case SWITCH_IME:
-      if (ime_control_delegate_)
-        return ime_control_delegate_->HandleSwitchIme(accelerator);
-      break;
+      return HandleSwitchIme(ime_control_delegate_.get(), accelerator);
     case LAUNCH_APP_0:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0);
-      return true;
+      return HandleLaunchAppN(0);
     case LAUNCH_APP_1:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1);
-      return true;
+      return HandleLaunchAppN(1);
     case LAUNCH_APP_2:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2);
-      return true;
+      return HandleLaunchAppN(2);
     case LAUNCH_APP_3:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3);
-      return true;
+      return HandleLaunchAppN(3);
     case LAUNCH_APP_4:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4);
-      return true;
+      return HandleLaunchAppN(4);
     case LAUNCH_APP_5:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5);
-      return true;
+      return HandleLaunchAppN(5);
     case LAUNCH_APP_6:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6);
-      return true;
+      return HandleLaunchAppN(6);
     case LAUNCH_APP_7:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7);
-      return true;
+      return HandleLaunchAppN(7);
     case LAUNCH_LAST_APP:
-      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
-      return true;
+      return HandleLaunchLastApp();
     case WINDOW_SNAP_LEFT:
-    case WINDOW_SNAP_RIGHT: {
-      wm::WindowState* window_state = wm::GetActiveWindowState();
-      // Disable window snapping shortcut key for full screen window due to
-      // http://crbug.com/135487.
-      if (!window_state ||
-          window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL ||
-          window_state->IsFullscreen()) {
-        break;
-      }
-
-      internal::SnapSizer::SnapWindow(window_state,
-          action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
-                                       internal::SnapSizer::RIGHT_EDGE);
-      return true;
-    }
+    case WINDOW_SNAP_RIGHT:
+      return HandleWindowSnap(action);
     case WINDOW_MINIMIZE:
-      return accelerators::ToggleMinimized();
-    case TOGGLE_FULLSCREEN: {
-      if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
-        content::RecordAction(
-            content::UserMetricsAction("Accel_Fullscreen_F4"));
-      }
-      accelerators::ToggleFullscreen();
-      return true;
-    }
-    case TOGGLE_MAXIMIZED: {
+      return HandleWindowMinimize();
+    case TOGGLE_FULLSCREEN:
+      return HandleToggleFullscreen(key_code);
+    case TOGGLE_MAXIMIZED:
       accelerators::ToggleMaximized();
       return true;
-    }
-    case WINDOW_POSITION_CENTER: {
-      content::RecordAction(content::UserMetricsAction("Accel_Center"));
-      aura::Window* window = wm::GetActiveWindow();
-      // Docked windows do not support centering and ignore accelerator.
-      if (window && !wm::GetWindowState(window)->IsDocked()) {
-        wm::CenterWindow(window);
-        return true;
-      }
-      break;
-    }
+    case WINDOW_POSITION_CENTER:
+     return HandlePositionCenter();
     case SCALE_UI_UP:
       return HandleScaleUI(true /* up */);
     case SCALE_UI_DOWN:
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index ce3476b..8569171 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -505,7 +505,7 @@
     EXPECT_EQ(window->bounds().ToString(), snap_right.ToString());
   }
   {
-    gfx::Rect normal_bounds = window->bounds();
+    gfx::Rect normal_bounds = window_state->GetRestoreBoundsInParent();
 
     GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy);
     EXPECT_TRUE(window_state->IsMaximized());
@@ -513,6 +513,8 @@
 
     GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy);
     EXPECT_FALSE(window_state->IsMaximized());
+    // Window gets restored to its restore bounds since side-maximized state
+    // is treated as a "maximized" state.
     EXPECT_EQ(normal_bounds.ToString(), window->bounds().ToString());
 
     GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy);
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 4ffc1ab..cd3a580 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -81,7 +81,9 @@
   { true, ui::VKEY_Z, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
     TOGGLE_SPOKEN_FEEDBACK },
   { true, ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN, SILENCE_SPOKEN_FEEDBACK},
-  { true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
+  { true, ui::VKEY_OEM_COMMA, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
+    SWITCH_TO_PREVIOUS_USER },
+  { true, ui::VKEY_OEM_PERIOD, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
     SWITCH_TO_NEXT_USER },
 #endif  // defined(OS_CHROMEOS)
   { true, ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FEEDBACK_PAGE },
diff --git a/ash/accelerators/accelerator_table.h b/ash/accelerators/accelerator_table.h
index 21bbf6a..d120960 100644
--- a/ash/accelerators/accelerator_table.h
+++ b/ash/accelerators/accelerator_table.h
@@ -135,6 +135,7 @@
   OPEN_CROSH,
   OPEN_FILE_MANAGER,
   SWITCH_TO_NEXT_USER,
+  SWITCH_TO_PREVIOUS_USER,
 #endif
 };
 
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 091465e..089ea15 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -156,10 +156,6 @@
         'keyboard_uma_event_filter.h',
         'launcher/launcher.cc',
         'launcher/launcher.h',
-        'launcher/launcher_delegate.h',
-        'launcher/launcher_item_delegate_manager.cc',
-        'launcher/launcher_item_delegate_manager.h',
-        'launcher/launcher_item_delegate.h',
         'launcher/launcher_types.cc',
         'launcher/launcher_types.h',
         'magnifier/magnification_controller.cc',
@@ -210,10 +206,15 @@
         'shelf/shelf_button.cc',
         'shelf/shelf_button.h',
         'shelf/shelf_button_host.h',
+        'shelf/shelf_delegate.h',
         'shelf/shelf_icon_observer.h',
+        'shelf/shelf_item_delegate.h',
+        'shelf/shelf_item_delegate_manager.cc',
+        'shelf/shelf_item_delegate_manager.h',
         'shelf/shelf_layout_manager.cc',
         'shelf/shelf_layout_manager.h',
         'shelf/shelf_layout_manager_observer.h',
+        'shelf/shelf_menu_model.h',
         'shelf/shelf_model.cc',
         'shelf/shelf_model.h',
         'shelf/shelf_model_observer.h',
@@ -228,6 +229,8 @@
         'shelf/shelf_view.h',
         'shelf/shelf_widget.cc',
         'shelf/shelf_widget.h',
+        'shelf/shelf_window_watcher.cc',
+        'shelf/shelf_window_watcher.h',
         'shell.cc',
         'shell.h',
         'shell_delegate.h',
@@ -422,8 +425,14 @@
         'wm/base_layout_manager.h',
         'wm/boot_splash_screen_chromeos.cc',
         'wm/boot_splash_screen_chromeos.h',
+        'wm/caption_buttons/alternate_frame_size_button.cc',
+        'wm/caption_buttons/alternate_frame_size_button.h',
+        'wm/caption_buttons/alternate_frame_size_button_delegate.h',
         'wm/caption_buttons/bubble_contents_button_row.cc',
         'wm/caption_buttons/bubble_contents_button_row.h',
+        'wm/caption_buttons/caption_button_types.h',
+        'wm/caption_buttons/frame_caption_button.cc',
+        'wm/caption_buttons/frame_caption_button.h',
         'wm/caption_buttons/frame_caption_button_container_view.cc',
         'wm/caption_buttons/frame_caption_button_container_view.h',
         'wm/caption_buttons/frame_maximize_button.cc',
@@ -433,7 +442,6 @@
         'wm/caption_buttons/maximize_bubble_controller.h',
         'wm/caption_buttons/maximize_bubble_controller_bubble.cc',
         'wm/caption_buttons/maximize_bubble_controller_bubble.h',
-        'wm/caption_buttons/maximize_bubble_frame_state.h',
         'wm/coordinate_conversion.cc',
         'wm/coordinate_conversion.h',
         'wm/custom_frame_view_ash.cc',
@@ -568,10 +576,6 @@
         'wm/wm_types.h',
         'wm/workspace_controller.cc',
         'wm/workspace_controller.h',
-        'wm/workspace/colored_window_controller.cc',
-        'wm/workspace/colored_window_controller.h',
-        'wm/workspace/desktop_background_fade_controller.cc',
-        'wm/workspace/desktop_background_fade_controller.h',
         'wm/workspace/magnetism_matcher.cc',
         'wm/workspace/magnetism_matcher.h',
         'wm/workspace/multi_window_resize_controller.cc',
@@ -651,32 +655,30 @@
         'test/ash_test_helper.h',
         'test/cursor_manager_test_api.cc',
         'test/cursor_manager_test_api.h',
-        'test/launcher_test_api.cc',
-        'test/launcher_test_api.h',
-        'test/shelf_view_test_api.cc',
-        'test/shelf_view_test_api.h',
         'test/display_manager_test_api.cc',
         'test/display_manager_test_api.h',
-        'test/launcher_item_delegate_manager_test_api.cc',
-        'test/launcher_item_delegate_manager_test_api.h',
+        'test/launcher_test_api.cc',
+        'test/launcher_test_api.h',
         'test/mirror_window_test_api.cc',
         'test/mirror_window_test_api.h',
         'test/overflow_bubble_view_test_api.cc',
         'test/overflow_bubble_view_test_api.h',
+        'test/shelf_item_delegate_manager_test_api.cc',
+        'test/shelf_item_delegate_manager_test_api.h',
+        'test/shelf_view_test_api.cc',
+        'test/shelf_view_test_api.h',
         'test/shell_test_api.cc',
         'test/shell_test_api.h',
         'test/test_activation_delegate.cc',
         'test/test_activation_delegate.h',
-        'test/test_user_wallpaper_delegate.cc',
-        'test/test_user_wallpaper_delegate.h',
-        'test/test_launcher_delegate.cc',
-        'test/test_launcher_delegate.h',
-        'test/test_launcher_item_delegate.cc',
-        'test/test_launcher_item_delegate.h',
         'test/test_screenshot_delegate.cc',
         'test/test_screenshot_delegate.cc',
         'test/test_session_state_delegate.cc',
         'test/test_session_state_delegate.h',
+        'test/test_shelf_delegate.cc',
+        'test/test_shelf_delegate.h',
+        'test/test_shelf_item_delegate.cc',
+        'test/test_shelf_item_delegate.h',
         'test/test_shell_delegate.cc',
         'test/test_shell_delegate.h',
         'test/test_suite.cc',
@@ -685,6 +687,8 @@
         'test/test_suite_init.mm',
         'test/test_system_tray_delegate.cc',
         'test/test_system_tray_delegate.h',
+        'test/test_user_wallpaper_delegate.cc',
+        'test/test_user_wallpaper_delegate.h',
         'test/ui_controls_factory_ash.cc',
         'test/ui_controls_factory_ash.h',
       ],
@@ -786,15 +790,16 @@
         'shelf/shelf_tooltip_manager_unittest.cc',
         'shelf/shelf_view_unittest.cc',
         'shelf/shelf_widget_unittest.cc',
+        'shelf/shelf_window_watcher_unittest.cc',
         'shell/app_list.cc',
         'shell/bubble.cc',
         'shell/context_menu.cc',
         'shell/context_menu.h',
-        'shell/launcher_delegate_impl.cc',
-        'shell/launcher_delegate_impl.h',
         'shell/lock_view.cc',
         'shell/panel_window.cc',
         'shell/panel_window.h',
+        'shell/shelf_delegate_impl.cc',
+        'shell/shelf_delegate_impl.h',
         'shell/shell_delegate_impl.cc',
         'shell/shell_delegate_impl.h',
         'shell/widgets.cc',
@@ -802,8 +807,8 @@
         'shell/window_type_launcher.h',
         'shell/window_watcher.cc',
         'shell/window_watcher.h',
-        'shell/window_watcher_launcher_item_delegate.cc',
-        'shell/window_watcher_launcher_item_delegate.h',
+        'shell/window_watcher_shelf_item_delegate.cc',
+        'shell/window_watcher_shelf_item_delegate.h',
         'shell/window_watcher_unittest.cc',
         'shell_unittest.cc',
         'system/chromeos/managed/tray_locally_managed_user_unittest.cc',
@@ -815,6 +820,7 @@
         'system/chromeos/tray_display_unittest.cc',
         'system/date/date_view_unittest.cc',
         'system/tray/system_tray_unittest.cc',
+        'system/tray/tray_details_view_unittest.cc',
         'system/user/tray_user_unittest.cc',
         'system/web_notification/web_notification_tray_unittest.cc',
         'test/ash_test_helper_unittest.cc',
@@ -824,6 +830,7 @@
         'wm/app_list_controller_unittest.cc',
         'wm/ash_native_cursor_manager_unittest.cc',
         'wm/base_layout_manager_unittest.cc',
+        'wm/caption_buttons/alternate_frame_size_button_unittest.cc',
         'wm/caption_buttons/frame_caption_button_container_view_unittest.cc',
         'wm/caption_buttons/frame_maximize_button_unittest.cc',
         'wm/dock/docked_window_layout_manager_unittest.cc',
@@ -958,11 +965,11 @@
         'shell/example_factory.h',
         'shell/keyboard_controller_proxy_stub.cc',
         'shell/keyboard_controller_proxy_stub.h',
-        'shell/launcher_delegate_impl.cc',
-        'shell/launcher_delegate_impl.h',
         'shell/lock_view.cc',
         'shell/panel_window.cc',
         'shell/panel_window.h',
+        'shell/shelf_delegate_impl.cc',
+        'shell/shelf_delegate_impl.h',
         'shell/shell_delegate_impl.cc',
         'shell/shell_delegate_impl.h',
         'shell/shell_main.cc',
@@ -975,8 +982,8 @@
         'shell/window_type_launcher.h',
         'shell/window_watcher.cc',
         'shell/window_watcher.h',
-        'shell/window_watcher_launcher_item_delegate.cc',
-        'shell/window_watcher_launcher_item_delegate.h',
+        'shell/window_watcher_shelf_item_delegate.cc',
+        'shell/window_watcher_shelf_item_delegate.h',
         '../content/app/startup_helper_win.cc',
         '../ui/views/test/test_views_delegate.cc',
       ],
diff --git a/ash/ash_constants.cc b/ash/ash_constants.cc
index a1404d0..82501f2 100644
--- a/ash/ash_constants.cc
+++ b/ash/ash_constants.cc
@@ -9,8 +9,6 @@
 
 namespace ash {
 
-DEFINE_WINDOW_PROPERTY_KEY(bool, kConstrainedWindowKey, false);
-
 const int kResizeAreaCornerSize = 16;
 const int kResizeOutsideBoundsSize = 6;
 const int kResizeOutsideBoundsScaleForTouch = 5;
diff --git a/ash/ash_constants.h b/ash/ash_constants.h
index 8e88985..cad4354 100644
--- a/ash/ash_constants.h
+++ b/ash/ash_constants.h
@@ -13,11 +13,6 @@
 
 namespace ash {
 
-// The window is a constrained window and lives therefore entirely within
-// another aura window.
-ASH_EXPORT extern const aura::WindowProperty<bool>* const
-    kConstrainedWindowKey;
-
 // In the window corners, the resize areas don't actually expand bigger, but the
 // 16 px at the end of each edge triggers diagonal resizing.
 ASH_EXPORT extern const int kResizeAreaCornerSize;
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 88f569d..29463d0 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -83,9 +83,7 @@
 // Use alternate visual style for the caption buttons (minimize, maximize,
 // restore, close). The alternate style:
 // - Adds a dedicated button for minimize.
-// - Increases the height of the maximized header.
 // - Removes the maximize button's help bubble.
-// - Switches snapping a window left/right to be always 50%.
 const char kAshEnableAlternateFrameCaptionButtonStyle[] =
     "ash-enable-alternate-caption-button";
 
@@ -206,8 +204,11 @@
     "ash-disable-drag-and-drop-applist-to-launcher";
 
 bool UseAlternateFrameCaptionButtonStyle() {
-  return CommandLine::ForCurrentProcess()->
-      HasSwitch(kAshEnableAlternateFrameCaptionButtonStyle);
+  // For the sake of simplicity, the alternate caption button style is only
+  // used if snapped windows are always 50% of the screen's width.
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  return command_line->HasSwitch(kAshEnableAlternateFrameCaptionButtonStyle) &&
+      !command_line->HasSwitch(kAshMultipleSnapWindowWidths);
 }
 
 bool UseAlternateShelfLayout() {
diff --git a/ash/display/display_error_observer_chromeos.cc b/ash/display/display_error_observer_chromeos.cc
index 3b619ef..34e1bc2 100644
--- a/ash/display/display_error_observer_chromeos.cc
+++ b/ash/display/display_error_observer_chromeos.cc
@@ -49,7 +49,9 @@
       base::string16(),  // message
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16(),  // display_source
-      message_center::NotifierId(system_notifier::NOTIFIER_DISPLAY_ERROR),
+      message_center::NotifierId(
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierDisplayError),
       message_center::RichNotificationData(),
       NULL));
   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc
index 3069c83..0914772 100644
--- a/ash/display/resolution_notification_controller.cc
+++ b/ash/display/resolution_notification_controller.cc
@@ -229,7 +229,8 @@
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16() /* display_source */,
       message_center::NotifierId(
-          system_notifier::NOTIFIER_DISPLAY_RESOLUTION_CHANGE),
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierDisplayResolutionChange),
       data,
       new ResolutionChangeNotificationDelegate(
           this, change_info_->timeout_count > 0)));
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 07ab4f3..1d5d636 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -461,14 +461,10 @@
 }
 
 void DragDropController::OnWindowDestroyed(aura::Window* window) {
-  if (drag_window_ == window) {
-    drag_window_->RemoveObserver(this);
+  if (drag_window_ == window)
     drag_window_ = NULL;
-  }
-  if (drag_source_window_ == window) {
-    drag_source_window_->RemoveObserver(this);
+  if (drag_source_window_ == window)
     drag_source_window_ = NULL;
-  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 0adf72a..58926e8 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -8,11 +8,11 @@
 #include <cmath>
 
 #include "ash/focus_cycler.h"
-#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_ash.h"
+#include "ash/shelf/shelf_delegate.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_navigator.h"
@@ -43,11 +43,11 @@
 const char Launcher::kNativeViewName[] = "ShelfView";
 
 Launcher::Launcher(ShelfModel* shelf_model,
-                   LauncherDelegate* launcher_delegate,
+                   ShelfDelegate* shelf_delegate,
                    ShelfWidget* shelf_widget)
     : shelf_view_(NULL),
       alignment_(shelf_widget->GetAlignment()),
-      delegate_(launcher_delegate),
+      delegate_(shelf_delegate),
       shelf_widget_(shelf_widget) {
   shelf_view_ = new internal::ShelfView(
       shelf_model, delegate_, shelf_widget_->shelf_layout_manager());
@@ -108,10 +108,10 @@
                      ui::EF_NONE,
                      false);
 
-  const ash::LauncherItem& item = shelf_view_->model()->items()[index];
-  ash::LauncherItemDelegate* item_delegate =
-      Shell::GetInstance()->launcher_item_delegate_manager()->
-          GetLauncherItemDelegate(item.id);
+  const LauncherItem& item = shelf_view_->model()->items()[index];
+  ShelfItemDelegate* item_delegate =
+      Shell::GetInstance()->shelf_item_delegate_manager()->GetShelfItemDelegate(
+          item.id);
   item_delegate->ItemSelected(event);
 }
 
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
index 5f1da0b..5c50b8c 100644
--- a/ash/launcher/launcher.h
+++ b/ash/launcher/launcher.h
@@ -41,7 +41,7 @@
 class LauncherTestAPI;
 }
 
-class LauncherDelegate;
+class ShelfDelegate;
 class ShelfIconObserver;
 class ShelfModel;
 class ShelfWidget;
@@ -50,9 +50,7 @@
  public:
   static const char kNativeViewName[];
 
-  Launcher(ShelfModel* shelf_model,
-           LauncherDelegate* launcher_delegate,
-           ShelfWidget* shelf_widget);
+  Launcher(ShelfModel* model, ShelfDelegate* delegate, ShelfWidget* widget);
   virtual ~Launcher();
 
   // Return the launcher for the primary display. NULL if no user is
@@ -122,7 +120,7 @@
 
   ShelfAlignment alignment_;
 
-  LauncherDelegate* delegate_;
+  ShelfDelegate* delegate_;
 
   ShelfWidget* shelf_widget_;
 
diff --git a/ash/launcher/launcher_item_delegate_manager.cc b/ash/launcher/launcher_item_delegate_manager.cc
deleted file mode 100644
index efd8857..0000000
--- a/ash/launcher/launcher_item_delegate_manager.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2013 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.
-
-#include "ash/launcher/launcher_item_delegate_manager.h"
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shell.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-
-namespace ash {
-
-LauncherItemDelegateManager::LauncherItemDelegateManager(ShelfModel* model)
-    : model_(model) {
-  DCHECK(model_);
-  model_->AddObserver(this);
-}
-
-LauncherItemDelegateManager::~LauncherItemDelegateManager() {
-  model_->RemoveObserver(this);
-  STLDeleteContainerPairSecondPointers(id_to_item_delegate_map_.begin(),
-                                       id_to_item_delegate_map_.end());
-}
-
-void LauncherItemDelegateManager::SetLauncherItemDelegate(
-    ash::LauncherID id,
-    scoped_ptr<LauncherItemDelegate> item_delegate) {
-  // If another LauncherItemDelegate is already registered for |id|, we assume
-  // that this request is replacing LauncherItemDelegate for |id| with
-  // |item_delegate|.
-  RemoveLauncherItemDelegate(id);
-  id_to_item_delegate_map_[id] = item_delegate.release();
-}
-
-LauncherItemDelegate* LauncherItemDelegateManager::GetLauncherItemDelegate(
-    ash::LauncherID id) {
-  if (model_->ItemIndexByID(id) != -1) {
-    // Each LauncherItem has to have a LauncherItemDelegate.
-    DCHECK(id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end());
-    return id_to_item_delegate_map_[id];
-  }
-  return NULL;
-}
-
-void LauncherItemDelegateManager::ShelfItemAdded(int index) {
-}
-
-void LauncherItemDelegateManager::ShelfItemRemoved(int index, LauncherID id) {
-  RemoveLauncherItemDelegate(id);
-}
-
-void LauncherItemDelegateManager::ShelfItemMoved(int start_index,
-                                                 int target_index) {
-}
-
-void LauncherItemDelegateManager::ShelfItemChanged(
-    int index,
-    const LauncherItem& old_item) {
-}
-
-void LauncherItemDelegateManager::ShelfStatusChanged() {
-}
-
-void LauncherItemDelegateManager::RemoveLauncherItemDelegate(
-    ash::LauncherID id) {
-  if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()) {
-    delete id_to_item_delegate_map_[id];
-    id_to_item_delegate_map_.erase(id);
-  }
-}
-
-}  // namespace ash
diff --git a/ash/launcher/launcher_item_delegate_manager.h b/ash/launcher/launcher_item_delegate_manager.h
deleted file mode 100644
index 1768cd9..0000000
--- a/ash/launcher/launcher_item_delegate_manager.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2013 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 ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
-#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
-
-#include <map>
-
-#include "ash/ash_export.h"
-#include "ash/launcher/launcher_types.h"
-#include "ash/shelf/shelf_model_observer.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace ash {
-class LauncherItemDelegate;
-class ShelfModel;
-
-namespace test {
-class LauncherItemDelegateManagerTestAPI;
-}
-
-// LauncherItemDelegateManager manages the set of LauncherItemDelegates for the
-// launcher. LauncherItemDelegateManager does not create LauncherItemDelegates,
-// rather it is expected that someone else invokes SetLauncherItemDelegate
-// appropriately. On the other hand, LauncherItemDelegateManager destroys
-// LauncherItemDelegates when the corresponding item from the model is removed.
-class ASH_EXPORT LauncherItemDelegateManager : public ShelfModelObserver {
- public:
-  explicit LauncherItemDelegateManager(ShelfModel* model);
-  virtual ~LauncherItemDelegateManager();
-
-  // Set |item_delegate| for |id| and take an ownership.
-  void SetLauncherItemDelegate(
-      ash::LauncherID id,
-      scoped_ptr<ash::LauncherItemDelegate> item_delegate);
-
-  // Returns LauncherItemDelegate for |item_type|. Always returns non-NULL.
-  LauncherItemDelegate* GetLauncherItemDelegate(ash::LauncherID id);
-
-  // ShelfModelObserver overrides:
-  virtual void ShelfItemAdded(int model_index) OVERRIDE;
-  virtual void ShelfItemRemoved(int index, ash::LauncherID id) OVERRIDE;
-  virtual void ShelfItemMoved(int start_index, int targetindex) OVERRIDE;
-  virtual void ShelfItemChanged(int index,
-                                const LauncherItem& old_item) OVERRIDE;
-  virtual void ShelfStatusChanged() OVERRIDE;
-
- private:
-  friend class ash::test::LauncherItemDelegateManagerTestAPI;
-
-  typedef std::map<ash::LauncherID, LauncherItemDelegate*>
-      LauncherIDToItemDelegateMap;
-
-  // Remove and destroy LauncherItemDelegate for |id|.
-  void RemoveLauncherItemDelegate(ash::LauncherID id);
-
-  // Owned by Shell.
-  ShelfModel* model_;
-
-  LauncherIDToItemDelegateMap id_to_item_delegate_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManager);
-};
-
-}  // namespace ash
-
-#endif  // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
diff --git a/ash/launcher/launcher_types.cc b/ash/launcher/launcher_types.cc
index 46fc827..67dc572 100644
--- a/ash/launcher/launcher_types.cc
+++ b/ash/launcher/launcher_types.cc
@@ -8,14 +8,25 @@
 
 const int kLauncherPreferredSize = 48;
 const int kLauncherBackgroundAlpha = 204;
+const int kInvalidImageResourceID = -1;
+const int kInvalidLauncherID = 0;
+const int kTimeToSwitchBackgroundMs = 1000;
 
 LauncherItem::LauncherItem()
     : type(TYPE_UNDEFINED),
-      id(0),
+      id(kInvalidLauncherID),
       status(STATUS_CLOSED) {
 }
 
 LauncherItem::~LauncherItem() {
 }
 
+LauncherItemDetails::LauncherItemDetails()
+    : type(TYPE_UNDEFINED),
+      image_resource_id(kInvalidImageResourceID) {
+}
+
+LauncherItemDetails::~LauncherItemDetails() {
+}
+
 }  // namespace ash
diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h
index c600629..bbb1bbc 100644
--- a/ash/launcher/launcher_types.h
+++ b/ash/launcher/launcher_types.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "ash/ash_export.h"
+#include "base/strings/string16.h"
 #include "ui/gfx/image/image_skia.h"
 
 namespace ash {
@@ -21,6 +22,14 @@
 // Max alpha of the launcher background.
 ASH_EXPORT extern const int kLauncherBackgroundAlpha;
 
+// Invalid image resource id used for LauncherItemDetails.
+extern const int kInvalidImageResourceID;
+
+extern const int kInvalidLauncherID;
+
+// Animation duration for switching black shelf and dock background on and off.
+ASH_EXPORT extern const int kTimeToSwitchBackgroundMs;
+
 // Type the LauncherItem represents.
 enum LauncherItemType {
   // Represents a running app panel.
@@ -81,6 +90,22 @@
   CYCLE_BACKWARD
 };
 
+// LauncherItemDetails may be set on Window (by way of
+// SetLauncherItemDetailsForWindow) to make the window appear in the shelf. See
+// ShelfWindowWatcher for details.
+struct ASH_EXPORT LauncherItemDetails {
+  LauncherItemDetails();
+  ~LauncherItemDetails();
+
+  LauncherItemType type;
+
+  // Resource id of the image to display on the shelf.
+  int image_resource_id;
+
+  // Title of the item.
+  base::string16 title;
+};
+
 }  // namespace ash
 
 #endif  // ASH_LAUNCHER_LAUNCHER_TYPES_H_
diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc
index 6359508..24eca33 100644
--- a/ash/launcher/launcher_unittest.cc
+++ b/ash/launcher/launcher_unittest.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/shelf/shelf_button.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_widget.h"
@@ -12,7 +12,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/launcher_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
-#include "ash/test/test_launcher_item_delegate.h"
+#include "ash/test/test_shelf_item_delegate.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/root_window.h"
 #include "ui/gfx/display.h"
@@ -51,7 +51,7 @@
     shelf_view_ = test.shelf_view();
     shelf_model_ = shelf_view_->model();
     item_delegate_manager_ =
-        Shell::GetInstance()->launcher_item_delegate_manager();
+        Shell::GetInstance()->shelf_item_delegate_manager();
 
     test_.reset(new ash::test::ShelfViewTestAPI(shelf_view_));
   }
@@ -72,7 +72,7 @@
     return shelf_model_;
   }
 
-  LauncherItemDelegateManager* item_manager() {
+  ShelfItemDelegateManager* item_manager() {
     return item_delegate_manager_;
   }
 
@@ -84,8 +84,8 @@
   Launcher* launcher_;
   ShelfView* shelf_view_;
   ShelfModel* shelf_model_;
-  LauncherItemDelegateManager* item_delegate_manager_;
-  scoped_ptr<ash::test::ShelfViewTestAPI> test_;
+  ShelfItemDelegateManager* item_delegate_manager_;
+  scoped_ptr<test::ShelfViewTestAPI> test_;
 
   DISALLOW_COPY_AND_ASSIGN(LauncherTest);
 };
@@ -121,10 +121,10 @@
   item.status = STATUS_RUNNING;
   int index = shelf_model()->Add(item);
 
-  scoped_ptr<LauncherItemDelegate> delegate(
-      new ash::test::TestLauncherItemDelegate(NULL));
-  item_manager()->SetLauncherItemDelegate(shelf_model()->items()[index].id,
-                                          delegate.Pass());
+  scoped_ptr<ShelfItemDelegate> delegate(
+      new test::TestShelfItemDelegate(NULL));
+  item_manager()->SetShelfItemDelegate(shelf_model()->items()[index].id,
+                                       delegate.Pass());
 
   ASSERT_EQ(++button_count, test_api()->GetButtonCount());
   ShelfButton* button = test_api()->GetButton(index);
diff --git a/ash/multi_profile_uma.cc b/ash/multi_profile_uma.cc
index f6d9840..cbf3b05 100644
--- a/ash/multi_profile_uma.cc
+++ b/ash/multi_profile_uma.cc
@@ -30,10 +30,28 @@
 }
 
 // static
+void MultiProfileUMA::RecordTeleportWindowType(TeleportWindowType window_type) {
+  UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindowType",
+                            window_type,
+                            NUM_TELEPORT_WINDOW_TYPES);
+}
+
+// static
 void MultiProfileUMA::RecordTeleportAction(TeleportWindowAction action) {
   UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindow",
                             action,
                             NUM_TELEPORT_WINDOW_ACTIONS);
 }
 
+// static
+void MultiProfileUMA::RecordUserCount(int number_of_users) {
+  UMA_HISTOGRAM_COUNTS_100("MultiProfile.UsersPerSession", number_of_users);
+}
+
+// static
+void MultiProfileUMA::RecordDiscardedTab(int number_of_users) {
+  UMA_HISTOGRAM_COUNTS_100("MultiProfile.DiscardedTabsPerUser",
+      number_of_users);
+}
+
 }  // namespace ash
diff --git a/ash/multi_profile_uma.h b/ash/multi_profile_uma.h
index 1c70482..937b4d8 100644
--- a/ash/multi_profile_uma.h
+++ b/ash/multi_profile_uma.h
@@ -35,6 +35,17 @@
     NUM_SWITCH_ACTIVE_USER_ACTIONS
   };
 
+  enum TeleportWindowType {
+    TELEPORT_WINDOW_BROWSER = 0,
+    TELEPORT_WINDOW_INCOGNITO_BROWSER,
+    TELEPORT_WINDOW_V1_APP,
+    TELEPORT_WINDOW_V2_APP,
+    TELEPORT_WINDOW_PANEL,
+    TELEPORT_WINDOW_POPUP,
+    TELEPORT_WINDOW_UNKNOWN,
+    NUM_TELEPORT_WINDOW_TYPES
+  };
+
   enum TeleportWindowAction {
     TELEPORT_WINDOW_DRAG_AND_DROP = 0,
     TELEPORT_WINDOW_CAPTION_MENU,
@@ -52,9 +63,18 @@
   // Record switching the active user and what UI path was taken.
   static void RecordSwitchActiveUser(SwitchActiveUserAction action);
 
+  // Record the type of window which got teleported to another desk.
+  static void RecordTeleportWindowType(TeleportWindowType window_type);
+
   // Record the way and how many times a window got teleported to another desk.
   static void RecordTeleportAction(TeleportWindowAction action);
 
+  // Record number of users joined into a session. Called upon logout.
+  static void RecordUserCount(int number_of_users);
+
+  // Record a discarded tab in the number of running users bucket.
+  static void RecordDiscardedTab(int number_of_users);
+
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(MultiProfileUMA);
 };
diff --git a/ash/popup_message.h b/ash/popup_message.h
index 040b9fe..72a0cf5 100644
--- a/ash/popup_message.h
+++ b/ash/popup_message.h
@@ -13,6 +13,7 @@
 
 namespace views {
 class BubbleDelegateView;
+class Widget;
 }
 
 namespace ash {
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 5447785..6fec31b 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -20,6 +20,7 @@
            BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
            SAME CONDITIONALS. -->
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BACKGROUND" file="common/launcher/launcher_background.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_CORNER" file="common/launcher/launcher_corner.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_DIMMING" file="common/launcher/launcher_dimming.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST" file="common/launcher/launcher_appmenu.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE" file="common/alt_launcher/status_app_menu_icon.png" />
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_corner.png b/ash/resources/default_100_percent/common/launcher/launcher_corner.png
new file mode 100644
index 0000000..d730065
--- /dev/null
+++ b/ash/resources/default_100_percent/common/launcher/launcher_corner.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_corner.png b/ash/resources/default_200_percent/common/launcher/launcher_corner.png
new file mode 100644
index 0000000..e5f8c96
--- /dev/null
+++ b/ash/resources/default_200_percent/common/launcher/launcher_corner.png
Binary files differ
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 45c0f7a..4775acc 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -518,23 +518,27 @@
   shelf_->shelf_layout_manager()->UpdateVisibilityState();
 }
 
-const aura::Window* RootWindowController::GetTopmostFullscreenWindow() const {
+const aura::Window* RootWindowController::GetWindowForFullscreenMode() const {
   const aura::Window::Windows& windows =
       GetContainer(kShellWindowId_DefaultContainer)->children();
+  const aura::Window* topmost_window = NULL;
   for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
        iter != windows.rend(); ++iter) {
-    if (wm::GetWindowState(*iter)->IsFullscreen())
-      return *iter;
+    if (((*iter)->type() == aura::client::WINDOW_TYPE_NORMAL ||
+         (*iter)->type() == aura::client::WINDOW_TYPE_PANEL) &&
+        (*iter)->layer()->GetTargetVisibility()) {
+      topmost_window = *iter;
+      break;
+    }
+  }
+  while (topmost_window) {
+    if (wm::GetWindowState(topmost_window)->IsFullscreen())
+      return topmost_window;
+    topmost_window = topmost_window->transient_parent();
   }
   return NULL;
 }
 
-aura::Window* RootWindowController::GetTopmostFullscreenWindow() {
-  return const_cast<aura::Window*>(
-      const_cast<const RootWindowController*>(this)->
-          GetTopmostFullscreenWindow());
-}
-
 void RootWindowController::ActivateKeyboard(
     keyboard::KeyboardController* keyboard_controller) {
   if (!keyboard::IsKeyboardEnabled() ||
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h
index 076bfe6..6e2d545 100644
--- a/ash/root_window_controller.h
+++ b/ash/root_window_controller.h
@@ -221,10 +221,9 @@
   // Initialize touch HUDs if necessary.
   void InitTouchHuds();
 
-  // Returns the window, if any, which is in fullscreen mode. If multiple
-  // windows are in fullscreen state, the topmost one is preferred.
-  const aura::Window* GetTopmostFullscreenWindow() const;
-  aura::Window* GetTopmostFullscreenWindow();
+  // Returns the topmost window or one of its transient parents, if any of them
+  // are in fullscreen mode.
+  const aura::Window* GetWindowForFullscreenMode() const;
 
   // Activate virtual keyboard on current root window controller.
   void ActivateKeyboard(keyboard::KeyboardController* keyboard_controller);
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index d547a8a..2ff7362 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -454,7 +454,7 @@
   }
 }
 
-TEST_F(RootWindowControllerTest, GetTopmostFullscreenWindow) {
+TEST_F(RootWindowControllerTest, GetWindowForFullscreenMode) {
   UpdateDisplay("600x600");
   internal::RootWindowController* controller =
       Shell::GetInstance()->GetPrimaryRootWindowController();
@@ -467,39 +467,23 @@
   Widget* w3 = Widget::CreateWindowWithParentAndBounds(NULL,
       w2->GetNativeWindow(), gfx::Rect(0, 0, 100, 100));
 
-  // Test that GetTopmostFullscreenWindow() finds the fullscreen window when one
+  // Test that GetWindowForFullscreenMode() finds the fullscreen window when one
   // of its transient children is active.
   w3->Activate();
-  EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
+  EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode());
 
-  // Since there's only one desktop workspace, it always returns the same
-  // fullscreen window.
+  // If the topmost window is not fullscreen, it returns NULL.
   w1->Activate();
-  EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
-}
+  EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode());
+  w1->Close();
+  w3->Close();
 
-TEST_F(RootWindowControllerTest, MultipleFullscreenWindows) {
-  UpdateDisplay("600x600");
-  internal::RootWindowController* controller =
-      Shell::GetInstance()->GetPrimaryRootWindowController();
-
-  Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
-  w1->Maximize();
-  Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
-  w2->SetFullscreen(true);
-  Widget* w3 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
-  w3->SetFullscreen(true);
-
-  // Test that GetTopmostFullscreenWindow() finds the active fullscreen window.
+  // Only w2 remains, if minimized GetWindowForFullscreenMode should return
+  // NULL.
   w2->Activate();
-  EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
-  w3->Activate();
-  EXPECT_EQ(w3->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
-
-  // If the active window is not fullscreen, it still returns the topmost
-  // fullscreen window, which is the last active one.
-  w1->Activate();
-  EXPECT_EQ(w3->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
+  EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode());
+  w2->Minimize();
+  EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode());
 }
 
 // Test that user session window can't be focused if user session blocked by
diff --git a/ash/scoped_target_root_window.h b/ash/scoped_target_root_window.h
index 0e7a8b1..c0ab7ef 100644
--- a/ash/scoped_target_root_window.h
+++ b/ash/scoped_target_root_window.h
@@ -4,6 +4,7 @@
 #ifndef ASH_SCOPED_TARGET_ROOT_WINDOW_H_
 #define ASH_SCOPED_TARGET_ROOT_WINDOW_H_
 
+#include "ash/ash_export.h"
 #include "base/basictypes.h"
 
 namespace aura {
@@ -18,7 +19,7 @@
 // in the same window where a user interaction happened.
 // An example usage is to specify the target root window when creating
 // a new window using launcher's icon.
-class ScopedTargetRootWindow {
+class ASH_EXPORT ScopedTargetRootWindow {
  public:
   explicit ScopedTargetRootWindow(aura::Window* root_window);
   ~ScopedTargetRootWindow();
diff --git a/ash/session_state_delegate.h b/ash/session_state_delegate.h
index 47570ff..91c6229 100644
--- a/ash/session_state_delegate.h
+++ b/ash/session_state_delegate.h
@@ -33,6 +33,12 @@
 // Delegate for checking and modifying the session state.
 class ASH_EXPORT SessionStateDelegate {
  public:
+  // Defines the cycle direction for |CycleActiveUser|.
+  enum CycleUser {
+    CYCLE_TO_NEXT_USER = 0,  // Cycle to the next user.
+    CYCLE_TO_PREVIOUS_USER,  // Cycle to the previous user.
+  };
+
   virtual ~SessionStateDelegate() {};
 
   // Returns the maximum possible number of logged in users.
@@ -96,9 +102,9 @@
   // (if that user has already signed in).
   virtual void SwitchActiveUser(const std::string& user_id) = 0;
 
-  // Switches the active user to the next user, with the same ordering as
-  // GetLoggedInUsers.
-  virtual void SwitchActiveUserToNext() = 0;
+  // Switches the active user to the next or previous user, with the same
+  // ordering as GetLoggedInUsers.
+  virtual void CycleActiveUser(CycleUser cycle_user) = 0;
 
   // Adds or removes sessions state observer.
   virtual void AddSessionStateObserver(SessionStateObserver* observer) = 0;
diff --git a/ash/session_state_delegate_stub.cc b/ash/session_state_delegate_stub.cc
index 6daa593..cea4132 100644
--- a/ash/session_state_delegate_stub.cc
+++ b/ash/session_state_delegate_stub.cc
@@ -82,7 +82,7 @@
 void SessionStateDelegateStub::SwitchActiveUser(const std::string& user_id) {
 }
 
-void SessionStateDelegateStub::SwitchActiveUserToNext() {
+void SessionStateDelegateStub::CycleActiveUser(CycleUser cycle_user) {
 }
 
 void SessionStateDelegateStub::AddSessionStateObserver(
diff --git a/ash/session_state_delegate_stub.h b/ash/session_state_delegate_stub.h
index 6fe1eb9..f496e6b 100644
--- a/ash/session_state_delegate_stub.h
+++ b/ash/session_state_delegate_stub.h
@@ -38,7 +38,7 @@
       ash::MultiProfileIndex index) const OVERRIDE;
   virtual void GetLoggedInUsers(UserIdList* users) OVERRIDE;
   virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE;
-  virtual void SwitchActiveUserToNext() OVERRIDE;
+  virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE;
   virtual void AddSessionStateObserver(
       ash::SessionStateObserver* observer) OVERRIDE;
   virtual void RemoveSessionStateObserver(
diff --git a/ash/shelf/alternate_app_list_button.cc b/ash/shelf/alternate_app_list_button.cc
index 4ce2834..bf442d1 100644
--- a/ash/shelf/alternate_app_list_button.cc
+++ b/ash/shelf/alternate_app_list_button.cc
@@ -4,8 +4,10 @@
 
 #include "ash/shelf/alternate_app_list_button.h"
 
+#include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
 #include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_button_host.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
@@ -22,6 +24,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/views/controls/button/image_button.h"
+#include "ui/views/painter.h"
 
 namespace ash {
 namespace internal {
@@ -39,6 +42,8 @@
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
   SetSize(gfx::Size(ShelfLayoutManager::kShelfSize,
                     ShelfLayoutManager::kShelfSize));
+  SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+                      kFocusBorderColor, gfx::Insets(1, 1, 1, 1)));
 }
 
 AlternateAppListButton::~AlternateAppListButton() {
@@ -156,7 +161,7 @@
                        forground_bounds.x(),
                        forground_bounds.y());
 
-  OnPaintFocusBorder(canvas);
+  views::Painter::PaintFocusPainter(this, canvas, focus_painter());
 }
 
 void AlternateAppListButton::GetAccessibleState(
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index a9612bf..697ed77 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -6,6 +6,7 @@
 
 #include <vector>
 
+#include "ash/ash_constants.h"
 #include "ash/launcher/launcher_types.h"
 #include "ash/shelf/shelf_button_host.h"
 #include "grit/ash_resources.h"
@@ -17,6 +18,7 @@
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/views/painter.h"
 
 namespace ash {
 namespace internal {
@@ -43,6 +45,8 @@
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
   SetSize(gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize));
   SetImageAlignment(ImageButton::ALIGN_CENTER, ImageButton::ALIGN_TOP);
+  SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+                      kFocusBorderColor, gfx::Insets(1, 1, 1, 1)));
 }
 
 AppListButton::~AppListButton() {
diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc
index b9fd49d..7013d2b 100644
--- a/ash/shelf/app_list_shelf_item_delegate.cc
+++ b/ash/shelf/app_list_shelf_item_delegate.cc
@@ -19,7 +19,7 @@
 }
 
 AppListShelfItemDelegate::~AppListShelfItemDelegate() {
-  // LauncherItemDelegateManager owns and destroys this class.
+  // ShelfItemDelegateManager owns and destroys this class.
 }
 
 bool AppListShelfItemDelegate::ItemSelected(const ui::Event& event) {
@@ -41,7 +41,7 @@
   return NULL;
 }
 
-LauncherMenuModel* AppListShelfItemDelegate::CreateApplicationMenu(
+ShelfMenuModel* AppListShelfItemDelegate::CreateApplicationMenu(
     int event_flags) {
   // AppList does not show an application menu.
   return NULL;
diff --git a/ash/shelf/app_list_shelf_item_delegate.h b/ash/shelf/app_list_shelf_item_delegate.h
index 750a84b..0396821 100644
--- a/ash/shelf/app_list_shelf_item_delegate.h
+++ b/ash/shelf/app_list_shelf_item_delegate.h
@@ -5,26 +5,25 @@
 #ifndef ASH_SHELF_APP_LIST_SHELF_ITEM_DELEGATE_H_
 #define ASH_SHELF_APP_LIST_SHELF_ITEM_DELEGATE_H_
 
-#include "ash/launcher/launcher_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 
 namespace ash {
 namespace internal {
 
-// LauncherItemDelegate for TYPE_APP_LIST.
-class AppListShelfItemDelegate : public LauncherItemDelegate {
+// ShelfItemDelegate for TYPE_APP_LIST.
+class AppListShelfItemDelegate : public ShelfItemDelegate {
  public:
   AppListShelfItemDelegate();
 
   virtual ~AppListShelfItemDelegate();
 
-  // ash::LauncherItemDelegate overrides:
+  // ShelfItemDelegate:
   virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
   virtual base::string16 GetTitle() OVERRIDE;
-  virtual ui::MenuModel* CreateContextMenu(
-      aura::Window* root_window) OVERRIDE;
-  virtual LauncherMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
+  virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE;
+  virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
   virtual bool IsDraggable() OVERRIDE;
   virtual bool ShouldShowTooltip() OVERRIDE;
 
diff --git a/ash/shelf/background_animator.cc b/ash/shelf/background_animator.cc
index 35e5fba..8137d60 100644
--- a/ash/shelf/background_animator.cc
+++ b/ash/shelf/background_animator.cc
@@ -34,11 +34,12 @@
   animation_.SetSlideDuration(time_in_ms);
 }
 
-void BackgroundAnimator::SetPaintsBackground(bool value, ChangeType type) {
+void BackgroundAnimator::SetPaintsBackground(
+    bool value, BackgroundAnimatorChangeType type) {
   if (paints_background_ == value)
     return;
   paints_background_ = value;
-  if (type == CHANGE_IMMEDIATE && !animation_.is_animating()) {
+  if (type == BACKGROUND_CHANGE_IMMEDIATE && !animation_.is_animating()) {
     animation_.Reset(value ? 1.0f : 0.0f);
     AnimationProgressed(&animation_);
     return;
diff --git a/ash/shelf/background_animator.h b/ash/shelf/background_animator.h
index 7436b30..805f7d5 100644
--- a/ash/shelf/background_animator.h
+++ b/ash/shelf/background_animator.h
@@ -11,6 +11,13 @@
 #include "ui/gfx/animation/slide_animation.h"
 
 namespace ash {
+
+// How the background can be changed.
+enum BackgroundAnimatorChangeType {
+  BACKGROUND_CHANGE_ANIMATE,
+  BACKGROUND_CHANGE_IMMEDIATE
+};
+
 namespace internal {
 
 // Delegate is notified any time the background changes.
@@ -25,12 +32,6 @@
 // BackgroundAnimator is used by the shelf to animate the background (alpha).
 class ASH_EXPORT BackgroundAnimator : public gfx::AnimationDelegate {
  public:
-  // How the background can be changed.
-  enum ChangeType {
-    CHANGE_ANIMATE,
-    CHANGE_IMMEDIATE
-  };
-
   BackgroundAnimator(BackgroundAnimatorDelegate* delegate,
                      int min_alpha,
                      int max_alpha);
@@ -42,7 +43,7 @@
   // Sets whether a background is rendered. Initial value is false. If |type|
   // is |CHANGE_IMMEDIATE| and an animation is not in progress this notifies
   // the delegate immediately (synchronously from this method).
-  void SetPaintsBackground(bool value, ChangeType type);
+  void SetPaintsBackground(bool value, BackgroundAnimatorChangeType type);
   bool paints_background() const { return paints_background_; }
 
   // Current alpha.
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc
index 4b10b8f..93a44e1 100644
--- a/ash/shelf/shelf_button.cc
+++ b/ash/shelf/shelf_button.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
 #include "ash/shelf/shelf_button_host.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -470,6 +471,15 @@
   CustomButton::OnBlur();
 }
 
+void ShelfButton::OnPaint(gfx::Canvas* canvas) {
+  CustomButton::OnPaint(canvas);
+  if (HasFocus()) {
+    gfx::Rect paint_bounds(GetLocalBounds());
+    paint_bounds.Inset(1, 1, 1, 1);
+    canvas->DrawSolidFocusRect(paint_bounds, kFocusBorderColor);
+  }
+}
+
 void ShelfButton::OnGestureEvent(ui::GestureEvent* event) {
   switch (event->type()) {
     case ui::ET_GESTURE_TAP_DOWN:
diff --git a/ash/shelf/shelf_button.h b/ash/shelf/shelf_button.h
index 1bfc510..60158f2 100644
--- a/ash/shelf/shelf_button.h
+++ b/ash/shelf/shelf_button.h
@@ -107,6 +107,7 @@
   virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
   virtual void OnFocus() OVERRIDE;
   virtual void OnBlur() OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
 
   // ui::EventHandler overrides:
   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
diff --git a/ash/launcher/launcher_delegate.h b/ash/shelf/shelf_delegate.h
similarity index 83%
rename from ash/launcher/launcher_delegate.h
rename to ash/shelf/shelf_delegate.h
index 3868bec..e168c47 100644
--- a/ash/launcher/launcher_delegate.h
+++ b/ash/shelf/shelf_delegate.h
@@ -1,25 +1,21 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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 ASH_LAUNCHER_LAUNCHER_DELEGATE_H_
-#define ASH_LAUNCHER_LAUNCHER_DELEGATE_H_
+#ifndef ASH_SHELF_SHELF_DELEGATE_H_
+#define ASH_SHELF_SHELF_DELEGATE_H_
 
 #include "ash/ash_export.h"
 #include "ash/launcher/launcher_types.h"
 
-namespace aura {
-class Window;
-}
-
 namespace ash {
 class Launcher;
 
 // Delegate for the Launcher.
-class ASH_EXPORT LauncherDelegate {
+class ASH_EXPORT ShelfDelegate {
  public:
   // Launcher owns the delegate.
-  virtual ~LauncherDelegate() {}
+  virtual ~ShelfDelegate() {}
 
   // Callback used to allow delegate to perform initialization actions that
   // depend on the Launcher being in a known state.
@@ -53,4 +49,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_LAUNCHER_LAUNCHER_DELEGATE_H_
+#endif  // ASH_SHELF_SHELF_DELEGATE_H_
diff --git a/ash/launcher/launcher_item_delegate.h b/ash/shelf/shelf_item_delegate.h
similarity index 68%
rename from ash/launcher/launcher_item_delegate.h
rename to ash/shelf/shelf_item_delegate.h
index c83de47..72815cf 100644
--- a/ash/launcher/launcher_item_delegate.h
+++ b/ash/shelf/shelf_item_delegate.h
@@ -2,42 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
-#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
+#ifndef ASH_SHELF_SHELF_ITEM_DELEGATE_H_
+#define ASH_SHELF_SHELF_ITEM_DELEGATE_H_
 
 #include "ash/ash_export.h"
-#include "ash/launcher/launcher_types.h"
 #include "base/strings/string16.h"
-#include "ui/base/models/simple_menu_model.h"
 
 namespace aura {
-class RootWindow;
+class Window;
 }
 
 namespace ui {
 class Event;
+class MenuModel;
 }
 
 namespace ash {
 
-// A special menu model which keeps track of an "active" menu item.
-class ASH_EXPORT LauncherMenuModel : public ui::SimpleMenuModel {
- public:
-  explicit LauncherMenuModel(ui::SimpleMenuModel::Delegate* delegate)
-      : ui::SimpleMenuModel(delegate) {}
-
-  // Returns |true| when the given |command_id| is active and needs to be drawn
-  // in a special state.
-  virtual bool IsCommandActive(int command_id) const = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LauncherMenuModel);
-};
+class ShelfMenuModel;
 
 // Delegate for the LauncherItem.
-class ASH_EXPORT LauncherItemDelegate {
+class ASH_EXPORT ShelfItemDelegate {
  public:
-  virtual ~LauncherItemDelegate() {}
+  virtual ~ShelfItemDelegate() {}
 
   // Invoked when the user clicks on a window entry in the launcher.
   // |event| is the click event. The |event| is dispatched by a view
@@ -65,7 +52,7 @@
   //  - A list containing the title and the active list of items.
   // The caller takes ownership of the returned model.
   // |event_flags| specifies the flags of the event which triggered this menu.
-  virtual ash::LauncherMenuModel* CreateApplicationMenu(int event_flags) = 0;
+  virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) = 0;
 
   // Whether the launcher item is draggable.
   virtual bool IsDraggable() = 0;
@@ -76,4 +63,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
+#endif  // ASH_SHELF_SHELF_ITEM_DELEGATE_H_
diff --git a/ash/shelf/shelf_item_delegate_manager.cc b/ash/shelf/shelf_item_delegate_manager.cc
new file mode 100644
index 0000000..79124ca
--- /dev/null
+++ b/ash/shelf/shelf_item_delegate_manager.cc
@@ -0,0 +1,72 @@
+// Copyright 2013 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.
+
+#include "ash/shelf/shelf_item_delegate_manager.h"
+
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shell.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace ash {
+
+ShelfItemDelegateManager::ShelfItemDelegateManager(ShelfModel* model)
+    : model_(model) {
+  DCHECK(model_);
+  model_->AddObserver(this);
+}
+
+ShelfItemDelegateManager::~ShelfItemDelegateManager() {
+  model_->RemoveObserver(this);
+  STLDeleteContainerPairSecondPointers(id_to_item_delegate_map_.begin(),
+                                       id_to_item_delegate_map_.end());
+}
+
+void ShelfItemDelegateManager::SetShelfItemDelegate(
+    LauncherID id,
+    scoped_ptr<ShelfItemDelegate> item_delegate) {
+  // If another ShelfItemDelegate is already registered for |id|, we assume
+  // that this request is replacing ShelfItemDelegate for |id| with
+  // |item_delegate|.
+  RemoveShelfItemDelegate(id);
+  id_to_item_delegate_map_[id] = item_delegate.release();
+}
+
+ShelfItemDelegate* ShelfItemDelegateManager::GetShelfItemDelegate(
+    LauncherID id) {
+  if (model_->ItemIndexByID(id) != -1) {
+    // Each LauncherItem has to have a ShelfItemDelegate.
+    DCHECK(id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end());
+    return id_to_item_delegate_map_[id];
+  }
+  return NULL;
+}
+
+void ShelfItemDelegateManager::ShelfItemAdded(int index) {
+}
+
+void ShelfItemDelegateManager::ShelfItemRemoved(int index, LauncherID id) {
+  RemoveShelfItemDelegate(id);
+}
+
+void ShelfItemDelegateManager::ShelfItemMoved(int start_index,
+                                              int target_index) {
+}
+
+void ShelfItemDelegateManager::ShelfItemChanged(int index,
+                                                const LauncherItem& old_item) {
+}
+
+void ShelfItemDelegateManager::ShelfStatusChanged() {
+}
+
+void ShelfItemDelegateManager::RemoveShelfItemDelegate(LauncherID id) {
+  if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()) {
+    delete id_to_item_delegate_map_[id];
+    id_to_item_delegate_map_.erase(id);
+  }
+}
+
+}  // namespace ash
diff --git a/ash/shelf/shelf_item_delegate_manager.h b/ash/shelf/shelf_item_delegate_manager.h
new file mode 100644
index 0000000..dc4df73
--- /dev/null
+++ b/ash/shelf/shelf_item_delegate_manager.h
@@ -0,0 +1,67 @@
+// Copyright 2013 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 ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_
+#define ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_
+
+#include <map>
+
+#include "ash/ash_export.h"
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_model_observer.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace ash {
+class ShelfItemDelegate;
+class ShelfModel;
+
+namespace test {
+class ShelfItemDelegateManagerTestAPI;
+}
+
+// ShelfItemDelegateManager manages the set of ShelfItemDelegates for the
+// launcher. ShelfItemDelegateManager does not create ShelfItemDelegates,
+// rather it is expected that someone else invokes SetShelfItemDelegate
+// appropriately. On the other hand, ShelfItemDelegateManager destroys
+// ShelfItemDelegates when the corresponding item from the model is removed.
+class ASH_EXPORT ShelfItemDelegateManager : public ShelfModelObserver {
+ public:
+  explicit ShelfItemDelegateManager(ShelfModel* model);
+  virtual ~ShelfItemDelegateManager();
+
+  // Set |item_delegate| for |id| and take an ownership.
+  void SetShelfItemDelegate(LauncherID id,
+                            scoped_ptr<ShelfItemDelegate> item_delegate);
+
+  // Returns ShelfItemDelegate for |item_type|. Always returns non-NULL.
+  ShelfItemDelegate* GetShelfItemDelegate(LauncherID id);
+
+  // ShelfModelObserver overrides:
+  virtual void ShelfItemAdded(int model_index) OVERRIDE;
+  virtual void ShelfItemRemoved(int index, LauncherID id) OVERRIDE;
+  virtual void ShelfItemMoved(int start_index, int targetindex) OVERRIDE;
+  virtual void ShelfItemChanged(int index,
+                                const LauncherItem& old_item) OVERRIDE;
+  virtual void ShelfStatusChanged() OVERRIDE;
+
+ private:
+  friend class test::ShelfItemDelegateManagerTestAPI;
+
+  typedef std::map<LauncherID, ShelfItemDelegate*> LauncherIDToItemDelegateMap;
+
+  // Remove and destroy ShelfItemDelegate for |id|.
+  void RemoveShelfItemDelegate(LauncherID id);
+
+  // Owned by Shell.
+  ShelfModel* model_;
+
+  LauncherIDToItemDelegateMap id_to_item_delegate_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateManager);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 566db94..116e014 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -175,9 +175,8 @@
   }
 
   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
-    if (shelf_) {
-      shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
-    }
+    if (shelf_)
+      shelf_->UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
     delete this;
   }
 
@@ -319,8 +318,11 @@
     // when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
     WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
     switch (window_state) {
-      case WORKSPACE_WINDOW_STATE_FULL_SCREEN:
-        if (FullscreenWithHiddenShelf()) {
+      case WORKSPACE_WINDOW_STATE_FULL_SCREEN: {
+        const aura::Window* fullscreen_window = GetRootWindowController(
+            root_window_)->GetWindowForFullscreenMode();
+        if (fullscreen_window && wm::GetWindowState(fullscreen_window)->
+                hide_shelf_when_fullscreen()) {
           SetState(SHELF_HIDDEN);
         } else {
           // The shelf is sometimes not hidden when in immersive fullscreen.
@@ -328,9 +330,12 @@
           SetState(SHELF_AUTO_HIDE);
         }
         break;
+      }
+
       case WORKSPACE_WINDOW_STATE_MAXIMIZED:
         SetState(CalculateShelfVisibility());
         break;
+
       case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
       case WORKSPACE_WINDOW_STATE_DEFAULT:
         SetState(CalculateShelfVisibility());
@@ -366,7 +371,7 @@
 
 void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
   window_overlaps_shelf_ = value;
-  UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
+  UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
 }
 
 void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
@@ -385,7 +390,7 @@
   gesture_drag_amount_ = 0.f;
   gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
       auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
-  UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
+  UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
 }
 
 ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
@@ -538,16 +543,6 @@
          GetAlignment() == SHELF_ALIGNMENT_TOP;
 }
 
-bool ShelfLayoutManager::FullscreenWithHiddenShelf() const {
-  RootWindowController* controller = GetRootWindowController(root_window_);
-  if (!controller)
-    return false;
-  const aura::Window* window = controller->GetTopmostFullscreenWindow();
-  if (!window)
-    return false;
-  return wm::GetWindowState(window)->hide_shelf_when_fullscreen();
-}
-
 // static
 ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) {
   ShelfWidget* shelf = RootWindowController::ForLauncher(window)->shelf();
@@ -598,8 +593,7 @@
   State old_state = state_;
   state_ = state;
 
-  BackgroundAnimator::ChangeType change_type =
-      BackgroundAnimator::CHANGE_ANIMATE;
+  BackgroundAnimatorChangeType change_type = BACKGROUND_CHANGE_ANIMATE;
   bool delay_background_change = false;
 
   // Do not animate the background when:
@@ -610,7 +604,7 @@
   if (state.visibility_state == SHELF_VISIBLE &&
       state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED &&
       old_state.visibility_state != SHELF_VISIBLE) {
-    change_type = BackgroundAnimator::CHANGE_IMMEDIATE;
+    change_type = BACKGROUND_CHANGE_IMMEDIATE;
   } else {
     // Delay the animation when the shelf was hidden, and has just been made
     // visible (e.g. using a gesture-drag).
@@ -930,8 +924,11 @@
 }
 
 void ShelfLayoutManager::UpdateShelfBackground(
-    BackgroundAnimator::ChangeType type) {
-  shelf_->SetPaintsBackground(GetShelfBackgroundType(), type);
+    BackgroundAnimatorChangeType type) {
+  const ShelfBackgroundType background_type(GetShelfBackgroundType());
+  shelf_->SetPaintsBackground(background_type, type);
+  FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
+                    OnBackgroundUpdated(background_type, type));
 }
 
 ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
@@ -1134,6 +1131,7 @@
   if (dock_bounds_ != dock_bounds) {
     dock_bounds_ = dock_bounds;
     OnWindowResized();
+    UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
   }
 }
 
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 13f90b6..5d0496b 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -122,6 +122,9 @@
   // Returns the ideal bounds of the shelf assuming it is visible.
   gfx::Rect GetIdealBounds();
 
+  // Returns the docked area bounds.
+  const gfx::Rect& dock_bounds() const { return dock_bounds_; }
+
   // Stops any animations and sets the bounds of the launcher and status
   // widgets.
   void LayoutShelf();
@@ -212,10 +215,6 @@
   // Is the shelf's alignment horizontal?
   bool IsHorizontalAlignment() const;
 
-  // Returns true if there is a fullscreen window and the shelf needs to be
-  // hidden for the topmost fullscreen window.
-  bool FullscreenWithHiddenShelf() const;
-
   // Returns a ShelfLayoutManager on the display which has a launcher for
   // given |window|. See RootWindowController::ForLauncher for more info.
   static ShelfLayoutManager* ForLauncher(aura::Window* window);
@@ -292,7 +291,7 @@
   void UpdateTargetBoundsForGesture(TargetBounds* target_bounds) const;
 
   // Updates the background of the shelf.
-  void UpdateShelfBackground(BackgroundAnimator::ChangeType type);
+  void UpdateShelfBackground(BackgroundAnimatorChangeType type);
 
   // Returns how the shelf background is painted.
   ShelfBackgroundType GetShelfBackgroundType() const;
@@ -331,7 +330,7 @@
   virtual void OnKeyboardBoundsChanging(
       const gfx::Rect& keyboard_bounds) OVERRIDE;
 
-  // Overridden from dock::DockObserver:
+  // Overridden from DockedWindowLayoutManagerObserver:
   virtual void OnDockBoundsChanging(
       const gfx::Rect& dock_bounds,
       DockedWindowLayoutManagerObserver::Reason reason) OVERRIDE;
diff --git a/ash/shelf/shelf_layout_manager_observer.h b/ash/shelf/shelf_layout_manager_observer.h
index 6209980..8a2d583 100644
--- a/ash/shelf/shelf_layout_manager_observer.h
+++ b/ash/shelf/shelf_layout_manager_observer.h
@@ -6,6 +6,7 @@
 #define ASH_SHELF_SHELF_LAYOUT_MANAGER_OBSERVER_H_
 
 #include "ash/ash_export.h"
+#include "ash/shelf/background_animator.h"
 #include "ash/shelf/shelf_types.h"
 
 namespace aura {
@@ -30,6 +31,11 @@
   // Called when the auto hide behavior is changed.
   virtual void OnAutoHideBehaviorChanged(aura::Window* root_window,
                                          ShelfAutoHideBehavior new_behavior) {}
+
+  // Called when shelf background animation is started.
+  virtual void OnBackgroundUpdated(
+      ShelfBackgroundType background_type,
+      BackgroundAnimatorChangeType change_type) {}
 };
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 1009ce9..8dac93d 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1326,6 +1326,68 @@
   EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
 }
 
+// Tests that the shelf is only hidden for a fullscreen window at the front and
+// toggles visibility when another window is activated.
+TEST_F(ShelfLayoutManagerTest, FullscreenWindowInFrontHidesShelf) {
+  ShelfLayoutManager* shelf = GetShelfLayoutManager();
+
+  // Create a window and make it full screen.
+  aura::Window* window1 = CreateTestWindow();
+  window1->SetBounds(gfx::Rect(0, 0, 100, 100));
+  window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+  window1->Show();
+
+  aura::Window* window2 = CreateTestWindow();
+  window2->SetBounds(gfx::Rect(0, 0, 100, 100));
+  window2->Show();
+
+  wm::GetWindowState(window1)->Activate();
+  EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
+
+  wm::GetWindowState(window2)->Activate();
+  EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
+
+  wm::GetWindowState(window1)->Activate();
+  EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
+}
+
+// Test the behavior of the shelf when a window on one display is fullscreen
+// but the other display has the active window.
+TEST_F(ShelfLayoutManagerTest, FullscreenWindowOnSecondDisplay) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  UpdateDisplay("800x600,800x600");
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+  Shell::RootWindowControllerList root_window_controllers =
+      Shell::GetAllRootWindowControllers();
+
+  // Create windows on either display.
+  aura::Window* window1 = CreateTestWindow();
+  window1->SetBoundsInScreen(
+      gfx::Rect(0, 0, 100, 100),
+      display_manager->GetDisplayAt(0));
+  window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+  window1->Show();
+
+  aura::Window* window2 = CreateTestWindow();
+  window2->SetBoundsInScreen(
+      gfx::Rect(800, 0, 100, 100),
+      display_manager->GetDisplayAt(1));
+  window2->Show();
+
+  EXPECT_EQ(root_windows[0], window1->GetRootWindow());
+  EXPECT_EQ(root_windows[1], window2->GetRootWindow());
+
+  wm::GetWindowState(window2)->Activate();
+  EXPECT_EQ(SHELF_HIDDEN,
+      root_window_controllers[0]->GetShelfLayoutManager()->visibility_state());
+  EXPECT_EQ(SHELF_VISIBLE,
+      root_window_controllers[1]->GetShelfLayoutManager()->visibility_state());
+}
+
+
 #if defined(OS_WIN)
 // RootWindow and Display can't resize on Windows Ash. http://crbug.com/165962
 #define MAYBE_SetAlignment DISABLED_SetAlignment
diff --git a/ash/shelf/shelf_menu_model.h b/ash/shelf/shelf_menu_model.h
new file mode 100644
index 0000000..18e1dd0
--- /dev/null
+++ b/ash/shelf/shelf_menu_model.h
@@ -0,0 +1,29 @@
+// Copyright 2013 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 ASH_SHELF_SHELF_MENU_MODEL_H_
+#define ASH_SHELF_SHELF_MENU_MODEL_H_
+
+#include "ash/ash_export.h"
+#include "ui/base/models/simple_menu_model.h"
+
+namespace ash {
+
+// A special menu model which keeps track of an "active" menu item.
+class ASH_EXPORT ShelfMenuModel : public ui::SimpleMenuModel {
+ public:
+  explicit ShelfMenuModel(ui::SimpleMenuModel::Delegate* delegate)
+      : ui::SimpleMenuModel(delegate) {}
+
+  // Returns |true| when the given |command_id| is active and needs to be drawn
+  // in a special state.
+  virtual bool IsCommandActive(int command_id) const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShelfMenuModel);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SHELF_SHELF_MENU_MODEL_H_
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 8e5b780..dd20a17 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -29,7 +29,7 @@
 namespace {
 const int kTooltipTopBottomMargin = 3;
 const int kTooltipLeftRightMargin = 10;
-const int kTooltipAppearanceDelay = 200;  // msec
+const int kTooltipAppearanceDelay = 1000;  // msec
 const int kTooltipMinHeight = 29 - 2 * kTooltipTopBottomMargin;
 const SkColor kTooltipTextColor = SkColorSetRGB(0x22, 0x22, 0x22);
 
diff --git a/ash/shelf/shelf_util.cc b/ash/shelf/shelf_util.cc
index 2e86814..77031d8 100644
--- a/ash/shelf/shelf_util.cc
+++ b/ash/shelf/shelf_util.cc
@@ -4,14 +4,20 @@
 
 #include "ash/shelf/shelf_util.h"
 
-#include "ui/aura/window.h"
 #include "ui/aura/window_property.h"
 
 DECLARE_WINDOW_PROPERTY_TYPE(ash::LauncherID);
+DECLARE_WINDOW_PROPERTY_TYPE(ash::LauncherItemDetails*);
 
 namespace ash {
 
-DEFINE_LOCAL_WINDOW_PROPERTY_KEY(LauncherID, kLauncherID, 0);
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(LauncherID, kLauncherID, kInvalidLauncherID);
+
+// ash::LauncherItemDetails for kLauncherItemDetaildKey is owned by the window
+// and will be freed automatically.
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(LauncherItemDetails,
+                                 kLauncherItemDetailsKey,
+                                 NULL);
 
 void SetLauncherIDForWindow(LauncherID id, aura::Window* window) {
   if (!window)
@@ -25,4 +31,20 @@
   return window->GetProperty(kLauncherID);
 }
 
+void SetLauncherItemDetailsForWindow(aura::Window* window,
+                                     const LauncherItemDetails& details) {
+  // |item_details| is owned by |window|.
+  LauncherItemDetails* item_details = new LauncherItemDetails(details);
+  window->SetProperty(kLauncherItemDetailsKey, item_details);
+}
+
+void ClearLauncherItemDetailsForWindow(aura::Window* window) {
+  window->ClearProperty(kLauncherItemDetailsKey);
+}
+
+const LauncherItemDetails* GetLauncherItemDetailsForWindow(
+    aura::Window* window) {
+  return window->GetProperty(kLauncherItemDetailsKey);
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_util.h b/ash/shelf/shelf_util.h
index 30953a7..6fb3d5e 100644
--- a/ash/shelf/shelf_util.h
+++ b/ash/shelf/shelf_util.h
@@ -7,6 +7,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/launcher/launcher_types.h"
+#include "ui/aura/window.h"
 
 namespace aura {
 class Window;
@@ -14,6 +15,9 @@
 
 namespace ash {
 
+extern const aura::WindowProperty<ash::LauncherItemDetails*>* const
+    kLauncherItemDetailsKey;
+
 // Associates LauncherItem of |id| with specified |window|.
 ASH_EXPORT void SetLauncherIDForWindow(LauncherID id, aura::Window* window);
 
@@ -23,6 +27,21 @@
 // currently active tab.
 ASH_EXPORT LauncherID GetLauncherIDForWindow(aura::Window* window);
 
+// Sets LauncherItemDetails for |window|.
+ASH_EXPORT void SetLauncherItemDetailsForWindow(
+    aura::Window* window,
+    const LauncherItemDetails& details);
+
+// Clears LauncherItemDetails for |window|.
+// If |window| has a LauncherItem by SetLauncherItemDetailsForWindow(), it will
+// be removed.
+ASH_EXPORT void ClearLauncherItemDetailsForWindow(aura::Window* window);
+
+// Returns LauncherItemDetails for |window| or NULL if it doesn't have.
+// Returned LauncherItemDetails object is owned by the |window|.
+ASH_EXPORT const LauncherItemDetails* GetLauncherItemDetailsForWindow(
+    aura::Window* window);
+
 }  // namespace ash
 
 #endif  // ASH_SHELF_SHELF_UTIL_H_
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 9393368..feab088 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -9,9 +9,6 @@
 #include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
 #include "ash/drag_drop/drag_image_view.h"
-#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/root_window_controller.h"
 #include "ash/scoped_target_root_window.h"
 #include "ash/shelf/alternate_app_list_button.h"
@@ -20,8 +17,12 @@
 #include "ash/shelf/overflow_bubble_view.h"
 #include "ash/shelf/overflow_button.h"
 #include "ash/shelf/shelf_button.h"
+#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_icon_observer.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
 #include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_menu_model.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
 #include "ash/shelf/shelf_widget.h"
@@ -50,7 +51,6 @@
 #include "ui/views/controls/menu/menu_model_adapter.h"
 #include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/focus/focus_search.h"
-#include "ui/views/focus_border.h"
 #include "ui/views/view_model.h"
 #include "ui/views/view_model_utils.h"
 #include "ui/views/widget/widget.h"
@@ -114,16 +114,18 @@
 // The rip off drag and drop proxy image should get scaled by this factor.
 const float kDragAndDropProxyScale = 1.5f;
 
+// The opacity represents that this partially disappeared item will get removed.
+const float kDraggedImageOpacity = 0.5f;
+
 namespace {
 
 // The MenuModelAdapter gets slightly changed to adapt the menu appearance to
 // our requirements.
-class LauncherMenuModelAdapter
-    : public views::MenuModelAdapter {
+class ShelfMenuModelAdapter : public views::MenuModelAdapter {
  public:
-  explicit LauncherMenuModelAdapter(ash::LauncherMenuModel* menu_model);
+  explicit ShelfMenuModelAdapter(ShelfMenuModel* menu_model);
 
-  // Overriding MenuModelAdapter's MenuDelegate implementation.
+  // views::MenuModelAdapter:
   virtual const gfx::Font* GetLabelFont(int command_id) const OVERRIDE;
   virtual bool IsCommandEnabled(int id) const OVERRIDE;
   virtual void GetHorizontalIconMargins(int id,
@@ -140,18 +142,17 @@
   virtual bool ShouldReserveSpaceForSubmenuIndicator() const OVERRIDE;
 
  private:
-  ash::LauncherMenuModel* launcher_menu_model_;
+  ShelfMenuModel* menu_model_;
 
-  DISALLOW_COPY_AND_ASSIGN(LauncherMenuModelAdapter);
+  DISALLOW_COPY_AND_ASSIGN(ShelfMenuModelAdapter);
 };
 
-LauncherMenuModelAdapter::LauncherMenuModelAdapter(
-    ash::LauncherMenuModel* menu_model)
+ShelfMenuModelAdapter::ShelfMenuModelAdapter(ShelfMenuModel* menu_model)
     : MenuModelAdapter(menu_model),
-      launcher_menu_model_(menu_model) {}
+      menu_model_(menu_model) {
+}
 
-const gfx::Font* LauncherMenuModelAdapter::GetLabelFont(
-    int command_id) const {
+const gfx::Font* ShelfMenuModelAdapter::GetLabelFont(int command_id) const {
   if (command_id != kCommandIdOfMenuName)
     return MenuModelAdapter::GetLabelFont(command_id);
 
@@ -159,14 +160,13 @@
   return &rb.GetFont(ui::ResourceBundle::BoldFont);
 }
 
-bool LauncherMenuModelAdapter::IsCommandEnabled(int id) const {
+bool ShelfMenuModelAdapter::IsCommandEnabled(int id) const {
   return id != kCommandIdOfMenuName;
 }
 
-bool LauncherMenuModelAdapter::GetForegroundColor(
-    int command_id,
-    bool is_hovered,
-    SkColor* override_color) const {
+bool ShelfMenuModelAdapter::GetForegroundColor(int command_id,
+                                               bool is_hovered,
+                                               SkColor* override_color) const {
   if (command_id != kCommandIdOfMenuName)
     return false;
 
@@ -174,11 +174,10 @@
   return true;
 }
 
-bool LauncherMenuModelAdapter::GetBackgroundColor(
-    int command_id,
-    bool is_hovered,
-    SkColor* override_color) const {
-  if (!launcher_menu_model_->IsCommandActive(command_id))
+bool ShelfMenuModelAdapter::GetBackgroundColor(int command_id,
+                                               bool is_hovered,
+                                               SkColor* override_color) const {
+  if (!menu_model_->IsCommandActive(command_id))
     return false;
 
   *override_color = is_hovered ? kFocusedActiveListItemBackgroundColor :
@@ -186,21 +185,20 @@
   return true;
 }
 
-void LauncherMenuModelAdapter::GetHorizontalIconMargins(
-    int command_id,
-    int icon_size,
-    int* left_margin,
-    int* right_margin) const {
+void ShelfMenuModelAdapter::GetHorizontalIconMargins(int command_id,
+                                                     int icon_size,
+                                                     int* left_margin,
+                                                     int* right_margin) const {
   *left_margin = kHorizontalIconSpacing;
   *right_margin = (command_id != kCommandIdOfMenuName) ?
       kHorizontalIconSpacing : -(icon_size + kHorizontalNoIconInsetSpacing);
 }
 
-int LauncherMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) {
+int ShelfMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) {
   return kMaximumAppMenuItemLength;
 }
 
-bool LauncherMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
+bool ShelfMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
   return false;
 }
 
@@ -243,22 +241,6 @@
   DISALLOW_COPY_AND_ASSIGN(LauncherFocusSearch);
 };
 
-class ShelfButtonFocusBorder : public views::FocusBorder {
- public:
-  ShelfButtonFocusBorder() {}
-  virtual ~ShelfButtonFocusBorder() {}
-
- private:
-  // views::FocusBorder overrides:
-  virtual void Paint(const View& view, gfx::Canvas* canvas) const OVERRIDE {
-    gfx::Rect rect(view.GetLocalBounds());
-    rect.Inset(1, 1);
-    canvas->DrawRect(rect, kFocusBorderColor);
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(ShelfButtonFocusBorder);
-};
-
 // AnimationDelegate used when inserting a new item. This steadily increases the
 // opacity of the layer as the animation progress.
 class FadeInAnimationDelegate
@@ -370,8 +352,8 @@
 };
 
 ShelfView::ShelfView(ShelfModel* model,
-                     LauncherDelegate* delegate,
-                     ShelfLayoutManager* shelf_layout_manager)
+                     ShelfDelegate* delegate,
+                     ShelfLayoutManager* manager)
     : model_(model),
       delegate_(delegate),
       view_model_(new views::ViewModel),
@@ -393,15 +375,17 @@
       drag_and_drop_launcher_id_(0),
       dragged_off_shelf_(false),
       snap_back_from_rip_off_view_(NULL),
-      item_manager_(Shell::GetInstance()->launcher_item_delegate_manager()),
-      layout_manager_(shelf_layout_manager),
-      overflow_mode_(false) {
+      item_manager_(Shell::GetInstance()->shelf_item_delegate_manager()),
+      layout_manager_(manager),
+      overflow_mode_(false),
+      main_shelf_(NULL),
+      dragged_off_from_overflow_to_shelf_(false) {
   DCHECK(model_);
   bounds_animator_.reset(new views::BoundsAnimator(this));
   bounds_animator_->AddObserver(this);
   set_context_menu_controller(this);
   focus_search_.reset(new LauncherFocusSearch(view_model_.get()));
-  tooltip_.reset(new ShelfTooltipManager(shelf_layout_manager, this));
+  tooltip_.reset(new ShelfTooltipManager(manager, this));
 }
 
 ShelfView::~ShelfView() {
@@ -602,7 +586,11 @@
   // Check if the application is known and pinned - if not, we have to pin it so
   // that we can re-arrange the launcher order accordingly. Note that items have
   // to be pinned to give them the same (order) possibilities as a shortcut.
-  if (!drag_and_drop_launcher_id_ || !delegate_->IsAppPinned(app_id)) {
+  // When an item is dragged from overflow to shelf, IsShowingOverflowBubble()
+  // returns true. At this time, we don't need to pin the item.
+  if (!IsShowingOverflowBubble() &&
+      (!drag_and_drop_launcher_id_ ||
+       !delegate_->IsAppPinned(app_id))) {
     delegate_->PinAppWithID(app_id);
     drag_and_drop_launcher_id_ =
         delegate_->GetLauncherIDForAppID(drag_and_drop_app_id_);
@@ -695,6 +683,27 @@
   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
 }
 
+void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
+  // The overflow button is not shown in overflow mode.
+  overflow_button_->SetVisible(false);
+  int last_button_index = model_->FirstPanelIndex() - 1;
+  DCHECK_LT(last_visible_index_, view_model_->view_size());
+  for (int i = 0; i < view_model_->view_size(); ++i) {
+    bool visible = i >= first_visible_index_ &&
+        i <= last_visible_index_;
+    if (!ash::switches::UseAlternateShelfLayout())
+      visible &= i != last_button_index;
+
+    // To track the dragging of |drag_view_| continuously, its visibility
+    // should be always true regardless of its position.
+    if (dragged_off_from_overflow_to_shelf_ &&
+        view_model_->view_at(i) == drag_view_)
+      view_model_->view_at(i)->SetVisible(true);
+    else
+      view_model_->view_at(i)->SetVisible(visible);
+  }
+}
+
 void ShelfView::CalculateIdealBounds(IdealBounds* bounds) {
   int available_size = layout_manager_->PrimaryAxisValue(width(), height());
   DCHECK(model_->item_count() == view_model_->view_size());
@@ -730,16 +739,7 @@
   }
 
   if (is_overflow_mode()) {
-    // The overflow button is not shown in overflow mode.
-    overflow_button_->SetVisible(false);
-    DCHECK_LT(last_visible_index_, view_model_->view_size());
-    for (int i = 0; i < view_model_->view_size(); ++i) {
-      bool visible = i >= first_visible_index_ &&
-          i <= last_visible_index_;
-      if (!ash::switches::UseAlternateShelfLayout())
-        visible &= i != last_button_index;
-      view_model_->view_at(i)->SetVisible(visible);
-    }
+    UpdateAllButtonsVisibilityInOverflowMode();
     return;
   }
 
@@ -952,7 +952,6 @@
       break;
   }
   view->set_context_menu_controller(this);
-  view->set_focus_border(new ShelfButtonFocusBorder);
 
   DCHECK(view);
   ConfigureChildView(view);
@@ -979,7 +978,7 @@
   }
 
   // If the item is no longer draggable, bail out.
-  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+  ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
       model_->items()[start_drag_index_].id);
   if (!item_delegate->IsDraggable()) {
     CancelDrag(-1);
@@ -997,7 +996,7 @@
   int current_index = view_model_->GetIndexOfView(drag_view_);
   DCHECK_NE(-1, current_index);
 
-  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+  ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
       model_->items()[current_index].id);
   if (!item_delegate->IsDraggable()) {
     CancelDrag(-1);
@@ -1069,6 +1068,8 @@
 bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
   int current_index = view_model_->GetIndexOfView(drag_view_);
   DCHECK_NE(-1, current_index);
+  std::string dragged_app_id =
+      delegate_->GetAppIDForLauncherID(model_->items()[current_index].id);
 
   gfx::Point screen_location = event.root_location();
   ash::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(),
@@ -1080,17 +1081,47 @@
     // If the shelf/overflow bubble bounds contains |screen_location| we insert
     // the item back into the shelf.
     if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
+      if (dragged_off_from_overflow_to_shelf_) {
+        // During the dragging an item from Shelf to Overflow, it can enter here
+        // directly because both are located very closly.
+        main_shelf_->EndDrag(true);
+        // Stops the animation of |drag_view_| and sets its bounds explicitly
+        // becase ContinueDrag() stops its animation. Without this, unexpected
+        // bounds will be set.
+        bounds_animator_->StopAnimatingView(drag_view_);
+        int drag_view_index = view_model_->GetIndexOfView(drag_view_);
+        drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
+        dragged_off_from_overflow_to_shelf_ = false;
+      }
       // Destroy our proxy view item.
       DestroyDragIconProxy();
       // Re-insert the item and return simply false since the caller will handle
       // the move as in any normal case.
       dragged_off_shelf_ = false;
       drag_view_->layer()->SetOpacity(1.0f);
-      // Overflow bubble should be enlarged immediately when an item is
-      // re-inserted.
+      // The size of Overflow bubble should be updated immediately when an item
+      // is re-inserted.
       if (is_overflow_mode())
         PreferredSizeChanged();
       return false;
+    } else if (is_overflow_mode() &&
+               main_shelf_->GetBoundsForDragInsertInScreen().Contains(
+                   screen_location)) {
+      if (!dragged_off_from_overflow_to_shelf_) {
+        dragged_off_from_overflow_to_shelf_ = true;
+        drag_image_->SetOpacity(1.0f);
+        main_shelf_->StartDrag(dragged_app_id, screen_location);
+      } else {
+        main_shelf_->Drag(screen_location);
+      }
+    } else if (dragged_off_from_overflow_to_shelf_) {
+      // Makes the |drag_image_| partially disappear again.
+      dragged_off_from_overflow_to_shelf_ = false;
+      drag_image_->SetOpacity(kDraggedImageOpacity);
+      main_shelf_->EndDrag(true);
+      bounds_animator_->StopAnimatingView(drag_view_);
+      int drag_view_index = view_model_->GetIndexOfView(drag_view_);
+      drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
     }
     // Move our proxy view item.
     UpdateDragIconProxy(screen_location);
@@ -1122,7 +1153,7 @@
       }
       // Make the item partially disappear to show that it will get removed if
       // dropped.
-      drag_image_->SetOpacity(0.5f);
+      drag_image_->SetOpacity(kDraggedImageOpacity);
     }
     return true;
   }
@@ -1150,9 +1181,13 @@
   bool snap_back = false;
   // Items which cannot be dragged off will be handled as a cancel.
   if (!cancel) {
-    // Make sure we do not try to remove un-removable items like items which
-    // were not pinned or have to be always there.
-    if (RemovableByRipOff(current_index) != REMOVABLE) {
+    if (dragged_off_from_overflow_to_shelf_) {
+      dragged_off_from_overflow_to_shelf_ = false;
+      main_shelf_->EndDrag(false);
+      drag_view_->layer()->SetOpacity(1.0f);
+    } else if (RemovableByRipOff(current_index) != REMOVABLE) {
+      // Make sure we do not try to remove un-removable items like items which
+      // were not pinned or have to be always there.
       cancel = true;
       snap_back = true;
     } else {
@@ -1164,7 +1199,12 @@
     }
   }
   if (cancel || snap_back) {
-    if (!cancelling_drag_model_changed_) {
+    if (dragged_off_from_overflow_to_shelf_) {
+      dragged_off_from_overflow_to_shelf_ = false;
+      // Main shelf handles revert of dragged item.
+      main_shelf_->EndDrag(true);
+      drag_view_->layer()->SetOpacity(1.0f);
+    } else if (!cancelling_drag_model_changed_) {
       // Only do something if the change did not come through a model change.
       gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
       gfx::Point relative_to = GetBoundsInScreen().origin();
@@ -1253,6 +1293,7 @@
   overflow_view->Init();
   overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
   overflow_view->OnShelfAlignmentChanged();
+  overflow_view->main_shelf_ = this;
   UpdateOverflowRange(overflow_view);
 
   overflow_bubble_->Show(overflow_button_, overflow_view);
@@ -1421,7 +1462,11 @@
   // When an item is dragged off from the overflow bubble, it is moved to last
   // position and and changed to invisible. Overflow bubble size should be
   // shrunk to fit only for visible items.
-  if (is_overflow_mode() && dragged_off_shelf_ &&
+  // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
+  // items in the shelf.
+  if (is_overflow_mode() &&
+      dragged_off_shelf_ &&
+      !dragged_off_from_overflow_to_shelf_ &&
       RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE)
     last_button_index--;
 
@@ -1614,7 +1659,7 @@
   if (index == -1)
     return;
 
-  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+  ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
       model_->items()[index].id);
   if (view_model_->view_size() <= 1 || !item_delegate->IsDraggable())
     return;  // View is being deleted or not draggable, ignore request.
@@ -1690,7 +1735,7 @@
   if (view_index == -1)
     return base::string16();
 
-  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+  ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
       model_->items()[view_index].id);
   return item_delegate->GetTitle();
 }
@@ -1748,9 +1793,8 @@
         break;
     }
 
-    LauncherItemDelegate* item_delegate =
-        item_manager_->GetLauncherItemDelegate(
-            model_->items()[view_index].id);
+    ShelfItemDelegate* item_delegate =
+        item_manager_->GetShelfItemDelegate(model_->items()[view_index].id);
     if (!item_delegate->ItemSelected(event))
       ShowListMenuForView(model_->items()[view_index], sender, event);
   }
@@ -1759,9 +1803,9 @@
 bool ShelfView::ShowListMenuForView(const LauncherItem& item,
                                     views::View* source,
                                     const ui::Event& event) {
-  scoped_ptr<ash::LauncherMenuModel> menu_model;
-  LauncherItemDelegate* item_delegate =
-      item_manager_->GetLauncherItemDelegate(item.id);
+  scoped_ptr<ShelfMenuModel> menu_model;
+  ShelfItemDelegate* item_delegate =
+      item_manager_->GetShelfItemDelegate(item.id);
   menu_model.reset(item_delegate->CreateApplicationMenu(event.flags()));
 
   // Make sure we have a menu and it has at least two items in addition to the
@@ -1770,7 +1814,7 @@
     return false;
 
   ShowMenu(scoped_ptr<views::MenuModelAdapter>(
-               new LauncherMenuModelAdapter(menu_model.get())),
+               new ShelfMenuModelAdapter(menu_model.get())),
            source,
            gfx::Point(),
            false,
@@ -1783,9 +1827,8 @@
                                        ui::MenuSourceType source_type) {
   int view_index = view_model_->GetIndexOfView(source);
   // TODO(simon.hong81): Create LauncherContextMenu for applist in its
-  // LauncherItemDelegate.
-  if (view_index != -1 &&
-      model_->items()[view_index].type == TYPE_APP_LIST) {
+  // ShelfItemDelegate.
+  if (view_index != -1 && model_->items()[view_index].type == TYPE_APP_LIST) {
     view_index = -1;
   }
 
@@ -1794,7 +1837,7 @@
     return;
   }
   scoped_ptr<ui::MenuModel> menu_model;
-  LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+  ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
       model_->items()[view_index].id);
   menu_model.reset(item_delegate->CreateContextMenu(
       source->GetWidget()->GetNativeView()->GetRootWindow()));
@@ -1955,8 +1998,8 @@
   const LauncherItem* item = LauncherItemForView(view);
   if (!item)
     return true;
-  LauncherItemDelegate* item_delegate =
-      item_manager_->GetLauncherItemDelegate(item->id);
+  ShelfItemDelegate* item_delegate =
+      item_manager_->GetShelfItemDelegate(item->id);
   return item_delegate->ShouldShowTooltip();
 }
 
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 4d6545e..1f090e6 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -32,11 +32,11 @@
 class ShelfViewTestAPI;
 }
 
-class LauncherDelegate;
-struct LauncherItem;
-class LauncherItemDelegateManager;
+class ShelfDelegate;
 class ShelfIconObserver;
+class ShelfItemDelegateManager;
 class ShelfModel;
+struct LauncherItem;
 
 namespace internal {
 
@@ -62,8 +62,8 @@
                              public app_list::ApplicationDragAndDropHost {
  public:
   ShelfView(ShelfModel* model,
-            LauncherDelegate* delegate,
-            ShelfLayoutManager* shelf_layout_manager);
+            ShelfDelegate* delegate,
+            ShelfLayoutManager* manager);
   virtual ~ShelfView();
 
   ShelfTooltipManager* tooltip_manager() { return tooltip_.get(); }
@@ -168,6 +168,9 @@
   // Sets the bounds of each view to its ideal bounds.
   void LayoutToIdealBounds();
 
+  // Update all button's visibility in overflow.
+  void UpdateAllButtonsVisibilityInOverflowMode();
+
   // Calculates the ideal bounds. The bounds of each button corresponding to an
   // item in the model is set in |view_model_|.
   void CalculateIdealBounds(IdealBounds* bounds);
@@ -335,7 +338,7 @@
   ShelfModel* model_;
 
   // Delegate; owned by Launcher.
-  LauncherDelegate* delegate_;
+  ShelfDelegate* delegate_;
 
   // Used to manage the set of active launcher buttons. There is a view per
   // item in |model_|.
@@ -431,8 +434,8 @@
   // The rip off view when a snap back operation is underway.
   views::View* snap_back_from_rip_off_view_;
 
-  // Holds LauncherItemDelegateManager.
-  LauncherItemDelegateManager* item_manager_;
+  // Holds ShelfItemDelegateManager.
+  ShelfItemDelegateManager* item_manager_;
 
   // Holds ShelfLayoutManager.
   ShelfLayoutManager* layout_manager_;
@@ -440,6 +443,12 @@
   // True when this ShelfView is used for Overflow Bubble.
   bool overflow_mode_;
 
+  // Holds a pointer to main ShelfView when a ShelfView is in overflow mode.
+  ShelfView* main_shelf_;
+
+  // True when ripped item from overflow bubble is entered into Shelf.
+  bool dragged_off_from_overflow_to_shelf_;
+
   DISALLOW_COPY_AND_ASSIGN(ShelfView);
 };
 
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 28812d1..d3e2b1d 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -9,13 +9,13 @@
 
 #include "ash/ash_switches.h"
 #include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/launcher/launcher_types.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/overflow_bubble.h"
 #include "ash/shelf/overflow_bubble_view.h"
 #include "ash/shelf/shelf_button.h"
 #include "ash/shelf/shelf_icon_observer.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
@@ -27,12 +27,13 @@
 #include "ash/test/overflow_bubble_view_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
-#include "ash/test/test_launcher_item_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
+#include "ash/test/test_shelf_item_delegate.h"
 #include "base/basictypes.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "grit/ash_resources.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/aura_test_base.h"
@@ -81,7 +82,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestShelfIconObserver);
 };
 
-class ShelfViewIconObserverTest : public ash::test::AshTestBase {
+class ShelfViewIconObserverTest : public AshTestBase {
  public:
   ShelfViewIconObserverTest() {}
   virtual ~ShelfViewIconObserverTest() {}
@@ -119,9 +120,8 @@
 };
 
 TEST_F(ShelfViewIconObserverTest, AddRemove) {
-  ash::test::TestLauncherDelegate* launcher_delegate =
-      ash::test::TestLauncherDelegate::instance();
-  ASSERT_TRUE(launcher_delegate);
+  TestShelfDelegate* shelf_delegate = TestShelfDelegate::instance();
+  ASSERT_TRUE(shelf_delegate);
 
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -130,7 +130,7 @@
 
   scoped_ptr<views::Widget> widget(new views::Widget());
   widget->Init(params);
-  launcher_delegate->AddLauncherItem(widget->GetNativeWindow());
+  shelf_delegate->AddLauncherItem(widget->GetNativeWindow());
   shelf_view_test()->RunMessageLoopUntilAnimationsDone();
   EXPECT_TRUE(observer()->change_notified());
   observer()->Reset();
@@ -156,9 +156,8 @@
   UpdateDisplay("400x400,400x400");
   TestShelfIconObserver second_observer(LauncherForSecondaryDisplay());
 
-  ash::test::TestLauncherDelegate* launcher_delegate =
-      ash::test::TestLauncherDelegate::instance();
-  ASSERT_TRUE(launcher_delegate);
+  TestShelfDelegate* shelf_delegate = TestShelfDelegate::instance();
+  ASSERT_TRUE(shelf_delegate);
 
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -167,7 +166,7 @@
 
   scoped_ptr<views::Widget> widget(new views::Widget());
   widget->Init(params);
-  launcher_delegate->AddLauncherItem(widget->GetNativeWindow());
+  shelf_delegate->AddLauncherItem(widget->GetNativeWindow());
   shelf_view_test()->RunMessageLoopUntilAnimationsDone();
   EXPECT_TRUE(observer()->change_notified());
   EXPECT_TRUE(second_observer.change_notified());
@@ -184,7 +183,7 @@
 }
 
 TEST_F(ShelfViewIconObserverTest, BoundsChanged) {
-  ash::ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf();
+  ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf();
   Launcher* launcher = Launcher::ForPrimaryDisplay();
   gfx::Size shelf_size =
       shelf->GetWindowBoundsInScreen().size();
@@ -199,24 +198,63 @@
 ////////////////////////////////////////////////////////////////////////////////
 // ShelfView tests.
 
-// LauncherItemDelegate for ShelfViewTest.OverflowBubbleSize only.
-// This class should only be used for re-insert test because it cannot handle
-// unpin request.
-class TestLauncherDelegateForShelfView : public TestLauncherDelegate {
+// Simple ShelfDelegate implmentation for ShelfViewTest.OverflowBubbleSize
+// and CheckDragAndDropFromOverflowBubbleToShelf
+class TestShelfDelegateForShelfView : public ShelfDelegate {
  public:
-  explicit TestLauncherDelegateForShelfView(ShelfModel* model)
-      : TestLauncherDelegate(model) {}
-  virtual ~TestLauncherDelegateForShelfView() {}
+  explicit TestShelfDelegateForShelfView(ShelfModel* model)
+      : model_(model) {}
+  virtual ~TestShelfDelegateForShelfView() {}
 
-  // TestLauncherDelegate overrides:
+  // ShelfDelegate overrides:
+  virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE {}
+
+  virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE {}
+
+  virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE {
+    LauncherID id = 0;
+    EXPECT_TRUE(base::StringToInt(app_id, &id));
+    return id;
+  }
+
+  virtual const std::string& GetAppIDForLauncherID(LauncherID id) OVERRIDE {
+    // Use |app_id_| member variable because returning a reference to local
+    // variable is not allowed.
+    app_id_ = base::IntToString(id);
+    return app_id_;
+  }
+
+  virtual void PinAppWithID(const std::string& app_id) OVERRIDE {
+  }
+
   virtual bool IsAppPinned(const std::string& app_id) OVERRIDE {
     // Returns true for ShelfViewTest.OverflowBubbleSize. To test ripping off in
     // that test, an item is already pinned state.
     return true;
   }
 
+  virtual bool CanPin() const OVERRIDE {
+    return true;
+  }
+
+  virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE {
+    LauncherID id = 0;
+    EXPECT_TRUE(base::StringToInt(app_id, &id));
+    ASSERT_GT(id, 0);
+    int index = model_->ItemIndexByID(id);
+    ASSERT_GE(index, 0);
+
+    model_->RemoveItemAt(index);
+  }
+
  private:
-  DISALLOW_COPY_AND_ASSIGN(TestLauncherDelegateForShelfView);
+  ShelfModel* model_;
+
+  // Temp member variable for returning a value. See the comment in the
+  // GetAppIDForLauncherID().
+  std::string app_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestShelfDelegateForShelfView);
 };
 
 class ShelfViewTest : public AshTestBase {
@@ -238,8 +276,7 @@
     test_api_.reset(new ShelfViewTestAPI(shelf_view_));
     test_api_->SetAnimationDuration(1);  // Speeds up animation for test.
 
-    item_manager_ =
-        ash::Shell::GetInstance()->launcher_item_delegate_manager();
+    item_manager_ = Shell::GetInstance()->shelf_item_delegate_manager();
     DCHECK(item_manager_);
 
     // Add browser shortcut launcher item at index 0 for test.
@@ -252,10 +289,9 @@
   }
 
  protected:
-  void CreateAndSetLauncherItemDelegateForID(LauncherID id) {
-    scoped_ptr<LauncherItemDelegate> delegate(
-        new ash::test::TestLauncherItemDelegate(NULL));
-    item_manager_->SetLauncherItemDelegate(id, delegate.Pass());
+  void CreateAndSetShelfItemDelegateForID(LauncherID id) {
+    scoped_ptr<ShelfItemDelegate> delegate(new TestShelfItemDelegate(NULL));
+    item_manager_->SetShelfItemDelegate(id, delegate.Pass());
   }
 
   LauncherID AddBrowserShortcut() {
@@ -264,7 +300,7 @@
 
     LauncherID id = model_->next_id();
     model_->AddAt(browser_index_, browser_shortcut);
-    CreateAndSetLauncherItemDelegateForID(id);
+    CreateAndSetShelfItemDelegateForID(id);
     test_api_->RunMessageLoopUntilAnimationsDone();
     return id;
   }
@@ -276,7 +312,7 @@
 
     LauncherID id = model_->next_id();
     model_->Add(item);
-    CreateAndSetLauncherItemDelegateForID(id);
+    CreateAndSetShelfItemDelegateForID(id);
     test_api_->RunMessageLoopUntilAnimationsDone();
     return id;
   }
@@ -294,7 +330,7 @@
 
     LauncherID id = model_->next_id();
     model_->Add(item);
-    CreateAndSetLauncherItemDelegateForID(id);
+    CreateAndSetShelfItemDelegateForID(id);
     return id;
   }
 
@@ -305,7 +341,7 @@
 
     LauncherID id = model_->next_id();
     model_->Add(item);
-    CreateAndSetLauncherItemDelegateForID(id);
+    CreateAndSetShelfItemDelegateForID(id);
     return id;
   }
 
@@ -336,8 +372,8 @@
     for (size_t model_index = 0;
          model_index < model_->items().size();
          ++model_index) {
-      ash::LauncherItem item = model_->items()[model_index];
-      ash::LauncherID id = item.id;
+      LauncherItem item = model_->items()[model_index];
+      LauncherID id = item.id;
       EXPECT_EQ(id_map[map_index].first, id);
       EXPECT_EQ(id_map[map_index].second, GetButtonByID(id));
       ++map_index;
@@ -419,14 +455,115 @@
     return shelf_view_->tooltip_manager()->anchor_;
   }
 
+  void AddButtonsUntilOverflow() {
+    int items_added = 0;
+    while (!test_api_->IsOverflowButtonVisible()) {
+      AddAppShortcut();
+      ++items_added;
+      ASSERT_LT(items_added, 10000);
+    }
+  }
+
   void ShowTooltip() {
     shelf_view_->tooltip_manager()->ShowInternal();
   }
 
+  void TestDraggingAnItemFromOverflowToShelf(bool cancel) {
+    test_api_->ShowOverflowBubble();
+    ASSERT_TRUE(test_api_->overflow_bubble() &&
+                test_api_->overflow_bubble()->IsShowing());
+
+    ash::test::ShelfViewTestAPI test_api_for_overflow(
+      test_api_->overflow_bubble()->shelf_view());
+
+    int total_item_count = model_->item_count();
+
+    int last_visible_item_id_in_shelf =
+        model_->items()[test_api_->GetLastVisibleIndex()].id;
+    int second_last_visible_item_id_in_shelf =
+        model_->items()[test_api_->GetLastVisibleIndex() - 1].id;
+    int first_visible_item_id_in_overflow =
+        model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id;
+    int second_last_visible_item_id_in_overflow =
+        model_->items()[test_api_for_overflow.GetLastVisibleIndex() - 1].id;
+
+    int drag_item_index =
+        test_api_for_overflow.GetLastVisibleIndex();
+    LauncherID drag_item_id = model_->items()[drag_item_index].id;
+    internal::ShelfButton* drag_button =
+        test_api_for_overflow.GetButton(drag_item_index);
+    gfx::Point center_point_of_drag_item =
+        drag_button->GetBoundsInScreen().CenterPoint();
+
+    aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(),
+                                         center_point_of_drag_item);
+    // Rip an item off to OverflowBubble.
+    generator.PressLeftButton();
+    gfx::Point rip_off_point(center_point_of_drag_item.x(), 0);
+    generator.MoveMouseTo(rip_off_point);
+    test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
+    ASSERT_TRUE(test_api_for_overflow.IsDraggingShelfItem());
+    ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
+
+    // Move a dragged item into Shelf at |drop_index|.
+    int drop_index = 1;
+    gfx::Point drop_point =
+        test_api_->GetButton(drop_index)->GetBoundsInScreen().CenterPoint();
+    int item_width = test_api_for_overflow.GetButtonSize();
+    // To insert at |drop_index|, more smaller x-axis value of |drop_point|
+    // should be used.
+    gfx::Point modified_drop_point(drop_point.x() - item_width / 4,
+                                   drop_point.y());
+    generator.MoveMouseTo(modified_drop_point);
+    test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
+    test_api_->RunMessageLoopUntilAnimationsDone();
+    ASSERT_TRUE(test_api_for_overflow.IsDraggingShelfItem());
+    ASSERT_TRUE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
+
+    if (cancel)
+      drag_button->OnMouseCaptureLost();
+    else
+      generator.ReleaseLeftButton();
+
+    test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
+    test_api_->RunMessageLoopUntilAnimationsDone();
+    ASSERT_FALSE(test_api_for_overflow.IsDraggingShelfItem());
+    ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
+
+    // Compare pre-stored items' id with newly positioned items' after dragging
+    // is canceled or finished.
+    if (cancel) {
+      EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex()].id,
+          last_visible_item_id_in_shelf);
+      EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex() - 1].id,
+          second_last_visible_item_id_in_shelf);
+      EXPECT_EQ(
+          model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id,
+          first_visible_item_id_in_overflow);
+      EXPECT_EQ(
+          model_->items()[test_api_for_overflow.GetLastVisibleIndex() - 1].id,
+          second_last_visible_item_id_in_overflow);
+    } else {
+      LauncherID drop_item_id = model_->items()[drop_index].id;
+      EXPECT_EQ(drop_item_id, drag_item_id);
+      EXPECT_EQ(model_->item_count(), total_item_count);
+      EXPECT_EQ(
+          model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id,
+          last_visible_item_id_in_shelf);
+      EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex()].id,
+          second_last_visible_item_id_in_shelf);
+      EXPECT_EQ(
+          model_->items()[test_api_for_overflow.GetFirstVisibleIndex() + 1].id,
+          first_visible_item_id_in_overflow);
+      EXPECT_EQ(model_->items()[test_api_for_overflow.GetLastVisibleIndex()].id,
+          second_last_visible_item_id_in_overflow);
+    }
+  }
+
   ShelfModel* model_;
   internal::ShelfView* shelf_view_;
   int browser_index_;
-  LauncherItemDelegateManager* item_manager_;
+  ShelfItemDelegateManager* item_manager_;
 
   scoped_ptr<ShelfViewTestAPI> test_api_;
 
@@ -1018,10 +1155,10 @@
   int index = model_->ItemIndexByID(last_added);
   internal::ShelfButton* button = GetButtonByID(last_added);
   ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state());
-  item.status = ash::STATUS_ACTIVE;
+  item.status = STATUS_ACTIVE;
   model_->Set(index, item);
   ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state());
-  item.status = ash::STATUS_ATTENTION;
+  item.status = STATUS_ATTENTION;
   model_->Set(index, item);
   ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state());
 }
@@ -1071,10 +1208,10 @@
   int index = model_->ItemIndexByID(last_added);
   internal::ShelfButton* button = GetButtonByID(last_added);
   ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state());
-  item.status = ash::STATUS_ACTIVE;
+  item.status = STATUS_ACTIVE;
   model_->Set(index, item);
   ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state());
-  item.status = ash::STATUS_ATTENTION;
+  item.status = STATUS_ATTENTION;
   model_->Set(index, item);
   ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state());
 }
@@ -1160,9 +1297,8 @@
   EXPECT_FALSE(tooltip_manager->IsVisible());
 
   // Change the shelf layout. This should not crash.
-  ash::Shell::GetInstance()->SetShelfAlignment(
-      ash::SHELF_ALIGNMENT_LEFT,
-      ash::Shell::GetPrimaryRootWindow());
+  Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT,
+                                          Shell::GetPrimaryRootWindow());
 }
 
 // Changing the shelf alignment closes any open tooltip.
@@ -1181,9 +1317,8 @@
   EXPECT_TRUE(tooltip_manager->IsVisible());
 
   // Changing shelf alignment hides the tooltip.
-  ash::Shell::GetInstance()->SetShelfAlignment(
-      ash::SHELF_ALIGNMENT_LEFT,
-      ash::Shell::GetPrimaryRootWindow());
+  Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT,
+                                          Shell::GetPrimaryRootWindow());
   EXPECT_FALSE(tooltip_manager->IsVisible());
 }
 
@@ -1339,29 +1474,23 @@
 
 // Checks the overflow bubble size when an item is ripped off and re-inserted.
 TEST_F(ShelfViewTest, OverflowBubbleSize) {
-  // Replace LauncherDelegate.
+  // Replace ShelfDelegate.
   test::ShellTestApi test_api(Shell::GetInstance());
-  test_api.SetLauncherDelegate(NULL);
-  LauncherDelegate *delegate = new TestLauncherDelegateForShelfView(model_);
-  test_api.SetLauncherDelegate(delegate);
+  test_api.SetShelfDelegate(NULL);
+  ShelfDelegate *delegate = new TestShelfDelegateForShelfView(model_);
+  test_api.SetShelfDelegate(delegate);
   test::LauncherTestAPI(
-      Launcher::ForPrimaryDisplay()).SetLauncherDelegate(delegate);
-  test_api_->SetLauncherDelegate(delegate);
+      Launcher::ForPrimaryDisplay()).SetShelfDelegate(delegate);
+  test_api_->SetShelfDelegate(delegate);
 
-  // Add buttons until overflow.
-  int items_added = 0;
-  while (!test_api_->IsOverflowButtonVisible()) {
-    AddAppShortcut();
-    ++items_added;
-    ASSERT_LT(items_added, 10000);
-  }
+  AddButtonsUntilOverflow();
 
   // Show overflow bubble.
   test_api_->ShowOverflowBubble();
   ASSERT_TRUE(test_api_->overflow_bubble() &&
               test_api_->overflow_bubble()->IsShowing());
 
-  ash::test::ShelfViewTestAPI test_for_overflow_view(
+  ShelfViewTestAPI test_for_overflow_view(
       test_api_->overflow_bubble()->shelf_view());
 
   int ripped_index = test_for_overflow_view.GetLastVisibleIndex();
@@ -1369,9 +1498,9 @@
   int item_width = test_for_overflow_view.GetButtonSize() +
       test_for_overflow_view.GetButtonSpacing();
 
-  aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(),
+  aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
                                        gfx::Point());
-  ash::internal::ShelfButton* button =
+  internal::ShelfButton* button =
       test_for_overflow_view.GetButton(ripped_index);
   // Rip off the last visible item.
   gfx::Point start_point = button->GetBoundsInScreen().CenterPoint();
@@ -1423,13 +1552,7 @@
 
   EXPECT_EQ(2, model_->item_count());
 
-  // Add buttons until overflow.
-  int items_added = 0;
-  while (!test_api_->IsOverflowButtonVisible()) {
-    AddAppShortcut();
-    ++items_added;
-    ASSERT_LT(items_added, 10000);
-  }
+  AddButtonsUntilOverflow();
 
   // Show overflow bubble.
   test_api_->ShowOverflowBubble();
@@ -1451,14 +1574,14 @@
   ASSERT_TRUE(test_api_->overflow_bubble() &&
               test_api_->overflow_bubble()->IsShowing());
 
-  ash::test::ShelfViewTestAPI test_for_overflow_view(
+  ShelfViewTestAPI test_for_overflow_view(
       test_api_->overflow_bubble()->shelf_view());
   int first_index = test_for_overflow_view.GetFirstVisibleIndex();
   int last_index = test_for_overflow_view.GetLastVisibleIndex();
 
-  ash::internal::ShelfButton* first_button =
+  internal::ShelfButton* first_button =
       test_for_overflow_view.GetButton(first_index);
-  ash::internal::ShelfButton* last_button =
+  internal::ShelfButton* last_button =
       test_for_overflow_view.GetButton(last_index);
   gfx::Point first_point = first_button->GetBoundsInScreen().CenterPoint();
   gfx::Point last_point = last_button->GetBoundsInScreen().CenterPoint();
@@ -1485,7 +1608,7 @@
 
   UpdateDisplay("800x600,800x600");
   Launcher* secondary_launcher =
-      Launcher::ForWindow(ash::Shell::GetAllRootWindows()[1]);
+      Launcher::ForWindow(Shell::GetAllRootWindows()[1]);
   internal::ShelfView* shelf_view_for_secondary =
       test::LauncherTestAPI(secondary_launcher).shelf_view();
 
@@ -1497,13 +1620,7 @@
   // Speeds up animation for test.
   test_api_for_secondary.SetAnimationDuration(1);
 
-  // Add buttons until overflow.
-  int items_added = 0;
-  while (!test_api_->IsOverflowButtonVisible()) {
-    AddAppShortcut();
-    ++items_added;
-    ASSERT_LT(items_added, 10000);
-  }
+  AddButtonsUntilOverflow();
 
   // Test #1: Test drag insertion bounds of primary shelf.
   // Show overflow bubble.
@@ -1511,10 +1628,10 @@
   ASSERT_TRUE(test_api_->overflow_bubble() &&
               test_api_->overflow_bubble()->IsShowing());
 
-  ash::test::ShelfViewTestAPI test_api_for_overflow_view(
+  ShelfViewTestAPI test_api_for_overflow_view(
       test_api_->overflow_bubble()->shelf_view());
 
-  ash::internal::ShelfButton* button = test_api_for_overflow_view.GetButton(
+  internal::ShelfButton* button = test_api_for_overflow_view.GetButton(
       test_api_for_overflow_view.GetLastVisibleIndex());
 
   // Checks that a point in shelf is contained in drag insert bounds.
@@ -1532,10 +1649,10 @@
   ASSERT_TRUE(test_api_for_secondary.overflow_bubble() &&
               test_api_for_secondary.overflow_bubble()->IsShowing());
 
-  ash::test::ShelfViewTestAPI test_api_for_overflow_view_of_secondary(
+  ShelfViewTestAPI test_api_for_overflow_view_of_secondary(
       test_api_for_secondary.overflow_bubble()->shelf_view());
 
-  ash::internal::ShelfButton* button_in_secondary =
+  internal::ShelfButton* button_in_secondary =
       test_api_for_overflow_view_of_secondary.GetButton(
           test_api_for_overflow_view_of_secondary.GetLastVisibleIndex());
 
@@ -1554,6 +1671,23 @@
   EXPECT_FALSE(drag_reinsert_bounds_in_secondary.Contains(point_in_shelf_view));
 }
 
+// Checks various drag and drop operations from OverflowBubble to Shelf.
+TEST_F(ShelfViewTest, CheckDragAndDropFromOverflowBubbleToShelf) {
+  // Replace LauncherDelegate.
+  test::ShellTestApi test_api(Shell::GetInstance());
+  test_api.SetShelfDelegate(NULL);
+  ShelfDelegate *delegate = new TestShelfDelegateForShelfView(model_);
+  test_api.SetShelfDelegate(delegate);
+  test::LauncherTestAPI(
+      Launcher::ForPrimaryDisplay()).SetShelfDelegate(delegate);
+  test_api_->SetShelfDelegate(delegate);
+
+  AddButtonsUntilOverflow();
+
+  TestDraggingAnItemFromOverflowToShelf(false);
+  TestDraggingAnItemFromOverflowToShelf(true);
+}
+
 class ShelfViewVisibleBoundsTest : public ShelfViewTest,
                                    public testing::WithParamInterface<bool> {
  public:
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 8dc737a..d483c07 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -6,9 +6,9 @@
 
 #include "ash/ash_switches.h"
 #include "ash/focus_cycler.h"
-#include "ash/launcher/launcher_delegate.h"
 #include "ash/root_window_controller.h"
 #include "ash/session_state_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_navigator.h"
@@ -46,7 +46,6 @@
 // The time to dim and un-dim.
 const int kTimeToDimMs = 3000;  // Slow in dimming.
 const int kTimeToUnDimMs = 200;  // Fast in activating.
-const int kTimeToSwitchBackgroundMs = 1000;
 
 // Class used to slightly dim shelf items when maximized and visible.
 class DimmerView : public views::View,
@@ -145,7 +144,7 @@
   // Make sure it is undimmed at the beginning and then fire off the dimming
   // animation.
   background_animator_.SetPaintsBackground(false,
-      ash::internal::BackgroundAnimator::CHANGE_IMMEDIATE);
+                                           ash::BACKGROUND_CHANGE_IMMEDIATE);
   SetHovered(false);
 }
 
@@ -161,8 +160,7 @@
   background_animator_.SetDuration(hovered ? kTimeToUnDimMs : kTimeToDimMs);
   background_animator_.SetPaintsBackground(!hovered,
       disable_dimming_animations_for_test_ ?
-          ash::internal::BackgroundAnimator::CHANGE_IMMEDIATE :
-          ash::internal::BackgroundAnimator::CHANGE_ANIMATE);
+          ash::BACKGROUND_CHANGE_IMMEDIATE : ash::BACKGROUND_CHANGE_ANIMATE);
 }
 
 void DimmerView::ForceUndimming(bool force) {
@@ -391,22 +389,55 @@
             SkBitmapOperations::ROTATION_90_CW,
             SkBitmapOperations::ROTATION_270_CW,
             SkBitmapOperations::ROTATION_180_CW));
-
+  const gfx::Rect dock_bounds(shelf_->shelf_layout_manager()->dock_bounds());
+  SkPaint paint;
+  paint.setAlpha(alpha_);
+  canvas->DrawImageInt(
+      launcher_background,
+      0, 0, launcher_background.width(), launcher_background.height(),
+      (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
+       dock_bounds.x() == 0 && dock_bounds.width() > 0) ?
+           dock_bounds.width() : 0, 0,
+      SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() ?
+          width() - dock_bounds.width() : width(), height(),
+      false,
+      paint);
+  if (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
+      dock_bounds.width() > 0) {
+    // The part of the shelf background that is in the corner below the docked
+    // windows close to the work area is an arched gradient that blends
+    // vertically oriented docked background and horizontal shelf.
+    gfx::ImageSkia launcher_corner =
+        *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_CORNER);
+    if (dock_bounds.x() == 0) {
+      launcher_corner = gfx::ImageSkiaOperations::CreateRotatedImage(
+          launcher_corner, SkBitmapOperations::ROTATION_90_CW);
+    }
+    canvas->DrawImageInt(
+        launcher_corner,
+        0, 0, launcher_corner.width(), launcher_corner.height(),
+        dock_bounds.x() > 0 ? dock_bounds.x() : dock_bounds.width() - height(),
+        0,
+        height(), height(),
+        false,
+        paint);
+    // The part of the shelf background that is just below the docked windows
+    // is drawn using the last (lowest) 1-pixel tall strip of the image asset.
+    // This avoids showing the border 3D shadow between the shelf and the dock.
+    canvas->DrawImageInt(
+        launcher_background,
+        0, launcher_background.height() - 1, launcher_background.width(), 1,
+        dock_bounds.x() > 0 ? dock_bounds.x() + height() : 0, 0,
+        dock_bounds.width() - height(), height(),
+        false,
+        paint);
+  }
   gfx::Rect black_rect =
       shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
           gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels),
           gfx::Rect(0, 0, kNumBlackPixels, height()),
           gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()),
           gfx::Rect(0, 0, width(), kNumBlackPixels));
-
-  SkPaint paint;
-  paint.setAlpha(alpha_);
-  canvas->DrawImageInt(
-      launcher_background,
-      0, 0, launcher_background.width(), launcher_background.height(),
-      0, 0, width(), height(),
-      false,
-      paint);
   canvas->FillRect(black_rect, SK_ColorBLACK);
 }
 
@@ -521,12 +552,12 @@
 
 void ShelfWidget::SetPaintsBackground(
     ShelfBackgroundType background_type,
-    internal::BackgroundAnimator::ChangeType change_type) {
+    BackgroundAnimatorChangeType change_type) {
   ui::Layer* opaque_background = delegate_view_->opaque_background();
   float target_opacity =
       (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
   scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
-  if (change_type != internal::BackgroundAnimator::CHANGE_IMMEDIATE) {
+  if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
     opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
         opaque_background->GetAnimator()));
     opaque_background_animation->SetTransitionDuration(
@@ -536,9 +567,11 @@
 
   // TODO(mukai): use ui::Layer on both opaque_background and normal background
   // retire background_animator_ at all. It would be simpler.
+  // See also DockedBackgroundWidget::SetPaintsBackground.
   background_animator_.SetPaintsBackground(
       background_type != SHELF_BACKGROUND_DEFAULT,
       change_type);
+  delegate_view_->SchedulePaint();
 }
 
 ShelfBackgroundType ShelfWidget::GetBackgroundType() const {
@@ -605,12 +638,12 @@
 
   Shell* shell = Shell::GetInstance();
   // This needs to be called before shelf_model().
-  LauncherDelegate* launcher_delegate = shell->GetLauncherDelegate();
-  if (!launcher_delegate)
+  ShelfDelegate* shelf_delegate = shell->GetShelfDelegate();
+  if (!shelf_delegate)
     return;  // Not ready to create Launcher
 
   launcher_.reset(new Launcher(shell->shelf_model(),
-                               shell->GetLauncherDelegate(),
+                               shell->GetShelfDelegate(),
                                this));
   SetFocusCycler(shell->focus_cycler());
 
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index c7adb74..83d4a8d 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -43,12 +43,11 @@
   ShelfAlignment GetAlignment() const;
 
   // Sets the shelf's background type.
-  void SetPaintsBackground(
-      ShelfBackgroundType background_type,
-      internal::BackgroundAnimator::ChangeType change_type);
+  void SetPaintsBackground(ShelfBackgroundType background_type,
+                           BackgroundAnimatorChangeType change_type);
   ShelfBackgroundType GetBackgroundType() const;
 
-  // Causes shelf items to be slightly dimmed (eg when a window is maximized).
+  // Causes shelf items to be slightly dimmed (e.g. when a window is maximized).
   void SetDimsShelf(bool dimming);
   bool GetDimsShelf() const;
 
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
new file mode 100644
index 0000000..f177e48
--- /dev/null
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -0,0 +1,214 @@
+// Copyright 2013 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.
+
+#include "ash/shelf/shelf_window_watcher.h"
+
+#include "ash/display/display_controller.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shelf/shelf_util.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/activation_client.h"
+#include "ui/aura/window.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/screen.h"
+
+namespace {
+
+// Sets LauncherItem property by using the value of |details|.
+void SetLauncherItemDetailsForLauncherItem(
+    ash::LauncherItem* item,
+    const ash::LauncherItemDetails& details) {
+  item->type = details.type;
+  if (details.image_resource_id != ash::kInvalidImageResourceID) {
+    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+    item->image = *rb.GetImageSkiaNamed(details.image_resource_id);
+  }
+}
+
+// Returns true if |window| has a LauncherItem added by ShelfWindowWatcher.
+bool HasLauncherItemForWindow(aura::Window* window) {
+  if (ash::GetLauncherItemDetailsForWindow(window) != NULL &&
+      ash::GetLauncherIDForWindow(window) != ash::kInvalidLauncherID)
+    return true;
+  return false;
+}
+
+}  // namespace
+
+namespace ash {
+namespace internal {
+
+ShelfWindowWatcher::RootWindowObserver::RootWindowObserver(
+    ShelfWindowWatcher* window_watcher)
+    : window_watcher_(window_watcher) {
+}
+
+ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() {
+}
+
+void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying(
+    aura::Window* window) {
+  window_watcher_->OnRootWindowRemoved(window);
+}
+
+ShelfWindowWatcher::ShelfWindowWatcher(ShelfModel* model)
+    : model_(model),
+      root_window_observer_(this),
+      observed_windows_(this),
+      observed_root_windows_(&root_window_observer_),
+      observed_activation_clients_(this) {
+  // We can't assume all RootWindows have the same ActivationClient.
+  // Add a RootWindow and its ActivationClient to the observed list.
+  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+  for (aura::Window::Windows::const_iterator it = root_windows.begin();
+       it != root_windows.end(); ++it)
+    OnRootWindowAdded(*it);
+
+  Shell::GetScreen()->AddObserver(this);
+}
+
+ShelfWindowWatcher::~ShelfWindowWatcher() {
+  Shell::GetScreen()->RemoveObserver(this);
+}
+
+void ShelfWindowWatcher::AddLauncherItem(aura::Window* window) {
+  const LauncherItemDetails* item_details =
+      GetLauncherItemDetailsForWindow(window);
+  LauncherItem item;
+  item.status = ash::wm::IsActiveWindow(window) ? STATUS_ACTIVE: STATUS_RUNNING;
+  SetLauncherItemDetailsForLauncherItem(&item, *item_details);
+  SetLauncherIDForWindow(model_->next_id(), window);
+  // TODO(simonhong): Create LauncherItemDelegate for LauncherItem.
+  model_->Add(item);
+}
+
+void ShelfWindowWatcher::RemoveLauncherItem(aura::Window* window) {
+  model_->RemoveItemAt(model_->ItemIndexByID(GetLauncherIDForWindow(window)));
+  SetLauncherIDForWindow(kInvalidLauncherID, window);
+}
+
+void ShelfWindowWatcher::OnRootWindowAdded(aura::Window* root_window) {
+  // |observed_activation_clients_| can have the same ActivationClient multiple
+  // times - which would be handled by the |observed_activation_clients_|.
+  observed_activation_clients_.Add(
+      aura::client::GetActivationClient(root_window));
+  observed_root_windows_.Add(root_window);
+
+  aura::Window* default_container = Shell::GetContainer(
+      root_window,
+      kShellWindowId_DefaultContainer);
+  observed_windows_.Add(default_container);
+  for (size_t i = 0; i < default_container->children().size(); ++i)
+    observed_windows_.Add(default_container->children()[i]);
+}
+
+void ShelfWindowWatcher::OnRootWindowRemoved(aura::Window* root_window) {
+  observed_root_windows_.Remove(root_window);
+  observed_activation_clients_.Remove(
+      aura::client::GetActivationClient(root_window));
+}
+
+void ShelfWindowWatcher::UpdateLauncherItemStatus(aura::Window* window,
+                                                  bool is_active) {
+  int index = GetLauncherItemIndexForWindow(window);
+  DCHECK_GE(index, 0);
+
+  LauncherItem item = model_->items()[index];
+  item.status = is_active ? STATUS_ACTIVE : STATUS_RUNNING;
+  model_->Set(index, item);
+}
+
+int ShelfWindowWatcher::GetLauncherItemIndexForWindow(
+    aura::Window* window) const {
+  return model_->ItemIndexByID(GetLauncherIDForWindow(window));
+}
+
+void ShelfWindowWatcher::OnWindowActivated(aura::Window* gained_active,
+                                           aura::Window* lost_active) {
+  if (gained_active && HasLauncherItemForWindow(gained_active))
+    UpdateLauncherItemStatus(gained_active, true);
+  if (lost_active && HasLauncherItemForWindow(lost_active))
+    UpdateLauncherItemStatus(lost_active, false);
+}
+
+void ShelfWindowWatcher::OnWindowAdded(aura::Window* window) {
+  observed_windows_.Add(window);
+  // Add LauncherItem if |window| already has a LauncherItemDetails when it is
+  // created. Don't make a new LauncherItem for the re-parented |window| that
+  // already has a LauncherItem.
+  if (GetLauncherIDForWindow(window) == ash::kInvalidLauncherID &&
+      GetLauncherItemDetailsForWindow(window))
+    AddLauncherItem(window);
+}
+
+void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window* window) {
+  // Remove a child window of default container and its item if it has.
+  if (observed_windows_.IsObserving(window))
+    observed_windows_.Remove(window);
+
+  if (HasLauncherItemForWindow(window))
+    RemoveLauncherItem(window);
+}
+
+void ShelfWindowWatcher::OnWindowDestroying(aura::Window* window) {
+  // Remove the default container.
+  if (observed_windows_.IsObserving(window))
+    observed_windows_.Remove(window);
+}
+
+void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window* window,
+                                                 const void* key,
+                                                 intptr_t old) {
+  if (key != kLauncherItemDetailsKey)
+    return;
+
+  if (GetLauncherItemDetailsForWindow(window) == NULL) {
+    // Removes LauncherItem for |window| when it has a LauncherItem.
+    if (reinterpret_cast<LauncherItemDetails*>(old) != NULL)
+      RemoveLauncherItem(window);
+    return;
+  }
+
+  // When LauncherItemDetails is changed, update LauncherItem.
+  if (HasLauncherItemForWindow(window)) {
+    int index = GetLauncherItemIndexForWindow(window);
+    DCHECK_GE(index, 0);
+    LauncherItem item = model_->items()[index];
+    const LauncherItemDetails* details =
+        GetLauncherItemDetailsForWindow(window);
+    SetLauncherItemDetailsForLauncherItem(&item, *details);
+    model_->Set(index, item);
+    return;
+  }
+
+  // Creates a new LauncherItem for |window|.
+  AddLauncherItem(window);
+}
+
+void ShelfWindowWatcher::OnDisplayBoundsChanged(const gfx::Display& display) {
+}
+
+void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display& new_display) {
+  // Add a new RootWindow and its ActivationClient to observed list.
+  aura::Window* root_window = Shell::GetInstance()->display_controller()->
+      GetRootWindowForDisplayId(new_display.id());
+
+  // When the primary root window's display get removed, the existing root
+  // window is taken over by the new display and the observer is already set.
+  if (!observed_root_windows_.IsObserving(root_window))
+    OnRootWindowAdded(root_window);
+}
+
+void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display& old_display) {
+  // When this is called, RootWindow of |old_display| is already removed.
+  // Instead, we remove an observer from RootWindow and ActivationClient in the
+  // OnRootWindowDestroyed().
+  // Do nothing here.
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/shelf/shelf_window_watcher.h b/ash/shelf/shelf_window_watcher.h
new file mode 100644
index 0000000..d63c658
--- /dev/null
+++ b/ash/shelf/shelf_window_watcher.h
@@ -0,0 +1,114 @@
+// Copyright 2013 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 ASH_SHELF_SHELF_WINDOW_WATCHER_H_
+#define ASH_SHELF_SHELF_WINDOW_WATCHER_H_
+
+#include "ash/shelf/scoped_observer_with_duplicated_sources.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/scoped_observer.h"
+#include "ui/aura/client/activation_change_observer.h"
+#include "ui/aura/window_observer.h"
+#include "ui/gfx/display_observer.h"
+
+namespace aura {
+
+class Window;
+
+namespace client {
+class ActivationClient;
+}
+
+}  // namespace aura
+
+namespace ash {
+
+class ShelfModel;
+
+namespace internal {
+
+// ShelfWindowWatcher creates and handles a LauncherItem for windows that have
+// a LauncherItemDetails property in the default container.
+class ShelfWindowWatcher : public aura::client::ActivationChangeObserver,
+                           public aura::WindowObserver,
+                           public gfx::DisplayObserver {
+ public:
+  ShelfWindowWatcher(ShelfModel* model);
+  virtual ~ShelfWindowWatcher();
+
+ private:
+  class RootWindowObserver : public aura::WindowObserver {
+   public:
+    explicit RootWindowObserver(ShelfWindowWatcher* window_watcher);
+    virtual ~RootWindowObserver();
+
+   private:
+    // aura::WindowObserver overrides:
+    virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+
+    // Owned by Shell.
+    ShelfWindowWatcher* window_watcher_;
+
+    DISALLOW_COPY_AND_ASSIGN(RootWindowObserver);
+  };
+
+  // Creates a LauncherItem for |window| that has LauncherItemDetails.
+  void AddLauncherItem(aura::Window* window);
+
+  // Removes a LauncherItem for |window|.
+  void RemoveLauncherItem(aura::Window* window);
+
+  // Adds observer to default container and ActivationClient of |root_window|.
+  void OnRootWindowAdded(aura::Window* root_window);
+
+  // Removes observer from ActivationClient of |root_window|.
+  void OnRootWindowRemoved(aura::Window* root_window);
+
+  // Updates the status of LauncherItem for |window|.
+  void UpdateLauncherItemStatus(aura::Window* window, bool is_active);
+
+  // Returns the index of LauncherItem associated with |window|.
+  int GetLauncherItemIndexForWindow(aura::Window* window) const;
+
+  // aura::client::ActivationChangeObserver overrides:
+  virtual void OnWindowActivated(aura::Window* gained_active,
+                                 aura::Window* lost_active) OVERRIDE;
+
+  // aura::WindowObserver overrides:
+  virtual void OnWindowAdded(aura::Window* window) OVERRIDE;
+  virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
+  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+  virtual void OnWindowPropertyChanged(aura::Window* window,
+                                       const void* key,
+                                       intptr_t old) OVERRIDE;
+
+  // gfx::DisplayObserver overrides:
+  virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
+  virtual void OnDisplayAdded(const gfx::Display& display) OVERRIDE;
+  virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
+
+  // Owned by Shell.
+  ShelfModel* model_;
+
+  RootWindowObserver root_window_observer_;
+
+  // Holds all observed windows.
+  ScopedObserver<aura::Window, aura::WindowObserver> observed_windows_;
+
+  // Holds all observed root windows.
+  ScopedObserver<aura::Window, aura::WindowObserver> observed_root_windows_;
+
+  // Holds all observed activation clients.
+  ScopedObserverWithDuplicatedSources<aura::client::ActivationClient,
+      aura::client::ActivationChangeObserver> observed_activation_clients_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcher);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_SHELF_SHELF_WINDOW_WATCHER_H_
diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc
new file mode 100644
index 0000000..1217aae
--- /dev/null
+++ b/ash/shelf/shelf_window_watcher_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2013 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.
+
+#include "ash/shelf/shelf_window_watcher.h"
+
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shelf/shelf_util.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test/shell_test_api.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace internal {
+
+class ShelfWindowWatcherTest : public test::AshTestBase {
+ public:
+  ShelfWindowWatcherTest() : model_(NULL) {}
+  virtual ~ShelfWindowWatcherTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    test::AshTestBase::SetUp();
+    model_ = test::ShellTestApi(Shell::GetInstance()).shelf_model();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    model_ = NULL;
+    test::AshTestBase::TearDown();
+  }
+
+  ash::LauncherID CreateLauncherItem(aura::Window* window) {
+    LauncherID id = model_->next_id();
+    ash::LauncherItemDetails item_details;
+    item_details.type = TYPE_PLATFORM_APP;
+    SetLauncherItemDetailsForWindow(window, item_details);
+    return id;
+  }
+
+  void UpdateLauncherItem(aura::Window* window) {
+  }
+
+ protected:
+  ShelfModel* model_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcherTest);
+};
+
+TEST_F(ShelfWindowWatcherTest, CreateAndRemoveLauncherItem) {
+  // ShelfModel only has an APP_LIST item.
+  EXPECT_EQ(1, model_->item_count());
+
+  scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0));
+  scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0));
+
+  // Create a LauncherItem for w1.
+  LauncherID id_w1 = CreateLauncherItem(w1.get());
+  EXPECT_EQ(2, model_->item_count());
+
+  int index_w1 = model_->ItemIndexByID(id_w1);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+
+  // Create a LauncherItem for w2.
+  LauncherID id_w2 = CreateLauncherItem(w2.get());
+  EXPECT_EQ(3, model_->item_count());
+
+  int index_w2 = model_->ItemIndexByID(id_w2);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status);
+
+  // LauncherItem is removed when assoicated window is destroyed.
+  ClearLauncherItemDetailsForWindow(w1.get());
+  EXPECT_EQ(2, model_->item_count());
+  ClearLauncherItemDetailsForWindow(w2.get());
+  EXPECT_EQ(1, model_->item_count());
+  // Clears twice doesn't do anything.
+  ClearLauncherItemDetailsForWindow(w2.get());
+  EXPECT_EQ(1, model_->item_count());
+
+}
+
+TEST_F(ShelfWindowWatcherTest, ActivateWindow) {
+  // ShelfModel only have APP_LIST item.
+  EXPECT_EQ(1, model_->item_count());
+  scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0));
+  scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0));
+
+  // Create a LauncherItem for w1.
+  LauncherID id_w1 = CreateLauncherItem(w1.get());
+  EXPECT_EQ(2, model_->item_count());
+  int index_w1 = model_->ItemIndexByID(id_w1);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+
+  // Create a LauncherItem for w2.
+  LauncherID id_w2 = CreateLauncherItem(w2.get());
+  EXPECT_EQ(3, model_->item_count());
+  int index_w2 = model_->ItemIndexByID(id_w2);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status);
+
+  // LauncherItem for w1 is active when w1 is activated.
+  wm::ActivateWindow(w1.get());
+  EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w1].status);
+
+  // LauncherItem for w2 is active state when w2 is activated.
+  wm::ActivateWindow(w2.get());
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+  EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w2].status);
+}
+
+TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
+  // ShelfModel only has an APP_LIST item.
+  EXPECT_EQ(1, model_->item_count());
+
+  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
+
+  // Create a LauncherItem for |window|.
+  LauncherID id = CreateLauncherItem(window.get());
+  EXPECT_EQ(2, model_->item_count());
+
+  int index = model_->ItemIndexByID(id);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);
+
+  // Update LauncherItem for |window|.
+  LauncherItemDetails details;
+  details.type = TYPE_PLATFORM_APP;
+
+  SetLauncherItemDetailsForWindow(window.get(), details);
+  // No new item is created after updating a launcher item.
+  EXPECT_EQ(2, model_->item_count());
+  // index and id are not changed after updating a launcher item.
+  EXPECT_EQ(index, model_->ItemIndexByID(id));
+  EXPECT_EQ(id, model_->items()[index].id);
+}
+
+TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
+  // ShelfModel only has an APP_LIST item.
+  EXPECT_EQ(1, model_->item_count());
+
+  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
+  wm::WindowState* window_state = wm::GetWindowState(window.get());
+
+  // Create a LauncherItem for |window|.
+  LauncherID id = CreateLauncherItem(window.get());
+  EXPECT_EQ(2, model_->item_count());
+
+  int index = model_->ItemIndexByID(id);
+  EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);
+
+  // Maximize window |window|.
+  EXPECT_FALSE(window_state->IsMaximized());
+  window_state->Maximize();
+  EXPECT_TRUE(window_state->IsMaximized());
+  // No new item is created after maximizing a window |window|.
+  EXPECT_EQ(2, model_->item_count());
+  // index and id are not changed after maximizing a window |window|.
+  EXPECT_EQ(index, model_->ItemIndexByID(id));
+  EXPECT_EQ(id, model_->items()[index].id);
+
+  // Restore window |window|.
+  window_state->Restore();
+  EXPECT_FALSE(window_state->IsMaximized());
+  // No new item is created after restoring a window |window|.
+  EXPECT_EQ(2, model_->item_count());
+  // index and id are not changed after maximizing a window |window|.
+  EXPECT_EQ(index, model_->ItemIndexByID(id));
+  EXPECT_EQ(id, model_->items()[index].id);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/shell.cc b/ash/shell.cc
index d5428cf..4cd9fd9 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -30,9 +30,6 @@
 #include "ash/high_contrast/high_contrast_controller.h"
 #include "ash/host/root_window_host_factory.h"
 #include "ash/keyboard_uma_event_filter.h"
-#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
 #include "ash/magnifier/magnification_controller.h"
 #include "ash/magnifier/partial_magnification_controller.h"
 #include "ash/media_delegate.h"
@@ -41,9 +38,13 @@
 #include "ash/screen_ash.h"
 #include "ash/session_state_delegate.h"
 #include "ash/shelf/app_list_shelf_item_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_widget.h"
+#include "ash/shelf/shelf_window_watcher.h"
 #include "ash/shell_delegate.h"
 #include "ash/shell_factory.h"
 #include "ash/shell_window_ids.h"
@@ -91,6 +92,7 @@
 #include "ui/base/ui_base_switches.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
+#include "ui/events/event_target_iterator.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/screen.h"
@@ -496,29 +498,29 @@
   return GetPrimaryRootWindowController()->GetSystemTray();
 }
 
-LauncherDelegate* Shell::GetLauncherDelegate() {
-  if (!launcher_delegate_) {
+ShelfDelegate* Shell::GetShelfDelegate() {
+  if (!shelf_delegate_) {
     shelf_model_.reset(new ShelfModel);
-    // Creates LauncherItemDelegateManager before LauncherDelegate.
-    launcher_item_delegate_manager_.reset(
-        new LauncherItemDelegateManager(shelf_model_.get()));
+    // Creates ShelfItemDelegateManager before ShelfDelegate.
+    shelf_item_delegate_manager_.reset(
+        new ShelfItemDelegateManager(shelf_model_.get()));
 
-    launcher_delegate_.reset(
-        delegate_->CreateLauncherDelegate(shelf_model_.get()));
-    scoped_ptr<LauncherItemDelegate> controller(
+    shelf_delegate_.reset(delegate_->CreateShelfDelegate(shelf_model_.get()));
+    scoped_ptr<ShelfItemDelegate> controller(
         new internal::AppListShelfItemDelegate);
 
     // Finding the shelf model's location of the app list and setting its
-    // LauncherItemDelegate.
+    // ShelfItemDelegate.
     int app_list_index = shelf_model_->GetItemIndexForType(TYPE_APP_LIST);
     DCHECK_GE(app_list_index, 0);
     LauncherID app_list_id = shelf_model_->items()[app_list_index].id;
     DCHECK(app_list_id);
-    launcher_item_delegate_manager_->SetLauncherItemDelegate(
-        app_list_id,
-        controller.Pass());
+    shelf_item_delegate_manager_->SetShelfItemDelegate(app_list_id,
+                                                       controller.Pass());
+    shelf_window_watcher_.reset(
+        new internal::ShelfWindowWatcher(shelf_model_.get()));
   }
-  return launcher_delegate_.get();
+  return shelf_delegate_.get();
 }
 
 void Shell::SetTouchHudProjectionEnabled(bool enabled) {
@@ -629,42 +631,57 @@
   // Drag-and-drop must be canceled prior to close all windows.
   drag_drop_controller_.reset();
 
+  // Controllers who have WindowObserver added must be deleted
+  // before |display_controller_| is deleted.
+
+#if defined(OS_CHROMEOS)
+  // VideoActivityNotifier must be deleted before |video_detector_| is
+  // deleted because it's observing video activity through
+  // VideoDetectorObserver interface.
+  video_activity_notifier_.reset();
+#endif  // defined(OS_CHROMEOS)
+  video_detector_.reset();
+
+  shadow_controller_.reset();
+  resize_shadow_controller_.reset();
+
+  window_cycle_controller_.reset();
+  mru_window_tracker_.reset();
+
+  // |shelf_window_watcher_| has a weak pointer to |shelf_Model_|
+  // and has window observers.
+  shelf_window_watcher_.reset();
+
   // Destroy all child windows including widgets.
   display_controller_->CloseChildWindows();
   display_controller_->CloseNonDesktopDisplay();
 
+  // Chrome implementation of shelf delegate depends on FocusClient,
+  // so must be deleted before |focus_client_|.
+  shelf_delegate_.reset();
+  focus_client_.reset();
+
   // Destroy SystemTrayNotifier after destroying SystemTray as TrayItems
   // needs to remove observers from it.
   system_tray_notifier_.reset();
 
-#if defined(OS_CHROMEOS)
-  // Destroy VideoActivityNotifier before destroying VideoDetector.
-  video_activity_notifier_.reset();
-#endif  // defined(OS_CHROMEOS)
-
   // These need a valid Shell instance to clean up properly, so explicitly
   // delete them before invalidating the instance.
   // Alphabetical. TODO(oshima): sort.
   magnification_controller_.reset();
   partial_magnification_controller_.reset();
-  resize_shadow_controller_.reset();
-  shadow_controller_.reset();
   tooltip_controller_.reset();
   event_client_.reset();
-  window_cycle_controller_.reset();
   nested_dispatcher_controller_.reset();
   user_action_client_.reset();
   visibility_controller_.reset();
-  launcher_delegate_.reset();
-  // |launcher_item_delegate_manager_| observes |shelf_model_|. It must be
+  // |shelf_item_delegate_manager_| observes |shelf_model_|. It must be
   // destroyed before |shelf_model_| is destroyed.
-  launcher_item_delegate_manager_.reset();
+  shelf_item_delegate_manager_.reset();
   shelf_model_.reset();
-  video_detector_.reset();
 
   power_button_controller_.reset();
   lock_state_controller_.reset();
-  mru_window_tracker_.reset();
 
   resolution_notification_controller_.reset();
   desktop_background_controller_.reset();
@@ -996,6 +1013,15 @@
   return aura::Env::GetInstance();
 }
 
+scoped_ptr<ui::EventTargetIterator> Shell::GetChildIterator() const {
+  return scoped_ptr<ui::EventTargetIterator>();
+}
+
+ui::EventTargeter* Shell::GetEventTargeter() {
+  NOTREACHED();
+  return NULL;
+}
+
 void Shell::OnEvent(ui::Event* event) {
 }
 
diff --git a/ash/shell.h b/ash/shell.h
index 1c4e779..5c49f6b 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -85,9 +85,7 @@
 class FirstRunHelper;
 class HighContrastController;
 class Launcher;
-class LauncherDelegate;
-class LauncherItemDelegate;
-class LauncherItemDelegateManager;
+class ShelfDelegate;
 class LockStateController;
 class MagnificationController;
 class MediaDelegate;
@@ -99,6 +97,7 @@
 class RootWindowHostFactory;
 class ScreenAsh;
 class SessionStateDelegate;
+class ShelfItemDelegateManager;
 class ShelfModel;
 class ShellDelegate;
 class ShellObserver;
@@ -137,6 +136,7 @@
 class RootWindowController;
 class ScopedTargetRootWindow;
 class ScreenPositionController;
+class ShelfWindowWatcher;
 class SlowAnimationEventFilter;
 class StatusAreaWidget;
 class SystemGestureEventFilter;
@@ -408,8 +408,8 @@
     return activation_client_;
   }
 
-  LauncherItemDelegateManager* launcher_item_delegate_manager() {
-    return launcher_item_delegate_manager_.get();
+  ShelfItemDelegateManager* shelf_item_delegate_manager() {
+    return shelf_item_delegate_manager_.get();
   }
 
   ScreenAsh* screen() { return screen_; }
@@ -512,7 +512,7 @@
   }
 
   // Returns the launcher delegate, creating if necesary.
-  LauncherDelegate* GetLauncherDelegate();
+  ShelfDelegate* GetShelfDelegate();
 
   void SetTouchHudProjectionEnabled(bool enabled);
 
@@ -559,6 +559,8 @@
   // Overridden from ui::EventTarget:
   virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE;
   virtual EventTarget* GetParentTarget() OVERRIDE;
+  virtual scoped_ptr<ui::EventTargetIterator> GetChildIterator() const OVERRIDE;
+  virtual ui::EventTargeter* GetEventTargeter() OVERRIDE;
   virtual void OnEvent(ui::Event* event) OVERRIDE;
 
   // Overridden from aura::client::ActivationChangeObserver:
@@ -598,8 +600,9 @@
   scoped_ptr<AccessibilityDelegate> accessibility_delegate_;
   scoped_ptr<NewWindowDelegate> new_window_delegate_;
   scoped_ptr<MediaDelegate> media_delegate_;
-  scoped_ptr<LauncherDelegate> launcher_delegate_;
-  scoped_ptr<LauncherItemDelegateManager> launcher_item_delegate_manager_;
+  scoped_ptr<ShelfDelegate> shelf_delegate_;
+  scoped_ptr<ShelfItemDelegateManager> shelf_item_delegate_manager_;
+  scoped_ptr<internal::ShelfWindowWatcher> shelf_window_watcher_;
 
   scoped_ptr<ShelfModel> shelf_model_;
   scoped_ptr<WindowPositioner> window_positioner_;
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index 07f888e..1359826 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -23,6 +23,7 @@
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/search_box_model.h"
 #include "ui/app_list/search_result.h"
+#include "ui/app_list/speech_ui_model.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/image/image_skia.h"
@@ -251,6 +252,10 @@
     return NULL;
   }
 
+  virtual app_list::SpeechUIModel* GetSpeechUI() OVERRIDE {
+    return &speech_ui_;
+  }
+
   virtual void GetShortcutPathForApp(
       const std::string& app_id,
       const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE {
@@ -338,6 +343,7 @@
   }
 
   scoped_ptr<app_list::AppListModel> model_;
+  app_list::SpeechUIModel speech_ui_;
   Users users_;
 
   DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate);
diff --git a/ash/shell/launcher_delegate_impl.cc b/ash/shell/launcher_delegate_impl.cc
deleted file mode 100644
index 9eba738..0000000
--- a/ash/shell/launcher_delegate_impl.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "ash/shell/launcher_delegate_impl.h"
-
-#include "ash/shell.h"
-#include "ash/shell/toplevel_window.h"
-#include "ash/shell/window_watcher.h"
-#include "ash/wm/window_util.h"
-#include "base/strings/string_util.h"
-#include "grit/ash_resources.h"
-
-namespace ash {
-namespace shell {
-
-LauncherDelegateImpl::LauncherDelegateImpl(WindowWatcher* watcher)
-    : watcher_(watcher) {
-}
-
-LauncherDelegateImpl::~LauncherDelegateImpl() {
-}
-
-void LauncherDelegateImpl::OnLauncherCreated(Launcher* launcher) {
-}
-
-void LauncherDelegateImpl::OnLauncherDestroyed(Launcher* launcher) {
-}
-
-LauncherID LauncherDelegateImpl::GetLauncherIDForAppID(
-    const std::string& app_id) {
-  return 0;
-}
-
-const std::string& LauncherDelegateImpl::GetAppIDForLauncherID(LauncherID id) {
-  return EmptyString();
-}
-
-void LauncherDelegateImpl::PinAppWithID(const std::string& app_id) {
-}
-
-bool LauncherDelegateImpl::IsAppPinned(const std::string& app_id) {
-  return false;
-}
-
-bool LauncherDelegateImpl::CanPin() const {
-  return false;
-}
-
-void LauncherDelegateImpl::UnpinAppWithID(const std::string& app_id) {
-}
-
-}  // namespace shell
-}  // namespace ash
diff --git a/ash/shell/shelf_delegate_impl.cc b/ash/shell/shelf_delegate_impl.cc
new file mode 100644
index 0000000..0112009
--- /dev/null
+++ b/ash/shell/shelf_delegate_impl.cc
@@ -0,0 +1,53 @@
+// Copyright 2013 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.
+
+#include "ash/shell/shelf_delegate_impl.h"
+
+#include "ash/shell.h"
+#include "ash/shell/toplevel_window.h"
+#include "ash/shell/window_watcher.h"
+#include "ash/wm/window_util.h"
+#include "base/strings/string_util.h"
+#include "grit/ash_resources.h"
+
+namespace ash {
+namespace shell {
+
+ShelfDelegateImpl::ShelfDelegateImpl(WindowWatcher* watcher)
+    : watcher_(watcher) {
+}
+
+ShelfDelegateImpl::~ShelfDelegateImpl() {
+}
+
+void ShelfDelegateImpl::OnLauncherCreated(Launcher* launcher) {
+}
+
+void ShelfDelegateImpl::OnLauncherDestroyed(Launcher* launcher) {
+}
+
+LauncherID ShelfDelegateImpl::GetLauncherIDForAppID(const std::string& app_id) {
+  return 0;
+}
+
+const std::string& ShelfDelegateImpl::GetAppIDForLauncherID(LauncherID id) {
+  return base::EmptyString();
+}
+
+void ShelfDelegateImpl::PinAppWithID(const std::string& app_id) {
+}
+
+bool ShelfDelegateImpl::IsAppPinned(const std::string& app_id) {
+  return false;
+}
+
+bool ShelfDelegateImpl::CanPin() const {
+  return false;
+}
+
+void ShelfDelegateImpl::UnpinAppWithID(const std::string& app_id) {
+}
+
+}  // namespace shell
+}  // namespace ash
diff --git a/ash/shell/launcher_delegate_impl.h b/ash/shell/shelf_delegate_impl.h
similarity index 66%
rename from ash/shell/launcher_delegate_impl.h
rename to ash/shell/shelf_delegate_impl.h
index c7b54ed..ed6dc6d 100644
--- a/ash/shell/launcher_delegate_impl.h
+++ b/ash/shell/shelf_delegate_impl.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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 ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
-#define ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
+#ifndef ASH_SHELL_SHELF_DELEGATE_IMPL_H_
+#define ASH_SHELL_SHELF_DELEGATE_IMPL_H_
 
-#include "ash/launcher/launcher_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
 #include "base/compiler_specific.h"
 
 namespace aura {
@@ -17,14 +17,14 @@
 
 class WindowWatcher;
 
-class LauncherDelegateImpl : public ash::LauncherDelegate {
+class ShelfDelegateImpl : public ShelfDelegate {
  public:
-  explicit LauncherDelegateImpl(WindowWatcher* watcher);
-  virtual ~LauncherDelegateImpl();
+  explicit ShelfDelegateImpl(WindowWatcher* watcher);
+  virtual ~ShelfDelegateImpl();
 
   void set_watcher(WindowWatcher* watcher) { watcher_ = watcher; }
 
-  // LauncherDelegate overrides:
+  // ShelfDelegate overrides:
   virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
   virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
   virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
@@ -38,10 +38,10 @@
   // Used to update Launcher. Owned by main.
   WindowWatcher* watcher_;
 
-  DISALLOW_COPY_AND_ASSIGN(LauncherDelegateImpl);
+  DISALLOW_COPY_AND_ASSIGN(ShelfDelegateImpl);
 };
 
 }  // namespace shell
 }  // namespace ash
 
-#endif  // ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
+#endif  // ASH_SHELL_SHELF_DELEGATE_IMPL_H_
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index acc8e3d..5dfe7b2 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -16,7 +16,7 @@
 #include "ash/shell/context_menu.h"
 #include "ash/shell/example_factory.h"
 #include "ash/shell/keyboard_controller_proxy_stub.h"
-#include "ash/shell/launcher_delegate_impl.h"
+#include "ash/shell/shelf_delegate_impl.h"
 #include "ash/shell/toplevel_window.h"
 #include "ash/shell_window_ids.h"
 #include "ash/system/tray/default_system_tray_delegate.h"
@@ -69,7 +69,7 @@
 
 ShellDelegateImpl::ShellDelegateImpl()
     : watcher_(NULL),
-      launcher_delegate_(NULL),
+      shelf_delegate_(NULL),
       browser_context_(NULL) {
 }
 
@@ -78,8 +78,8 @@
 
 void ShellDelegateImpl::SetWatcher(WindowWatcher* watcher) {
   watcher_ = watcher;
-  if (launcher_delegate_)
-    launcher_delegate_->set_watcher(watcher);
+  if (shelf_delegate_)
+    shelf_delegate_->set_watcher(watcher);
 }
 
 bool ShellDelegateImpl::IsFirstRunAfterBoot() const {
@@ -121,9 +121,9 @@
   return ash::shell::CreateAppListViewDelegate();
 }
 
-LauncherDelegate* ShellDelegateImpl::CreateLauncherDelegate(ShelfModel* model) {
-  launcher_delegate_ = new LauncherDelegateImpl(watcher_);
-  return launcher_delegate_;
+ShelfDelegate* ShellDelegateImpl::CreateShelfDelegate(ShelfModel* model) {
+  shelf_delegate_ = new ShelfDelegateImpl(watcher_);
+  return shelf_delegate_;
 }
 
 ash::SystemTrayDelegate* ShellDelegateImpl::CreateSystemTrayDelegate() {
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index 500bef2..ecc7ee6 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -21,7 +21,7 @@
 namespace ash {
 namespace shell {
 
-class LauncherDelegateImpl;
+class ShelfDelegateImpl;
 class WindowWatcher;
 
 class ShellDelegateImpl : public ash::ShellDelegate {
@@ -45,7 +45,7 @@
       CreateKeyboardControllerProxy() OVERRIDE;
   virtual content::BrowserContext* GetCurrentBrowserContext() OVERRIDE;
   virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE;
-  virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) OVERRIDE;
+  virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) OVERRIDE;
   virtual ash::SystemTrayDelegate* CreateSystemTrayDelegate() OVERRIDE;
   virtual ash::UserWallpaperDelegate* CreateUserWallpaperDelegate() OVERRIDE;
   virtual ash::CapsLockDelegate* CreateCapsLockDelegate() OVERRIDE;
@@ -64,7 +64,7 @@
   // Used to update Launcher. Owned by main.
   WindowWatcher* watcher_;
 
-  LauncherDelegateImpl* launcher_delegate_;
+  ShelfDelegateImpl* shelf_delegate_;
   content::BrowserContext* browser_context_;
 
   DISALLOW_COPY_AND_ASSIGN(ShellDelegateImpl);
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index e701e25..f90c4f3 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -344,7 +344,8 @@
         ASCIIToUTF16("Notification message body."),
         gfx::Image(),
         ASCIIToUTF16("www.testshell.org"),
-        message_center::NotifierId(),
+        message_center::NotifierId(
+            message_center::NotifierId::APPLICATION, "test-id"),
         message_center::RichNotificationData(),
         NULL /* delegate */));
 
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index 761a9cc..988985a 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -6,12 +6,12 @@
 
 #include "ash/display/display_controller.h"
 #include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
 #include "ash/shelf/shelf_model.h"
 #include "ash/shelf/shelf_util.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
-#include "ash/shell/window_watcher_launcher_item_delegate.h"
+#include "ash/shell/window_watcher_shelf_item_delegate.h"
 #include "ash/shell_window_ids.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
@@ -117,11 +117,11 @@
 
   model->Add(item);
 
-  ash::LauncherItemDelegateManager* manager =
-      ash::Shell::GetInstance()->launcher_item_delegate_manager();
-  scoped_ptr<LauncherItemDelegate> delegate(
-      new WindowWatcherLauncherItemDelegate(id, this));
-  manager->SetLauncherItemDelegate(id, delegate.Pass());
+  ShelfItemDelegateManager* manager =
+      Shell::GetInstance()->shelf_item_delegate_manager();
+  scoped_ptr<ShelfItemDelegate> delegate(
+      new WindowWatcherShelfItemDelegate(id, this));
+  manager->SetShelfItemDelegate(id, delegate.Pass());
   SetLauncherIDForWindow(id, new_window);
 }
 
diff --git a/ash/shell/window_watcher_launcher_item_delegate.cc b/ash/shell/window_watcher_launcher_item_delegate.cc
deleted file mode 100644
index 76e39fd..0000000
--- a/ash/shell/window_watcher_launcher_item_delegate.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 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.
-
-#include "ash/shell/window_watcher_launcher_item_delegate.h"
-
-#include "ash/shell/window_watcher.h"
-#include "ash/wm/window_util.h"
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace shell {
-
-WindowWatcherLauncherItemDelegate::WindowWatcherLauncherItemDelegate(
-    ash::LauncherID id,
-    ash::shell::WindowWatcher* watcher)
-    : id_(id),
-      watcher_(watcher) {
-  DCHECK_GT(id_, 0);
-  DCHECK(watcher_);
-}
-
-WindowWatcherLauncherItemDelegate::~WindowWatcherLauncherItemDelegate() {
-}
-
-bool WindowWatcherLauncherItemDelegate::ItemSelected(const ui::Event& event) {
-  aura::Window* window = watcher_->GetWindowByID(id_);
-  if (window->type() == aura::client::WINDOW_TYPE_PANEL)
-    ash::wm::MoveWindowToEventRoot(window, event);
-  window->Show();
-  ash::wm::ActivateWindow(window);
-  return false;
-}
-
-base::string16 WindowWatcherLauncherItemDelegate::GetTitle() {
-  return watcher_->GetWindowByID(id_)->title();
-}
-
-ui::MenuModel* WindowWatcherLauncherItemDelegate::CreateContextMenu(
-    aura::Window* root_window) {
-  return NULL;
-}
-
-ash::LauncherMenuModel*
-WindowWatcherLauncherItemDelegate::CreateApplicationMenu(
-    int event_flags) {
-  return NULL;
-}
-
-bool WindowWatcherLauncherItemDelegate::IsDraggable() {
-  return true;
-}
-
-bool WindowWatcherLauncherItemDelegate::ShouldShowTooltip() {
-  return true;
-}
-
-}  // namespace shell
-}  // namespace ash
diff --git a/ash/shell/window_watcher_launcher_item_delegate.h b/ash/shell/window_watcher_launcher_item_delegate.h
deleted file mode 100644
index 280f3b4..0000000
--- a/ash/shell/window_watcher_launcher_item_delegate.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 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 ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_
-#define ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-
-namespace ash {
-namespace shell {
-
-class WindowWatcher;
-
-// LauncherItemDelegate implementation used by WindowWatcher.
-class WindowWatcherLauncherItemDelegate : public ash::LauncherItemDelegate {
- public:
-  WindowWatcherLauncherItemDelegate(ash::LauncherID id,
-                                    ash::shell::WindowWatcher* watcher);
-  virtual ~WindowWatcherLauncherItemDelegate();
-
-  // ash::LauncherItemDelegate overrides:
-  virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
-  virtual base::string16 GetTitle() OVERRIDE;
-  virtual ui::MenuModel* CreateContextMenu(
-      aura::Window* root_window) OVERRIDE;
-  virtual ash::LauncherMenuModel* CreateApplicationMenu(
-      int event_flags) OVERRIDE;
-  virtual bool IsDraggable() OVERRIDE;
-  virtual bool ShouldShowTooltip() OVERRIDE;
-
- private:
-  ash::LauncherID id_;
-  ash::shell::WindowWatcher* watcher_;
-
-  DISALLOW_COPY_AND_ASSIGN(WindowWatcherLauncherItemDelegate);
-};
-
-}  // namespace shell
-}  // namespace ash
-
-#endif  // ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_
diff --git a/ash/shell/window_watcher_shelf_item_delegate.cc b/ash/shell/window_watcher_shelf_item_delegate.cc
new file mode 100644
index 0000000..ecb4308
--- /dev/null
+++ b/ash/shell/window_watcher_shelf_item_delegate.cc
@@ -0,0 +1,57 @@
+// Copyright 2013 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.
+
+#include "ash/shell/window_watcher_shelf_item_delegate.h"
+
+#include "ash/shell/window_watcher.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace shell {
+
+WindowWatcherShelfItemDelegate::WindowWatcherShelfItemDelegate(
+    LauncherID id,
+    WindowWatcher* watcher)
+    : id_(id), watcher_(watcher) {
+  DCHECK_GT(id_, 0);
+  DCHECK(watcher_);
+}
+
+WindowWatcherShelfItemDelegate::~WindowWatcherShelfItemDelegate() {
+}
+
+bool WindowWatcherShelfItemDelegate::ItemSelected(const ui::Event& event) {
+  aura::Window* window = watcher_->GetWindowByID(id_);
+  if (window->type() == aura::client::WINDOW_TYPE_PANEL)
+    wm::MoveWindowToEventRoot(window, event);
+  window->Show();
+  wm::ActivateWindow(window);
+  return false;
+}
+
+base::string16 WindowWatcherShelfItemDelegate::GetTitle() {
+  return watcher_->GetWindowByID(id_)->title();
+}
+
+ui::MenuModel* WindowWatcherShelfItemDelegate::CreateContextMenu(
+    aura::Window* root_window) {
+  return NULL;
+}
+
+ShelfMenuModel* WindowWatcherShelfItemDelegate::CreateApplicationMenu(
+    int event_flags) {
+  return NULL;
+}
+
+bool WindowWatcherShelfItemDelegate::IsDraggable() {
+  return true;
+}
+
+bool WindowWatcherShelfItemDelegate::ShouldShowTooltip() {
+  return true;
+}
+
+}  // namespace shell
+}  // namespace ash
diff --git a/ash/shell/window_watcher_shelf_item_delegate.h b/ash/shell/window_watcher_shelf_item_delegate.h
new file mode 100644
index 0000000..cf8f6a7
--- /dev/null
+++ b/ash/shell/window_watcher_shelf_item_delegate.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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 ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_
+#define ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_
+
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace ash {
+namespace shell {
+
+class WindowWatcher;
+
+// ShelfItemDelegate implementation used by WindowWatcher.
+class WindowWatcherShelfItemDelegate : public ShelfItemDelegate {
+ public:
+  WindowWatcherShelfItemDelegate(LauncherID id, WindowWatcher* watcher);
+  virtual ~WindowWatcherShelfItemDelegate();
+
+  // ShelfItemDelegate:
+  virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
+  virtual base::string16 GetTitle() OVERRIDE;
+  virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE;
+  virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
+  virtual bool IsDraggable() OVERRIDE;
+  virtual bool ShouldShowTooltip() OVERRIDE;
+
+ private:
+  LauncherID id_;
+  WindowWatcher* watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(WindowWatcherShelfItemDelegate);
+};
+
+}  // namespace shell
+}  // namespace ash
+
+#endif  // ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index e84af7e..41c4406 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -44,15 +44,15 @@
 
 class AccessibilityDelegate;
 class CapsLockDelegate;
-class LauncherDelegate;
-struct LauncherItem;
 class MediaDelegate;
 class NewWindowDelegate;
 class RootWindowHostFactory;
 class SessionStateDelegate;
+class ShelfDelegate;
 class ShelfModel;
 class SystemTrayDelegate;
 class UserWallpaperDelegate;
+struct LauncherItem;
 
 enum UserMetricsAction {
   UMA_ACCEL_KEYBOARD_BRIGHTNESS_DOWN_F6,
@@ -68,7 +68,6 @@
   UMA_GESTURE_OVERVIEW,
   UMA_LAUNCHER_CLICK_ON_APP,
   UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON,
-  UMA_MINIMIZE_PER_KEY,
   UMA_MOUSE_DOWN,
   UMA_SHELF_ALIGNMENT_SET_BOTTOM,
   UMA_SHELF_ALIGNMENT_SET_LEFT,
@@ -145,9 +144,9 @@
   // the created delegate.
   virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() = 0;
 
-  // Creates a new LauncherDelegate. Shell takes ownership of the returned
+  // Creates a new ShelfDelegate. Shell takes ownership of the returned
   // value.
-  virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) = 0;
+  virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) = 0;
 
   // Creates a system-tray delegate. Shell takes ownership of the delegate.
   virtual SystemTrayDelegate* CreateSystemTrayDelegate() = 0;
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index 5d75e94..e9b042d 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -353,7 +353,7 @@
     ash::SystemTrayDelegate* delegate =
         ash::Shell::GetInstance()->system_tray_delegate();
     if (sender == footer()->content()) {
-      owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+      TransitionToDefaultView();
     } else if (sender == manage_devices_) {
       delegate->ManageBluetoothDevices();
     } else if (sender == enable_bluetooth_) {
diff --git a/ash/system/chromeos/audio/tray_audio.cc b/ash/system/chromeos/audio/tray_audio.cc
index da83aee..5922f00 100644
--- a/ash/system/chromeos/audio/tray_audio.cc
+++ b/ash/system/chromeos/audio/tray_audio.cc
@@ -518,7 +518,7 @@
   // Overridden from ViewClickListener.
   virtual void OnViewClicked(views::View* sender) OVERRIDE {
     if (sender == footer()->content()) {
-      owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+      TransitionToDefaultView();
     } else {
       AudioDeviceMap::iterator iter = device_map_.find(sender);
       if (iter == device_map_.end())
diff --git a/ash/system/chromeos/label_tray_view.cc b/ash/system/chromeos/label_tray_view.cc
index 5af294e..0ae4604 100644
--- a/ash/system/chromeos/label_tray_view.cc
+++ b/ash/system/chromeos/label_tray_view.cc
@@ -9,6 +9,7 @@
 #include "ash/system/tray/view_click_listener.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/font.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
 
diff --git a/ash/system/chromeos/managed/tray_locally_managed_user.cc b/ash/system/chromeos/managed/tray_locally_managed_user.cc
index 9a8fd48..bd3e380 100644
--- a/ash/system/chromeos/managed/tray_locally_managed_user.cc
+++ b/ash/system/chromeos/managed/tray_locally_managed_user.cc
@@ -10,6 +10,7 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_notification_view.h"
 #include "ash/system/user/login_status.h"
+#include "base/callback.h"
 #include "base/logging.h"
 #include "grit/ash_resources.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -77,18 +78,14 @@
 void TrayLocallyManagedUser::CreateOrUpdateNotification(
     const base::string16& new_message) {
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-  scoped_ptr<Notification> notification(new Notification(
-      message_center::NOTIFICATION_TYPE_SIMPLE,
-      kNotificationId,
-      string16() /* no title */,
-      new_message,
-      bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER),
-      base::string16() /* display_source */,
-      message_center::NotifierId(
-          system_notifier::NOTIFIER_LOCALLY_MANAGED_USER),
-      message_center::RichNotificationData(),
-      NULL /* no delegate */));
-  notification->SetSystemPriority();
+  scoped_ptr<Notification> notification(
+      message_center::Notification::CreateSystemNotification(
+          kNotificationId,
+          string16() /* no title */,
+          new_message,
+          bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER),
+          system_notifier::kNotifierLocallyManagedUser,
+          base::Closure() /* null callback */));
   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
 }
 
diff --git a/ash/system/chromeos/network/network_connect.cc b/ash/system/chromeos/network/network_connect.cc
index 4013c77..d8f524a 100644
--- a/ash/system/chromeos/network/network_connect.cc
+++ b/ash/system/chromeos/network/network_connect.cc
@@ -439,7 +439,7 @@
                                        UTF8ToUTF16(cellular->name())),
             ui::ResourceBundle::GetSharedInstance().GetImageNamed(
                 IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED),
-            ash::system_notifier::NOTIFIER_NETWORK,
+            ash::system_notifier::kNotifierNetwork,
             base::Bind(&ash::network_connect::ShowNetworkSettings,
                        service_path)));
     return;
diff --git a/ash/system/chromeos/network/network_state_list_detailed_view.cc b/ash/system/chromeos/network/network_state_list_detailed_view.cc
index ffd8ad5..ff45d77 100644
--- a/ash/system/chromeos/network/network_state_list_detailed_view.cc
+++ b/ash/system/chromeos/network/network_state_list_detailed_view.cc
@@ -299,8 +299,7 @@
   ResetInfoBubble();
 
   if (sender == footer()->content()) {
-    RootWindowController::ForWindow(GetWidget()->GetNativeView())->
-        GetSystemTray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+    TransitionToDefaultView();
     return;
   }
 
diff --git a/ash/system/chromeos/network/network_state_notifier.cc b/ash/system/chromeos/network/network_state_notifier.cc
index c8beb32..ac69a8a 100644
--- a/ash/system/chromeos/network/network_state_notifier.cc
+++ b/ash/system/chromeos/network/network_state_notifier.cc
@@ -67,7 +67,7 @@
           title,
           message,
           icon,
-          ash::system_notifier::NOTIFIER_NETWORK_ERROR,
+          ash::system_notifier::kNotifierNetworkError,
           callback));
 }
 
@@ -184,7 +184,7 @@
           l10n_util::GetStringFUTF16(IDS_NETWORK_CELLULAR_ACTIVATED,
                                      UTF8ToUTF16((cellular->name()))),
           icon,
-          system_notifier::NOTIFIER_NETWORK,
+          system_notifier::kNotifierNetwork,
           base::Bind(&ash::network_connect::ShowNetworkSettings,
                      cellular->path())));
 }
diff --git a/ash/system/chromeos/network/tray_sms.cc b/ash/system/chromeos/network/tray_sms.cc
index 9adb5f9..7b00c27 100644
--- a/ash/system/chromeos/network/tray_sms.cc
+++ b/ash/system/chromeos/network/tray_sms.cc
@@ -234,7 +234,7 @@
   // Overridden from ViewClickListener.
   virtual void OnViewClicked(views::View* sender) OVERRIDE {
     if (sender == footer()->content())
-      owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+      TransitionToDefaultView();
   }
 
   DISALLOW_COPY_AND_ASSIGN(SmsDetailedView);
diff --git a/ash/system/chromeos/power/tray_power.cc b/ash/system/chromeos/power/tray_power.cc
index 1eb0048..1fdd6a9 100644
--- a/ash/system/chromeos/power/tray_power.cc
+++ b/ash/system/chromeos/power/tray_power.cc
@@ -199,7 +199,9 @@
             IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT),
         rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
         base::string16(),
-        message_center::NotifierId(system_notifier::NOTIFIER_POWER),
+        message_center::NotifierId(
+            message_center::NotifierId::SYSTEM_COMPONENT,
+            system_notifier::kNotifierPower),
         message_center::RichNotificationData(),
         NULL));
     message_center_->AddNotification(notification.Pass());
diff --git a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
index 2998b3b..0bba2b2 100644
--- a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
@@ -63,7 +63,9 @@
       base::string16() /* body is blank */,
       resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16() /* display_source */,
-      message_center::NotifierId(system_notifier::NOTIFIER_SCREEN_CAPTURE),
+      message_center::NotifierId(
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierScreenCapture),
       data,
       new tray::ScreenNotificationDelegate(this)));
   notification->SetSystemPriority();
diff --git a/ash/system/chromeos/screen_security/screen_share_tray_item.cc b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
index bf70c44..bceae59 100644
--- a/ash/system/chromeos/screen_security/screen_share_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
@@ -73,7 +73,9 @@
       base::string16() /* body is blank */,
       resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16() /* display_source */,
-      message_center::NotifierId(system_notifier::NOTIFIER_SCREEN_SHARE),
+      message_center::NotifierId(
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierScreenShare),
       data,
       new tray::ScreenNotificationDelegate(this)));
   notification->SetSystemPriority();
diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc
index bf0b3dc..35627e1 100644
--- a/ash/system/chromeos/tray_display.cc
+++ b/ash/system/chromeos/tray_display.cc
@@ -107,12 +107,21 @@
 }
 
 void OpenSettings() {
-  user::LoginStatus login_status =
-      Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
-  if (login_status == user::LOGGED_IN_USER ||
-      login_status == user::LOGGED_IN_OWNER ||
-      login_status == user::LOGGED_IN_GUEST) {
-    Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
+  // switch is intentionally introduced without default, to cause an error when
+  // a new type of login status is introduced.
+  switch (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus()) {
+    case user::LOGGED_IN_NONE:
+    case user::LOGGED_IN_LOCKED:
+      return;
+
+    case user::LOGGED_IN_USER:
+    case user::LOGGED_IN_OWNER:
+    case user::LOGGED_IN_GUEST:
+    case user::LOGGED_IN_RETAIL_MODE:
+    case user::LOGGED_IN_PUBLIC:
+    case user::LOGGED_IN_LOCALLY_MANAGED:
+    case user::LOGGED_IN_KIOSK_APP:
+      Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
   }
 }
 
@@ -264,7 +273,6 @@
     int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
         kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
     label_->SizeToFit(label_max_width);
-    PreferredSizeChanged();
   }
 
   views::ImageView* image_;
@@ -373,7 +381,9 @@
       additional_message,
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
       base::string16(),  // display_source
-      message_center::NotifierId(system_notifier::NOTIFIER_DISPLAY),
+      message_center::NotifierId(
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierDisplay),
       message_center::RichNotificationData(),
       new message_center::HandleNotificationClickedDelegate(
           base::Bind(&OpenSettings))));
diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc
index 24428d1..ae4a546 100644
--- a/ash/system/date/date_view.cc
+++ b/ash/system/date/date_view.cc
@@ -17,6 +17,7 @@
 #include "third_party/icu/source/i18n/unicode/dtptngen.h"
 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/grid_layout.h"
diff --git a/ash/system/drive/tray_drive.cc b/ash/system/drive/tray_drive.cc
index 028aa29..4305d5a 100644
--- a/ash/system/drive/tray_drive.cc
+++ b/ash/system/drive/tray_drive.cc
@@ -364,7 +364,7 @@
   virtual void OnViewClicked(views::View* sender) OVERRIDE {
     SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
     if (sender == footer()->content()) {
-      owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+      TransitionToDefaultView();
     } else if (sender == settings_) {
       delegate->ShowDriveSettings();
     }
diff --git a/ash/system/ime/tray_ime.cc b/ash/system/ime/tray_ime.cc
index 87e254a..d58b1ef 100644
--- a/ash/system/ime/tray_ime.cc
+++ b/ash/system/ime/tray_ime.cc
@@ -147,7 +147,7 @@
   virtual void OnViewClicked(views::View* sender) OVERRIDE {
     SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
     if (sender == footer()->content()) {
-      owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+      TransitionToDefaultView();
     } else if (sender == settings_) {
       delegate->ShowIMESettings();
     } else {
@@ -231,7 +231,9 @@
       base::string16(),  // message
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_IME),
       base::string16(),  // display_source
-      message_center::NotifierId(system_notifier::NOTIFIER_INPUT_METHOD),
+      message_center::NotifierId(
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierInputMethod),
       message_center::RichNotificationData(),
       new message_center::HandleNotificationClickedDelegate(
           base::Bind(&TrayIME::PopupDetailedView,
diff --git a/ash/system/locale/locale_notification_controller.cc b/ash/system/locale/locale_notification_controller.cc
index 7d66d5b..419baaf 100644
--- a/ash/system/locale/locale_notification_controller.cc
+++ b/ash/system/locale/locale_notification_controller.cc
@@ -117,7 +117,9 @@
           IDS_ASH_STATUS_TRAY_LOCALE_CHANGE_MESSAGE, from, to),
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_LOCALE),
       base::string16()  /* display_source */,
-      message_center::NotifierId(system_notifier::NOTIFIER_LOCALE),
+      message_center::NotifierId(
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierLocale),
       optional,
       new LocaleNotificationDelegate(delegate)));
   message_center::MessageCenter::Get()->AddNotification(notification.Pass());
diff --git a/ash/system/session_length_limit/tray_session_length_limit.cc b/ash/system/session_length_limit/tray_session_length_limit.cc
index fe8ff7e..cdf69d5 100644
--- a/ash/system/session_length_limit/tray_session_length_limit.cc
+++ b/ash/system/session_length_limit/tray_session_length_limit.cc
@@ -103,7 +103,8 @@
       bundle.GetImageNamed(IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER),
       base::string16() /* display_source */,
       message_center::NotifierId(
-          system_notifier::NOTIFIER_SESSION_LENGTH_TIMEOUT),
+          message_center::NotifierId::SYSTEM_COMPONENT,
+          system_notifier::kNotifierSessionLengthTimeout),
       data,
       NULL /* delegate */));
   notification->SetSystemPriority();
diff --git a/ash/system/system_notifier.cc b/ash/system/system_notifier.cc
index 554c0f9..e4d65d2 100644
--- a/ash/system/system_notifier.cc
+++ b/ash/system/system_notifier.cc
@@ -13,33 +13,67 @@
 
 // See http://dev.chromium.org/chromium-os/chromiumos-design-docs/
 // system-notifications for the reasoning.
-const AshSystemComponentNotifierType kAlwaysShownNotifierIds[] = {
-  NOTIFIER_DISPLAY,
-  NOTIFIER_DISPLAY_ERROR,
-  NOTIFIER_POWER,
+const char* kAlwaysShownNotifierIds[] = {
+  kNotifierDisplay,
+  kNotifierDisplayError,
+  kNotifierPower,
+  NULL
 };
 
-}  // namespace
+const char* kAshSystemNotifiers[] = {
+  kNotifierDisplay,
+  kNotifierDisplayResolutionChange,
+  kNotifierDisplayError,
+  kNotifierInputMethod,
+  kNotifierLocale,
+  kNotifierLocallyManagedUser,
+  kNotifierMultiProfileFirstRun,
+  kNotifierNetwork,
+  kNotifierNetworkError,
+  kNotifierScreenshot,
+  kNotifierScreenCapture,
+  kNotifierScreenShare,
+  kNotifierSessionLengthTimeout,
+  kNotifierPower,
+  NULL
+};
 
-std::string SystemComponentTypeToString(AshSystemComponentNotifierType type) {
-  if (type == NOTIFIER_SCREENSHOT)
-    return "screenshot";
-
-  // TODO(mukai): fill the names of other components.
-  NOTIMPLEMENTED();
-  return std::string();
-}
-
-bool ShouldAlwaysShowPopups(const message_center::NotifierId& notifier_id) {
+bool MatchSystemNotifierId(const message_center::NotifierId& notifier_id,
+                           const char* id_list[]) {
   if (notifier_id.type != message_center::NotifierId::SYSTEM_COMPONENT)
     return false;
 
-  for (size_t i = 0; i < arraysize(kAlwaysShownNotifierIds); ++i) {
-    if (notifier_id.system_component_type == kAlwaysShownNotifierIds[i])
+  for (size_t i = 0; id_list[i] != NULL; ++i) {
+    if (notifier_id.id == id_list[i])
       return true;
   }
   return false;
 }
 
+}  // namespace
+
+const char kNotifierDisplay[] = "ash.display";
+const char kNotifierDisplayResolutionChange[] = "ash.display.resolution-change";
+const char kNotifierDisplayError[] = "ash.display.error";
+const char kNotifierInputMethod[] = "ash.input-method";
+const char kNotifierLocale[] = "ash.locale";
+const char kNotifierLocallyManagedUser[] = "ash.locally-managed-user";
+const char kNotifierMultiProfileFirstRun[] = "ash.multi-profile.first-run";
+const char kNotifierNetwork[] = "ash.network";
+const char kNotifierNetworkError[] = "ash.network.error";
+const char kNotifierScreenshot[] = "ash.screenshot";
+const char kNotifierScreenCapture[] = "ash.screen-capture";
+const char kNotifierScreenShare[] = "ash.screen-share";
+const char kNotifierSessionLengthTimeout[] = "ash.session-length-timeout";
+const char kNotifierPower[] = "ash.power";
+
+bool ShouldAlwaysShowPopups(const message_center::NotifierId& notifier_id) {
+  return MatchSystemNotifierId(notifier_id, kAlwaysShownNotifierIds);
+}
+
+bool IsAshSystemNotifier(const message_center::NotifierId& notifier_id) {
+  return MatchSystemNotifierId(notifier_id, kAshSystemNotifiers);
+}
+
 }  // namespace system_notifier
 }  // namespace ash
diff --git a/ash/system/system_notifier.h b/ash/system/system_notifier.h
index 3d823c8..53ac77d 100644
--- a/ash/system/system_notifier.h
+++ b/ash/system/system_notifier.h
@@ -13,27 +13,21 @@
 namespace ash {
 namespace system_notifier {
 
-enum AshSystemComponentNotifierType {
-  NOTIFIER_NO_SYSTEM_COMPONENT = -1,
-
-  // Alphabetical order.
-  NOTIFIER_DISPLAY,
-  NOTIFIER_DISPLAY_RESOLUTION_CHANGE,
-  NOTIFIER_DISPLAY_ERROR,
-  NOTIFIER_INPUT_METHOD,
-  NOTIFIER_LOCALE,
-  NOTIFIER_LOCALLY_MANAGED_USER,
-  NOTIFIER_NETWORK,
-  NOTIFIER_NETWORK_ERROR,
-  NOTIFIER_SCREENSHOT,
-  NOTIFIER_SCREEN_CAPTURE,
-  NOTIFIER_SCREEN_SHARE,
-  NOTIFIER_SESSION_LENGTH_TIMEOUT,
-  NOTIFIER_POWER,
-};
-
-ASH_EXPORT std::string SystemComponentTypeToString(
-    AshSystemComponentNotifierType type);
+// The list of ash system notifier IDs. Alphabetical order.
+ASH_EXPORT extern const char kNotifierDisplay[];
+ASH_EXPORT extern const char kNotifierDisplayResolutionChange[];
+ASH_EXPORT extern const char kNotifierDisplayError[];
+ASH_EXPORT extern const char kNotifierInputMethod[];
+ASH_EXPORT extern const char kNotifierLocale[];
+ASH_EXPORT extern const char kNotifierLocallyManagedUser[];
+ASH_EXPORT extern const char kNotifierMultiProfileFirstRun[];
+ASH_EXPORT extern const char kNotifierNetwork[];
+ASH_EXPORT extern const char kNotifierNetworkError[];
+ASH_EXPORT extern const char kNotifierScreenshot[];
+ASH_EXPORT extern const char kNotifierScreenCapture[];
+ASH_EXPORT extern const char kNotifierScreenShare[];
+ASH_EXPORT extern const char kNotifierSessionLengthTimeout[];
+ASH_EXPORT extern const char kNotifierPower[];
 
 // Returns true if notifications from |notifier_id| should always appear as
 // popups. "Always appear" means the popups should appear even in login screen,
@@ -41,6 +35,10 @@
 ASH_EXPORT bool ShouldAlwaysShowPopups(
     const message_center::NotifierId& notifier_id);
 
+// Returns true if |notifier_id| is the system notifier from Ash.
+ASH_EXPORT bool IsAshSystemNotifier(
+    const message_center::NotifierId& notifier_id);
+
 }  // namespace system_notifier
 }  // namespace ash
 
diff --git a/ash/system/tray/actionable_view.cc b/ash/system/tray/actionable_view.cc
index 1fae3ba..169b46d 100644
--- a/ash/system/tray/actionable_view.cc
+++ b/ash/system/tray/actionable_view.cc
@@ -22,10 +22,14 @@
 ActionableView::~ActionableView() {
 }
 
-void ActionableView::DrawBorder(gfx::Canvas* canvas, const gfx::Rect& bounds) {
-  gfx::Rect rect = bounds;
-  rect.Inset(1, 1, 3, 3);
-  canvas->DrawRect(rect, kFocusBorderColor);
+void ActionableView::OnPaintFocus(gfx::Canvas* canvas) {
+  gfx::Rect rect(GetFocusBounds());
+  rect.Inset(1, 1, 3, 2);
+  canvas->DrawSolidFocusRect(rect, kFocusBorderColor);
+}
+
+gfx::Rect ActionableView::GetFocusBounds() {
+  return GetLocalBounds();
 }
 
 const char* ActionableView::GetClassName() const {
@@ -59,9 +63,27 @@
   accessible_name_ = name;
 }
 
-void ActionableView::OnPaintFocusBorder(gfx::Canvas* canvas) {
+void ActionableView::GetAccessibleState(ui::AccessibleViewState* state) {
+  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
+  state->name = accessible_name_;
+}
+
+void ActionableView::OnPaint(gfx::Canvas* canvas) {
+  View::OnPaint(canvas);
   if (HasFocus())
-    DrawBorder(canvas, GetLocalBounds());
+    OnPaintFocus(canvas);
+}
+
+void ActionableView::OnFocus() {
+  View::OnFocus();
+  // We render differently when focused.
+  SchedulePaint();
+}
+
+void ActionableView::OnBlur() {
+  View::OnBlur();
+  // We render differently when focused.
+  SchedulePaint();
 }
 
 void ActionableView::OnGestureEvent(ui::GestureEvent* event) {
@@ -69,10 +91,5 @@
     event->SetHandled();
 }
 
-void ActionableView::GetAccessibleState(ui::AccessibleViewState* state) {
-  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
-  state->name = accessible_name_;
-}
-
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/system/tray/actionable_view.h b/ash/system/tray/actionable_view.h
index 0d0f1a1..f2b3ec1 100644
--- a/ash/system/tray/actionable_view.h
+++ b/ash/system/tray/actionable_view.h
@@ -21,6 +21,8 @@
 // Exported for SystemTray.
 class ASH_EXPORT ActionableView : public views::View {
  public:
+  static const char kViewClassName[];
+
   ActionableView();
 
   virtual ~ActionableView();
@@ -28,10 +30,11 @@
   void SetAccessibleName(const base::string16& name);
   const base::string16& accessible_name() const { return accessible_name_; }
 
-  static const char kViewClassName[];
-
  protected:
-  void DrawBorder(gfx::Canvas* canvas, const gfx::Rect& bounds);
+  void OnPaintFocus(gfx::Canvas* canvas);
+
+  // Returns the bounds to paint the focus rectangle in.
+  virtual gfx::Rect GetFocusBounds();
 
   // Performs an action when user clicks on the view (on mouse-press event), or
   // presses a key when this view is in focus. Returns true if the event has
@@ -45,7 +48,9 @@
   virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
   virtual void OnMouseCaptureLost() OVERRIDE;
   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
-  virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+  virtual void OnFocus() OVERRIDE;
+  virtual void OnBlur() OVERRIDE;
 
   // Overridden from ui::EventHandler.
   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
diff --git a/ash/system/tray/fixed_sized_scroll_view.cc b/ash/system/tray/fixed_sized_scroll_view.cc
index 59b524b..d3fff9a 100644
--- a/ash/system/tray/fixed_sized_scroll_view.cc
+++ b/ash/system/tray/fixed_sized_scroll_view.cc
@@ -9,7 +9,6 @@
 
 FixedSizedScrollView::FixedSizedScrollView() {
   set_notify_enter_exit_on_child(true);
-  set_focus_border(NULL);
 }
 
 FixedSizedScrollView::~FixedSizedScrollView() {
@@ -54,9 +53,5 @@
   contents()->SetBoundsRect(bounds);
 }
 
-void FixedSizedScrollView::OnPaintFocusBorder(gfx::Canvas* canvas) {
-  // Do not paint the focus border.
-}
-
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/system/tray/fixed_sized_scroll_view.h b/ash/system/tray/fixed_sized_scroll_view.h
index c5d9bc0..aff53c2 100644
--- a/ash/system/tray/fixed_sized_scroll_view.h
+++ b/ash/system/tray/fixed_sized_scroll_view.h
@@ -33,7 +33,6 @@
  protected:
   // Overridden from views::View:
   virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
-  virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
 
  private:
   gfx::Size fixed_size_;
diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc
index f68ea12..8afa2de 100644
--- a/ash/system/tray/hover_highlight_view.cc
+++ b/ash/system/tray/hover_highlight_view.cc
@@ -11,6 +11,7 @@
 #include "ui/base/accessibility/accessible_view_state.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
diff --git a/ash/system/tray/special_popup_row.cc b/ash/system/tray/special_popup_row.cc
index 896d151..fd10f47 100644
--- a/ash/system/tray/special_popup_row.cc
+++ b/ash/system/tray/special_popup_row.cc
@@ -13,6 +13,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/rect.h"
+#include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/painter.h"
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 045526c..b5d8cc3 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -101,11 +101,11 @@
     }
     is_persistent_ = is_persistent;
 
-    // If ChromeVox is enabled, focus the default item.
-    AccessibilityDelegate* delegate =
-        Shell::GetInstance()->accessibility_delegate();
-    if (delegate->IsSpokenFeedbackEnabled())
-      bubble_->FocusDefault();
+    // If ChromeVox is enabled, focus the default item if no item is focused.
+    if (Shell::GetInstance()->accessibility_delegate()->
+        IsSpokenFeedbackEnabled()) {
+      bubble_->FocusDefaultIfNeeded();
+    }
   }
 
   // Convenience accessors:
@@ -461,11 +461,11 @@
   notification_bubble_.reset();
   if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
     system_bubble_->bubble()->UpdateView(items, bubble_type);
-    // If ChromeVox is enabled, focus the default item.
-    AccessibilityDelegate* delegate =
-        Shell::GetInstance()->accessibility_delegate();
-    if (delegate->IsSpokenFeedbackEnabled())
-      system_bubble_->bubble()->FocusDefault();
+    // If ChromeVox is enabled, focus the default item if no item is focused.
+    if (Shell::GetInstance()->accessibility_delegate()->
+        IsSpokenFeedbackEnabled()) {
+      system_bubble_->bubble()->FocusDefaultIfNeeded();
+    }
   } else {
     // Remember if the menu is a single property (like e.g. volume) or the
     // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
diff --git a/ash/system/tray/system_tray_bubble.cc b/ash/system/tray/system_tray_bubble.cc
index 362e996..1aa60bc 100644
--- a/ash/system/tray/system_tray_bubble.cc
+++ b/ash/system/tray/system_tray_bubble.cc
@@ -271,9 +271,9 @@
   }
 }
 
-void SystemTrayBubble::FocusDefault() {
+void SystemTrayBubble::FocusDefaultIfNeeded() {
   views::FocusManager* manager = bubble_view_->GetFocusManager();
-  if (!manager)
+  if (!manager || manager->GetFocusedView())
     return;
 
   views::View* view = manager->GetNextFocusableView(NULL, NULL, false, false);
@@ -352,11 +352,14 @@
 
 void SystemTrayBubble::CreateItemViews(user::LoginStatus login_status) {
   std::vector<views::View*> item_views;
+  views::View* focus_view = NULL;
   for (size_t i = 0; i < items_.size(); ++i) {
     views::View* view = NULL;
     switch (bubble_type_) {
       case BUBBLE_TYPE_DEFAULT:
         view = items_[i]->CreateDefaultView(login_status);
+        if (items_[i]->restore_focus())
+          focus_view = view;
         break;
       case BUBBLE_TYPE_DETAILED:
         view = items_[i]->CreateDetailedView(login_status);
@@ -377,6 +380,8 @@
         item_views[i], is_default_bubble,
         is_default_bubble && (i < item_views.size() - 2)));
   }
+  if (focus_view)
+    focus_view->RequestFocus();
 }
 
 }  // namespace internal
diff --git a/ash/system/tray/system_tray_bubble.h b/ash/system/tray/system_tray_bubble.h
index 7232805..b84c3ea 100644
--- a/ash/system/tray/system_tray_bubble.h
+++ b/ash/system/tray/system_tray_bubble.h
@@ -43,8 +43,8 @@
                 user::LoginStatus login_status,
                 views::TrayBubbleView::InitParams* init_params);
 
-  // Focus the default item.
-  void FocusDefault();
+  // Focus the default item if no item is focused. Othewise, do nothing.
+  void FocusDefaultIfNeeded();
 
   BubbleType bubble_type() const { return bubble_type_; }
   views::TrayBubbleView* bubble_view() const { return bubble_view_; }
diff --git a/ash/system/tray/system_tray_item.cc b/ash/system/tray/system_tray_item.cc
index b2acf08..f5d5b71 100644
--- a/ash/system/tray/system_tray_item.cc
+++ b/ash/system/tray/system_tray_item.cc
@@ -12,7 +12,8 @@
 namespace ash {
 
 SystemTrayItem::SystemTrayItem(SystemTray* system_tray)
-    : system_tray_(system_tray) {
+    : system_tray_(system_tray),
+      restore_focus_(false) {
 }
 
 SystemTrayItem::~SystemTrayItem() {
diff --git a/ash/system/tray/system_tray_item.h b/ash/system/tray/system_tray_item.h
index a0d7d1b..b42f3f5 100644
--- a/ash/system/tray/system_tray_item.h
+++ b/ash/system/tray/system_tray_item.h
@@ -108,8 +108,14 @@
   // Returns the system tray that this item belongs to.
   SystemTray* system_tray() const { return system_tray_; }
 
+  bool restore_focus() const { return restore_focus_; }
+  void set_restore_focus(bool restore_focus) {
+    restore_focus_ = restore_focus;
+  }
+
  private:
   SystemTray* system_tray_;
+  bool restore_focus_;
 
   DISALLOW_COPY_AND_ASSIGN(SystemTrayItem);
 };
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 3939b70..80cac07 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -333,10 +333,10 @@
   set_notify_enter_exit_on_child(true);
 
   // Initially we want to paint the background, but without the hover effect.
-  hide_background_animator_.SetPaintsBackground(true,
-      internal::BackgroundAnimator::CHANGE_IMMEDIATE);
-  hover_background_animator_.SetPaintsBackground(false,
-      internal::BackgroundAnimator::CHANGE_IMMEDIATE);
+  hide_background_animator_.SetPaintsBackground(
+      true, BACKGROUND_CHANGE_IMMEDIATE);
+  hover_background_animator_.SetPaintsBackground(
+      false, BACKGROUND_CHANGE_IMMEDIATE);
 
   tray_container_ = new TrayContainer(shelf_alignment_);
   SetContents(tray_container_);
@@ -362,8 +362,8 @@
   if (!background_ || draw_background_as_active_ ||
       ash::switches::UseAlternateShelfLayout())
     return;
-  hover_background_animator_.SetPaintsBackground(true,
-      internal::BackgroundAnimator::CHANGE_ANIMATE);
+  hover_background_animator_.SetPaintsBackground(
+      true, BACKGROUND_CHANGE_ANIMATE);
 }
 
 void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) {
@@ -371,22 +371,14 @@
   if (!background_ || draw_background_as_active_ ||
       ash::switches::UseAlternateShelfLayout())
     return;
-  hover_background_animator_.SetPaintsBackground(false,
-      internal::BackgroundAnimator::CHANGE_ANIMATE);
+  hover_background_animator_.SetPaintsBackground(
+      false, BACKGROUND_CHANGE_ANIMATE);
 }
 
 void TrayBackgroundView::ChildPreferredSizeChanged(views::View* child) {
   PreferredSizeChanged();
 }
 
-void TrayBackgroundView::OnPaintFocusBorder(gfx::Canvas* canvas) {
-  // The tray itself expands to the right and bottom edge of the screen to make
-  // sure clicking on the edges brings up the popup. However, the focus border
-  // should be only around the container.
-  if (HasFocus())
-    DrawBorder(canvas, GetContentsBounds());
-}
-
 void TrayBackgroundView::GetAccessibleState(ui::AccessibleViewState* state) {
   state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
   state->name = GetAccessibleNameForTray();
@@ -403,6 +395,13 @@
   return false;
 }
 
+gfx::Rect TrayBackgroundView::GetFocusBounds() {
+  // The tray itself expands to the right and bottom edge of the screen to make
+  // sure clicking on the edges brings up the popup. However, the focus border
+  // should be only around the container.
+  return GetContentsBounds();
+}
+
 void TrayBackgroundView::UpdateBackground(int alpha) {
   // The animator should never fire when the alternate shelf layout is used.
   if (!background_ || draw_background_as_active_)
@@ -419,8 +418,7 @@
 }
 
 void TrayBackgroundView::SetPaintsBackground(
-    bool value,
-    internal::BackgroundAnimator::ChangeType change_type) {
+    bool value, BackgroundAnimatorChangeType change_type) {
   DCHECK(!ash::switches::UseAlternateShelfLayout());
   hide_background_animator_.SetPaintsBackground(value, change_type);
 }
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index b459684..5adaf4e 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -70,12 +70,12 @@
   virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
   virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
   virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
-  virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
   virtual void AboutToRequestFocusFromTabTraversal(bool reverse) OVERRIDE;
 
   // Overridden from internal::ActionableView.
   virtual bool PerformAction(const ui::Event& event) OVERRIDE;
+  virtual gfx::Rect GetFocusBounds() OVERRIDE;
 
   // Overridden from internal::BackgroundAnimatorDelegate.
   virtual void UpdateBackground(int alpha) OVERRIDE;
@@ -108,9 +108,8 @@
 
   // Sets whether the tray paints a background. Default is true, but is set to
   // false if a window overlaps the shelf.
-  void SetPaintsBackground(
-      bool value,
-      internal::BackgroundAnimator::ChangeType change_type);
+  void SetPaintsBackground(bool value,
+                           BackgroundAnimatorChangeType change_type);
 
   // Initializes animations for the bubble.
   void InitializeBubbleAnimations(views::Widget* bubble_widget);
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc
index dd33cdf..72efe4f 100644
--- a/ash/system/tray/tray_details_view.cc
+++ b/ash/system/tray/tray_details_view.cc
@@ -5,10 +5,12 @@
 #include "ash/system/tray/tray_details_view.h"
 
 #include "ash/system/tray/fixed_sized_scroll_view.h"
+#include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/layout/box_layout.h"
 
@@ -114,6 +116,16 @@
   scroll_content_ = NULL;
 }
 
+void TrayDetailsView::TransitionToDefaultView() {
+  // Cache pointer to owner in this function scope. TrayDetailsView will be
+  // deleted after called ShowDefaultView.
+  SystemTrayItem* owner = owner_;
+  if (footer_ && footer_->content() && footer_->content()->HasFocus())
+    owner->set_restore_focus(true);
+  owner->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  owner->set_restore_focus(false);
+}
+
 void TrayDetailsView::Layout() {
   if (!scroller_ || !footer_ || bounds().IsEmpty()) {
     views::View::Layout();
diff --git a/ash/system/tray/tray_details_view.h b/ash/system/tray/tray_details_view.h
index 073873a..28e9082 100644
--- a/ash/system/tray/tray_details_view.h
+++ b/ash/system/tray/tray_details_view.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_TRAY_TRAY_DETAILS_VIEW_H_
 #define ASH_SYSTEM_TRAY_TRAY_DETAILS_VIEW_H_
 
+#include "ash/ash_export.h"
 #include "ash/system/tray/special_popup_row.h"
 #include "ui/views/view.h"
 
@@ -22,7 +23,7 @@
 class ScrollBorder;
 class ViewClickListener;
 
-class TrayDetailsView : public views::View {
+class ASH_EXPORT TrayDetailsView : public views::View {
  public:
   explicit TrayDetailsView(SystemTrayItem* owner);
   virtual ~TrayDetailsView();
@@ -41,6 +42,11 @@
   // Removes (and destroys) all child views.
   void Reset();
 
+  // Transition to default view from details view. If |footer_| has focus before
+  // transition, the default view should focus on the owner of this details
+  // view.
+  void TransitionToDefaultView();
+
   SystemTrayItem* owner() const { return owner_; }
   SpecialPopupRow* footer() const { return footer_; }
   FixedSizedScrollView* scroller() const { return scroller_; }
diff --git a/ash/system/tray/tray_details_view_unittest.cc b/ash/system/tray/tray_details_view_unittest.cc
new file mode 100644
index 0000000..49a637e
--- /dev/null
+++ b/ash/system/tray/tray_details_view_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright 2013 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.
+
+#include "ash/system/tray/tray_details_view.h"
+
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/system_tray_item.h"
+#include "ash/system/tray/tray_details_view.h"
+#include "ash/system/tray/view_click_listener.h"
+#include "ash/test/ash_test_base.h"
+#include "base/run_loop.h"
+#include "grit/ash_strings.h"
+#include "ui/aura/window.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace test {
+
+namespace {
+
+SystemTray* GetSystemTray() {
+  return Shell::GetPrimaryRootWindowController()->shelf()->
+      status_area_widget()->system_tray();
+}
+
+class TestDetailsView : public internal::TrayDetailsView,
+                        public internal::ViewClickListener {
+ public:
+  explicit TestDetailsView(SystemTrayItem* owner)
+      : internal::TrayDetailsView(owner)  {}
+
+  virtual ~TestDetailsView() {}
+
+  void CreateFooterAndFocus() {
+    // Uses bluetooth label for testing purpose. It can be changed to any
+    // string_id.
+    CreateSpecialRow(IDS_ASH_STATUS_TRAY_BLUETOOTH, this);
+    footer()->content()->RequestFocus();
+  }
+
+  // Overridden from internal::ViewClickListener:
+  virtual void OnViewClicked(views::View* sender) OVERRIDE {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestDetailsView);
+};
+
+// Trivial item implementation that tracks its views for testing.
+class TestItem : public SystemTrayItem {
+ public:
+  TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {}
+
+  // Overridden from SystemTrayItem:
+  virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
+    tray_view_ = new views::View;
+    return tray_view_;
+  }
+  virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
+    default_view_ = new views::View;
+    default_view_->set_focusable(true);
+    return default_view_;
+  }
+  virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
+    detailed_view_ = new TestDetailsView(this);
+    return detailed_view_;
+  }
+  virtual void DestroyTrayView() OVERRIDE {
+    tray_view_ = NULL;
+  }
+  virtual void DestroyDefaultView() OVERRIDE {
+    default_view_ = NULL;
+  }
+  virtual void DestroyDetailedView() OVERRIDE {
+    detailed_view_ = NULL;
+  }
+
+  views::View* tray_view() const { return tray_view_; }
+  views::View* default_view() const { return default_view_; }
+  TestDetailsView* detailed_view() const { return detailed_view_; }
+
+ private:
+  views::View* tray_view_;
+  views::View* default_view_;
+  TestDetailsView* detailed_view_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestItem);
+};
+
+}  // namespace
+
+typedef AshTestBase TrayDetailsViewTest;
+
+TEST_F(TrayDetailsViewTest, TransitionToDefaultViewTest) {
+  SystemTray* tray = GetSystemTray();
+  ASSERT_TRUE(tray->GetWidget());
+
+  TestItem* test_item_1 = new TestItem;
+  TestItem* test_item_2 = new TestItem;
+  tray->AddTrayItem(test_item_1);
+  tray->AddTrayItem(test_item_2);
+
+  // Ensure the tray views are created.
+  ASSERT_TRUE(test_item_1->tray_view() != NULL);
+  ASSERT_TRUE(test_item_2->tray_view() != NULL);
+
+  // Show the default view.
+  tray->ShowDefaultView(BUBBLE_CREATE_NEW);
+  RunAllPendingInMessageLoop();
+
+  // Show the detailed view of item 2.
+  tray->ShowDetailedView(test_item_2, 0, true, BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(test_item_2->detailed_view());
+  RunAllPendingInMessageLoop();
+  EXPECT_FALSE(test_item_2->default_view());
+
+  // Transition back to default view, the default view of item 2 should have
+  // focus.
+  test_item_2->detailed_view()->CreateFooterAndFocus();
+  test_item_2->detailed_view()->TransitionToDefaultView();
+  RunAllPendingInMessageLoop();
+
+  EXPECT_TRUE(test_item_2->default_view());
+  EXPECT_FALSE(test_item_2->detailed_view());
+  EXPECT_TRUE(test_item_2->default_view()->HasFocus());
+
+  // Show the detailed view of item 2 again.
+  tray->ShowDetailedView(test_item_2, 0, true, BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(test_item_2->detailed_view());
+  RunAllPendingInMessageLoop();
+  EXPECT_FALSE(test_item_2->default_view());
+
+  // Transition back to default view, the default view of item 2 should NOT have
+  // focus.
+  test_item_2->detailed_view()->TransitionToDefaultView();
+  RunAllPendingInMessageLoop();
+
+  EXPECT_TRUE(test_item_2->default_view());
+  EXPECT_FALSE(test_item_2->detailed_view());
+  EXPECT_FALSE(test_item_2->default_view()->HasFocus());
+}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/system/tray/tray_empty.cc b/ash/system/tray/tray_empty.cc
index 27fb730..f15f7b3 100644
--- a/ash/system/tray/tray_empty.cc
+++ b/ash/system/tray/tray_empty.cc
@@ -6,6 +6,7 @@
 
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/background.h"
+#include "ui/views/border.h"
 #include "ui/views/view.h"
 
 namespace {
diff --git a/ash/system/tray/tray_notification_view.cc b/ash/system/tray/tray_notification_view.cc
index aa4b1d1..c095892 100644
--- a/ash/system/tray/tray_notification_view.cc
+++ b/ash/system/tray/tray_notification_view.cc
@@ -10,6 +10,7 @@
 #include "grit/ui_resources.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/views/background.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/grid_layout.h"
diff --git a/ash/system/tray/tray_popup_header_button.cc b/ash/system/tray/tray_popup_header_button.cc
index 7e20c00..e9430e5 100644
--- a/ash/system/tray/tray_popup_header_button.cc
+++ b/ash/system/tray/tray_popup_header_button.cc
@@ -8,6 +8,7 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
+#include "ui/views/painter.h"
 
 namespace ash {
 namespace internal {
@@ -37,6 +38,10 @@
   SetAccessibleName(bundle.GetLocalizedString(accessible_name_id));
   set_focusable(true);
   set_request_focus_on_press(false);
+
+  SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+                      kFocusBorderColor,
+                      gfx::Insets(1, 2, 2, 3)));
 }
 
 TrayPopupHeaderButton::~TrayPopupHeaderButton() {}
@@ -57,13 +62,6 @@
       ash::kBorderDarkColor);
 }
 
-void TrayPopupHeaderButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
-  if (HasFocus()) {
-    canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
-                     kFocusBorderColor);
-  }
-}
-
 void TrayPopupHeaderButton::StateChanged() {
   SchedulePaint();
 }
diff --git a/ash/system/tray/tray_popup_header_button.h b/ash/system/tray/tray_popup_header_button.h
index f9209c8..37849bc 100644
--- a/ash/system/tray/tray_popup_header_button.h
+++ b/ash/system/tray/tray_popup_header_button.h
@@ -32,7 +32,6 @@
   virtual const char* GetClassName() const OVERRIDE;
   virtual gfx::Size GetPreferredSize() OVERRIDE;
   virtual void OnPaintBorder(gfx::Canvas* canvas) OVERRIDE;
-  virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
 
   // Overridden from views::CustomButton:
   virtual void StateChanged() OVERRIDE;
diff --git a/ash/system/tray/tray_popup_label_button.cc b/ash/system/tray/tray_popup_label_button.cc
index 43cf3bd..2f97c37 100644
--- a/ash/system/tray/tray_popup_label_button.cc
+++ b/ash/system/tray/tray_popup_label_button.cc
@@ -8,6 +8,7 @@
 #include "ash/system/tray/tray_popup_label_button_border.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/rect.h"
+#include "ui/views/painter.h"
 
 namespace ash {
 namespace internal {
@@ -20,16 +21,12 @@
   set_request_focus_on_press(false);
   set_animate_on_state_change(false);
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+      kFocusBorderColor,
+      gfx::Insets(1, 1, 2, 2)));
 }
 
 TrayPopupLabelButton::~TrayPopupLabelButton() {}
 
-void TrayPopupLabelButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
-  if (HasFocus()) {
-    canvas->DrawRect(gfx::Rect(1, 1, width() - 3, height() - 3),
-                     ash::kFocusBorderColor);
-  }
-}
-
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/system/tray/tray_popup_label_button.h b/ash/system/tray/tray_popup_label_button.h
index 8d21abd..a1f9db6 100644
--- a/ash/system/tray/tray_popup_label_button.h
+++ b/ash/system/tray/tray_popup_label_button.h
@@ -21,9 +21,6 @@
   virtual ~TrayPopupLabelButton();
 
  private:
-  // Overridden from views::LabelButton:
-  virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
-
   DISALLOW_COPY_AND_ASSIGN(TrayPopupLabelButton);
 };
 
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index 63fd215..f064a5f 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -229,7 +229,7 @@
   AccessibilityDelegate* delegate =
       Shell::GetInstance()->accessibility_delegate();
   if (sender == footer()->content()) {
-    owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+    TransitionToDefaultView();
   } else if (sender == spoken_feedback_view_) {
     delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
   } else if (sender == high_contrast_view_) {
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc
index 8d1499d..e0d9587 100644
--- a/ash/system/user/tray_user.cc
+++ b/ash/system/user/tray_user.cc
@@ -481,12 +481,12 @@
   // user.
   base::string16 display_name =
       Shell::GetInstance()->session_state_delegate()->GetUserDisplayName(0);
-  RemoveChars(display_name, kDisplayNameMark, &display_name);
+  base::RemoveChars(display_name, kDisplayNameMark, &display_name);
   display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
   // Retrieve the domain managing the device and wrap it with markers.
   base::string16 domain = UTF8ToUTF16(
       Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
-  RemoveChars(domain, kDisplayNameMark, &domain);
+  base::RemoveChars(domain, kDisplayNameMark, &domain);
   base::i18n::WrapStringWithLTRFormatting(&domain);
   // Retrieve the label text, inserting the display name and domain.
   text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index 09e615e..b0b75d8 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -416,6 +416,9 @@
 }
 
 void WebNotificationTray::HidePopups() {
+  DCHECK(popup_collection_.get());
+
+  popup_collection_->MarkAllPopupsShown();
   popup_collection_.reset();
   work_area_observer_->StopObserving();
 }
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc
index 5ae2e49..b64e2b8 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/web_notification/web_notification_tray_unittest.cc
@@ -208,6 +208,16 @@
   // Removing the visible notification should hide the popup bubble.
   RemoveNotification("test_id3");
   EXPECT_FALSE(GetTray()->IsPopupVisible());
+
+  // Now test that we can show multiple popups and then show the message center.
+  AddNotification("test_id4");
+  AddNotification("test_id5");
+  EXPECT_TRUE(GetTray()->IsPopupVisible());
+
+  GetTray()->message_center_tray_->ShowMessageCenterBubble();
+  GetTray()->message_center_tray_->HideMessageCenterBubble();
+
+  EXPECT_FALSE(GetTray()->IsPopupVisible());
 }
 
 using message_center::NotificationList;
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index cd4174a..335972d 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -138,8 +138,7 @@
       metro_viewer_host_.reset(
           new TestMetroViewerProcessHost(ipc_thread_->message_loop_proxy()));
       CHECK(metro_viewer_host_->LaunchViewerAndWaitForConnection(
-          win8::test::kDefaultTestAppUserModelId,
-          L"test_open"));
+          win8::test::kDefaultTestAppUserModelId));
       aura::RemoteRootWindowHostWin* root_window_host =
           aura::RemoteRootWindowHostWin::Instance();
       CHECK(root_window_host != NULL);
diff --git a/ash/test/ash_unittests.cc b/ash/test/ash_unittests.cc
index a499452..040e40f 100644
--- a/ash/test/ash_unittests.cc
+++ b/ash/test/ash_unittests.cc
@@ -8,8 +8,10 @@
 
 int main(int argc, char** argv) {
   ash::test::AuraShellTestSuite test_suite(argc, argv);
-  return base::LaunchUnitTests(argc,
-                               argv,
-                               base::Bind(&ash::test::AuraShellTestSuite::Run,
-                                          base::Unretained(&test_suite)));
+
+  return base::LaunchUnitTestsSerially(
+      argc,
+      argv,
+      base::Bind(&ash::test::AuraShellTestSuite::Run,
+                 base::Unretained(&test_suite)));
 }
diff --git a/ash/test/launcher_item_delegate_manager_test_api.cc b/ash/test/launcher_item_delegate_manager_test_api.cc
deleted file mode 100644
index 159f7da..0000000
--- a/ash/test/launcher_item_delegate_manager_test_api.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2013 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.
-
-#include "ash/test/launcher_item_delegate_manager_test_api.h"
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
-#include "base/stl_util.h"
-
-namespace ash {
-namespace test {
-
-LauncherItemDelegateManagerTestAPI::LauncherItemDelegateManagerTestAPI(
-    LauncherItemDelegateManager* manager)
-    : manager_(manager) {
-  DCHECK(manager_);
-}
-
-void
-LauncherItemDelegateManagerTestAPI::RemoveAllLauncherItemDelegateForTest() {
-  STLDeleteContainerPairSecondPointers(
-      manager_->id_to_item_delegate_map_.begin(),
-      manager_->id_to_item_delegate_map_.end());
-  manager_->id_to_item_delegate_map_.clear();
-}
-
-}  // namespace test
-}  // namespace ash
diff --git a/ash/test/launcher_item_delegate_manager_test_api.h b/ash/test/launcher_item_delegate_manager_test_api.h
deleted file mode 100644
index d916a05..0000000
--- a/ash/test/launcher_item_delegate_manager_test_api.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2013 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 ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_
-#define ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_
-
-#include "base/basictypes.h"
-
-namespace ash {
-
-class LauncherItemDelegateManager;
-
-namespace test {
-
-// Accesses private methods from a LauncherItemDelegateManager for testing.
-class LauncherItemDelegateManagerTestAPI {
- public:
-  explicit LauncherItemDelegateManagerTestAPI(
-      LauncherItemDelegateManager* manager);
-
-  // Clear all exsiting LauncherItemDelegate for test.
-  void RemoveAllLauncherItemDelegateForTest();
-
- private:
-  LauncherItemDelegateManager* manager_;  // Not owned.
-
-  DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManagerTestAPI);
-};
-
-}  // namespace test
-}  // namespace ash
-
-#endif  // ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_
diff --git a/ash/test/launcher_test_api.cc b/ash/test/launcher_test_api.cc
index 86137f4..2e56a86 100644
--- a/ash/test/launcher_test_api.cc
+++ b/ash/test/launcher_test_api.cc
@@ -20,7 +20,7 @@
   return launcher_->shelf_view_;
 }
 
-void LauncherTestAPI::SetLauncherDelegate(LauncherDelegate* delegate) {
+void LauncherTestAPI::SetShelfDelegate(ShelfDelegate* delegate) {
   launcher_->delegate_ = delegate;
 }
 
diff --git a/ash/test/launcher_test_api.h b/ash/test/launcher_test_api.h
index c680e93..d838339 100644
--- a/ash/test/launcher_test_api.h
+++ b/ash/test/launcher_test_api.h
@@ -10,7 +10,7 @@
 namespace ash {
 
 class Launcher;
-class LauncherDelegate;
+class ShelfDelegate;
 
 namespace internal {
 class ShelfView;
@@ -28,8 +28,8 @@
   // An accessor for |shelf_view|.
   internal::ShelfView* shelf_view();
 
-  // Set LauncherDelegate.
-  void SetLauncherDelegate(LauncherDelegate* delegate);
+  // Set ShelfDelegate.
+  void SetShelfDelegate(ShelfDelegate* delegate);
 
  private:
   Launcher* launcher_;
diff --git a/ash/test/shelf_item_delegate_manager_test_api.cc b/ash/test/shelf_item_delegate_manager_test_api.cc
new file mode 100644
index 0000000..740164d
--- /dev/null
+++ b/ash/test/shelf_item_delegate_manager_test_api.cc
@@ -0,0 +1,28 @@
+// Copyright 2013 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.
+
+#include "ash/test/shelf_item_delegate_manager_test_api.h"
+
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
+#include "base/stl_util.h"
+
+namespace ash {
+namespace test {
+
+ShelfItemDelegateManagerTestAPI::ShelfItemDelegateManagerTestAPI(
+    ShelfItemDelegateManager* manager)
+    : manager_(manager) {
+  DCHECK(manager_);
+}
+
+void ShelfItemDelegateManagerTestAPI::RemoveAllShelfItemDelegateForTest() {
+  STLDeleteContainerPairSecondPointers(
+      manager_->id_to_item_delegate_map_.begin(),
+      manager_->id_to_item_delegate_map_.end());
+  manager_->id_to_item_delegate_map_.clear();
+}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/test/shelf_item_delegate_manager_test_api.h b/ash/test/shelf_item_delegate_manager_test_api.h
new file mode 100644
index 0000000..183bab5
--- /dev/null
+++ b/ash/test/shelf_item_delegate_manager_test_api.h
@@ -0,0 +1,33 @@
+// Copyright 2013 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 ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_
+#define ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_
+
+#include "base/basictypes.h"
+
+namespace ash {
+
+class ShelfItemDelegateManager;
+
+namespace test {
+
+// Accesses private methods from a ShelfItemDelegateManager for testing.
+class ShelfItemDelegateManagerTestAPI {
+ public:
+  explicit ShelfItemDelegateManagerTestAPI(ShelfItemDelegateManager* manager);
+
+  // Clear all exsiting ShelfItemDelegate for test.
+  void RemoveAllShelfItemDelegateForTest();
+
+ private:
+  ShelfItemDelegateManager* manager_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateManagerTestAPI);
+};
+
+}  // namespace test
+}  // namespace ash
+
+#endif  // ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_
diff --git a/ash/test/shelf_view_test_api.cc b/ash/test/shelf_view_test_api.cc
index 568eda9..b0e0dff 100644
--- a/ash/test/shelf_view_test_api.cc
+++ b/ash/test/shelf_view_test_api.cc
@@ -120,7 +120,7 @@
   return shelf_view_->SameDragType(typea, typeb);
 }
 
-void ShelfViewTestAPI::SetLauncherDelegate(LauncherDelegate* delegate) {
+void ShelfViewTestAPI::SetShelfDelegate(ShelfDelegate* delegate) {
   shelf_view_->delegate_ = delegate;
 }
 
@@ -128,5 +128,13 @@
   return shelf_view_->GetBoundsForDragInsertInScreen();
 }
 
+bool ShelfViewTestAPI::IsDraggingShelfItem() {
+  return shelf_view_->dragging();
+}
+
+bool ShelfViewTestAPI::DraggedItemFromOverflowToShelf() {
+    return shelf_view_->dragged_off_from_overflow_to_shelf_;
+}
+
 }  // namespace test
 }  // namespace ash
diff --git a/ash/test/shelf_view_test_api.h b/ash/test/shelf_view_test_api.h
index d26c02d..56ca723 100644
--- a/ash/test/shelf_view_test_api.h
+++ b/ash/test/shelf_view_test_api.h
@@ -15,7 +15,7 @@
 
 namespace ash {
 
-class LauncherDelegate;
+class ShelfDelegate;
 
 namespace internal {
 class OverflowBubble;
@@ -77,12 +77,18 @@
   // Wrapper for ShelfView::SameDragType.
   bool SameDragType(LauncherItemType typea, LauncherItemType typeb) const;
 
-  // Sets LauncherDelegate.
-  void SetLauncherDelegate(LauncherDelegate* delegate);
+  // Sets ShelfDelegate.
+  void SetShelfDelegate(ShelfDelegate* delegate);
 
   // Returns re-insertable bounds in screen.
   gfx::Rect GetBoundsForDragInsertInScreen();
 
+  // Returns true if item is ripped off.
+  bool IsDraggingShelfItem();
+
+  // Returns true if an item is ripped off and entered into shelf.
+  bool DraggedItemFromOverflowToShelf();
+
  private:
   internal::ShelfView* shelf_view_;
 
diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc
index 353a76c..5fe130e 100644
--- a/ash/test/shell_test_api.cc
+++ b/ash/test/shell_test_api.cc
@@ -4,8 +4,8 @@
 
 #include "ash/test/shell_test_api.h"
 
-#include "ash/launcher/launcher_delegate.h"
 #include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_delegate.h"
 #include "ash/shell.h"
 
 #if defined(OS_CHROMEOS)
@@ -67,8 +67,8 @@
 #endif  // defined(OS_CHROMEOS)
 }
 
-void ShellTestApi::SetLauncherDelegate(LauncherDelegate* delegate) {
-  shell_->launcher_delegate_.reset(delegate);
+void ShellTestApi::SetShelfDelegate(ShelfDelegate* delegate) {
+  shell_->shelf_delegate_.reset(delegate);
 }
 
 }  // namespace test
diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h
index 9457e75..269eb78 100644
--- a/ash/test/shell_test_api.h
+++ b/ash/test/shell_test_api.h
@@ -15,7 +15,7 @@
 
 namespace ash {
 class AshNativeCursorManager;
-class LauncherDelegate;
+class ShelfDelegate;
 class ShelfModel;
 class Shell;
 
@@ -47,8 +47,8 @@
 
   void DisableOutputConfiguratorAnimation();
 
-  // Set LauncherDelegate.
-  void SetLauncherDelegate(LauncherDelegate* delegate);
+  // Set ShelfDelegate.
+  void SetShelfDelegate(ShelfDelegate* delegate);
 
  private:
   Shell* shell_;  // not owned
diff --git a/ash/test/test_launcher_delegate.cc b/ash/test/test_launcher_delegate.cc
deleted file mode 100644
index 67e8be3..0000000
--- a/ash/test/test_launcher_delegate.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "ash/test/test_launcher_delegate.h"
-
-#include "ash/launcher/launcher_item_delegate_manager.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shelf/shelf_util.h"
-#include "ash/shell.h"
-#include "ash/test/test_launcher_item_delegate.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "grit/ash_resources.h"
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace test {
-
-TestLauncherDelegate* TestLauncherDelegate::instance_ = NULL;
-
-TestLauncherDelegate::TestLauncherDelegate(ShelfModel* model)
-    : model_(model) {
-  CHECK(!instance_);
-  instance_ = this;
-}
-
-TestLauncherDelegate::~TestLauncherDelegate() {
-  instance_ = NULL;
-}
-
-void TestLauncherDelegate::AddLauncherItem(aura::Window* window) {
-  AddLauncherItem(window, STATUS_CLOSED);
-}
-
-void TestLauncherDelegate::AddLauncherItem(
-    aura::Window* window,
-    LauncherItemStatus status) {
-  ash::LauncherItem item;
-  if (window->type() == aura::client::WINDOW_TYPE_PANEL)
-    item.type = ash::TYPE_APP_PANEL;
-  else
-    item.type = ash::TYPE_PLATFORM_APP;
-  LauncherID id = model_->next_id();
-  item.status = status;
-  model_->Add(item);
-  window->AddObserver(this);
-
-  ash::LauncherItemDelegateManager* manager =
-      ash::Shell::GetInstance()->launcher_item_delegate_manager();
-  // |manager| owns TestLauncherItemDelegate.
-  scoped_ptr<LauncherItemDelegate> delegate(
-      new TestLauncherItemDelegate(window));
-  manager->SetLauncherItemDelegate(id, delegate.Pass());
-  SetLauncherIDForWindow(id, window);
-}
-
-void TestLauncherDelegate::RemoveLauncherItemForWindow(aura::Window* window) {
-  ash::LauncherID id = GetLauncherIDForWindow(window);
-  if (id == 0)
-    return;
-  int index = model_->ItemIndexByID(id);
-  DCHECK_NE(-1, index);
-  model_->RemoveItemAt(index);
-  window->RemoveObserver(this);
-}
-
-void TestLauncherDelegate::OnWindowDestroying(aura::Window* window) {
-  RemoveLauncherItemForWindow(window);
-}
-
-void TestLauncherDelegate::OnWindowHierarchyChanging(
-      const HierarchyChangeParams& params) {
-  // The window may be legitimately reparented while staying open if it moves
-  // to another display or container. If the window does not have a new parent
-  // then remove the launcher item.
-  if (!params.new_parent)
-    RemoveLauncherItemForWindow(params.target);
-}
-
-void TestLauncherDelegate::OnLauncherCreated(Launcher* launcher) {
-}
-
-void TestLauncherDelegate::OnLauncherDestroyed(Launcher* launcher) {
-}
-
-LauncherID TestLauncherDelegate::GetLauncherIDForAppID(
-    const std::string& app_id) {
-  return 0;
-}
-
-const std::string& TestLauncherDelegate::GetAppIDForLauncherID(LauncherID id) {
-  return EmptyString();
-}
-
-void TestLauncherDelegate::PinAppWithID(const std::string& app_id) {
-}
-
-bool TestLauncherDelegate::CanPin() const {
-  return true;
-}
-
-bool TestLauncherDelegate::IsAppPinned(const std::string& app_id) {
-  return false;
-}
-
-void TestLauncherDelegate::UnpinAppWithID(const std::string& app_id) {
-}
-
-}  // namespace test
-}  // namespace ash
diff --git a/ash/test/test_launcher_item_delegate.cc b/ash/test/test_launcher_item_delegate.cc
deleted file mode 100644
index 3913c68..0000000
--- a/ash/test/test_launcher_item_delegate.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2013 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.
-
-#include "ash/test/test_launcher_item_delegate.h"
-
-#include "ash/wm/window_util.h"
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace test {
-
-TestLauncherItemDelegate::TestLauncherItemDelegate(aura::Window* window)
-    : window_(window) {
-}
-
-TestLauncherItemDelegate::~TestLauncherItemDelegate() {
-}
-
-bool TestLauncherItemDelegate::ItemSelected(const ui::Event& event) {
-  if (window_) {
-    if (window_->type() == aura::client::WINDOW_TYPE_PANEL)
-      ash::wm::MoveWindowToEventRoot(window_, event);
-    window_->Show();
-    ash::wm::ActivateWindow(window_);
-  }
-  return false;
-}
-
-base::string16 TestLauncherItemDelegate::GetTitle() {
-  return window_ ? window_->title() : base::string16();
-}
-
-ui::MenuModel* TestLauncherItemDelegate::CreateContextMenu(
-    aura::Window* root_window) {
-  return NULL;
-}
-
-ash::LauncherMenuModel* TestLauncherItemDelegate::CreateApplicationMenu(
-    int event_flags) {
-  return NULL;
-}
-
-bool TestLauncherItemDelegate::IsDraggable() {
-  return true;
-}
-
-bool TestLauncherItemDelegate::ShouldShowTooltip() {
-  return true;
-}
-
-}  // namespace test
-}  // namespace ash
diff --git a/ash/test/test_launcher_item_delegate.h b/ash/test/test_launcher_item_delegate.h
deleted file mode 100644
index a2f6310..0000000
--- a/ash/test/test_launcher_item_delegate.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 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 ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_
-#define ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-
-namespace aura {
-class Window;
-}
-
-namespace ash {
-namespace test {
-
-// Test implementation of ash::LauncherItemDelegate.
-class TestLauncherItemDelegate : public ash::LauncherItemDelegate {
- public:
-  explicit TestLauncherItemDelegate(aura::Window* window);
-  virtual ~TestLauncherItemDelegate();
-
-  // ash::LauncherItemDelegate overrides:
-  virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
-  virtual base::string16 GetTitle() OVERRIDE;
-  virtual ui::MenuModel* CreateContextMenu(
-      aura::Window* root_window) OVERRIDE;
-  virtual ash::LauncherMenuModel* CreateApplicationMenu(
-      int event_flags) OVERRIDE;
-  virtual bool IsDraggable() OVERRIDE;
-  virtual bool ShouldShowTooltip() OVERRIDE;
-
- private:
-  aura::Window* window_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestLauncherItemDelegate);
-};
-
-}  // namespace test
-}  // namespace ash
-
-#endif  // ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_
diff --git a/ash/test/test_metro_viewer_process_host.cc b/ash/test/test_metro_viewer_process_host.cc
index 3a58433..a5e3fbf 100644
--- a/ash/test/test_metro_viewer_process_host.cc
+++ b/ash/test/test_metro_viewer_process_host.cc
@@ -42,5 +42,9 @@
     const string16& search_string) {
 }
 
+void TestMetroViewerProcessHost::OnWindowSizeChanged(uint32 width,
+                                                     uint32 height) {
+}
+
 }  // namespace test
 }  // namespace ash
diff --git a/ash/test/test_metro_viewer_process_host.h b/ash/test/test_metro_viewer_process_host.h
index e6d0a64..7c3770e 100644
--- a/ash/test/test_metro_viewer_process_host.h
+++ b/ash/test/test_metro_viewer_process_host.h
@@ -26,6 +26,7 @@
   virtual void OnSetTargetSurface(gfx::NativeViewId target_surface) OVERRIDE;
   virtual void OnOpenURL(const string16& url) OVERRIDE;
   virtual void OnHandleSearchRequest(const string16& search_string) OVERRIDE;
+  virtual void OnWindowSizeChanged(uint32 width, uint32 height) OVERRIDE;
 
   scoped_ptr<AcceleratedSurface> backing_surface_;
 
diff --git a/ash/test/test_session_state_delegate.cc b/ash/test/test_session_state_delegate.cc
index 8032f4f..5c16807 100644
--- a/ash/test/test_session_state_delegate.cc
+++ b/ash/test/test_session_state_delegate.cc
@@ -147,7 +147,7 @@
   activated_user_ = user_id;
 }
 
-void TestSessionStateDelegate::SwitchActiveUserToNext() {
+void TestSessionStateDelegate::CycleActiveUser(CycleUser cycle_user) {
   activated_user_ = "someone@tray";
 }
 
diff --git a/ash/test/test_session_state_delegate.h b/ash/test/test_session_state_delegate.h
index fcd375d..8c26c88 100644
--- a/ash/test/test_session_state_delegate.h
+++ b/ash/test/test_session_state_delegate.h
@@ -41,7 +41,7 @@
       ash::MultiProfileIndex index) const OVERRIDE;
   virtual void GetLoggedInUsers(UserIdList* users) OVERRIDE;
   virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE;
-  virtual void SwitchActiveUserToNext() OVERRIDE;
+  virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE;
   virtual void AddSessionStateObserver(
       ash::SessionStateObserver* observer) OVERRIDE;
   virtual void RemoveSessionStateObserver(
diff --git a/ash/test/test_shelf_delegate.cc b/ash/test/test_shelf_delegate.cc
new file mode 100644
index 0000000..c880d24
--- /dev/null
+++ b/ash/test/test_shelf_delegate.cc
@@ -0,0 +1,108 @@
+// Copyright 2013 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.
+
+#include "ash/test/test_shelf_delegate.h"
+
+#include "ash/shelf/shelf_item_delegate_manager.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shelf/shelf_util.h"
+#include "ash/shell.h"
+#include "ash/test/test_shelf_item_delegate.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "grit/ash_resources.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace test {
+
+TestShelfDelegate* TestShelfDelegate::instance_ = NULL;
+
+TestShelfDelegate::TestShelfDelegate(ShelfModel* model)
+    : model_(model) {
+  CHECK(!instance_);
+  instance_ = this;
+}
+
+TestShelfDelegate::~TestShelfDelegate() {
+  instance_ = NULL;
+}
+
+void TestShelfDelegate::AddLauncherItem(aura::Window* window) {
+  AddLauncherItem(window, STATUS_CLOSED);
+}
+
+void TestShelfDelegate::AddLauncherItem(aura::Window* window,
+                                        LauncherItemStatus status) {
+  LauncherItem item;
+  if (window->type() == aura::client::WINDOW_TYPE_PANEL)
+    item.type = TYPE_APP_PANEL;
+  else
+    item.type = TYPE_PLATFORM_APP;
+  LauncherID id = model_->next_id();
+  item.status = status;
+  model_->Add(item);
+  window->AddObserver(this);
+
+  ShelfItemDelegateManager* manager =
+      Shell::GetInstance()->shelf_item_delegate_manager();
+  // |manager| owns TestShelfItemDelegate.
+  scoped_ptr<ShelfItemDelegate> delegate(new TestShelfItemDelegate(window));
+  manager->SetShelfItemDelegate(id, delegate.Pass());
+  SetLauncherIDForWindow(id, window);
+}
+
+void TestShelfDelegate::RemoveLauncherItemForWindow(aura::Window* window) {
+  LauncherID id = GetLauncherIDForWindow(window);
+  if (id == 0)
+    return;
+  int index = model_->ItemIndexByID(id);
+  DCHECK_NE(-1, index);
+  model_->RemoveItemAt(index);
+  window->RemoveObserver(this);
+}
+
+void TestShelfDelegate::OnWindowDestroying(aura::Window* window) {
+  RemoveLauncherItemForWindow(window);
+}
+
+void TestShelfDelegate::OnWindowHierarchyChanging(
+      const HierarchyChangeParams& params) {
+  // The window may be legitimately reparented while staying open if it moves
+  // to another display or container. If the window does not have a new parent
+  // then remove the launcher item.
+  if (!params.new_parent)
+    RemoveLauncherItemForWindow(params.target);
+}
+
+void TestShelfDelegate::OnLauncherCreated(Launcher* launcher) {
+}
+
+void TestShelfDelegate::OnLauncherDestroyed(Launcher* launcher) {
+}
+
+LauncherID TestShelfDelegate::GetLauncherIDForAppID(const std::string& app_id) {
+  return 0;
+}
+
+const std::string& TestShelfDelegate::GetAppIDForLauncherID(LauncherID id) {
+  return base::EmptyString();
+}
+
+void TestShelfDelegate::PinAppWithID(const std::string& app_id) {
+}
+
+bool TestShelfDelegate::CanPin() const {
+  return true;
+}
+
+bool TestShelfDelegate::IsAppPinned(const std::string& app_id) {
+  return false;
+}
+
+void TestShelfDelegate::UnpinAppWithID(const std::string& app_id) {
+}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/test/test_launcher_delegate.h b/ash/test/test_shelf_delegate.h
similarity index 66%
rename from ash/test/test_launcher_delegate.h
rename to ash/test/test_shelf_delegate.h
index 11328bc..e475453 100644
--- a/ash/test/test_launcher_delegate.h
+++ b/ash/test/test_shelf_delegate.h
@@ -1,14 +1,14 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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 ASH_TEST_TEST_LAUNCHER_DELEGATE_H_
-#define ASH_TEST_TEST_LAUNCHER_DELEGATE_H_
+#ifndef ASH_TEST_TEST_SHELF_DELEGATE_H_
+#define ASH_TEST_TEST_SHELF_DELEGATE_H_
 
 #include <map>
 #include <set>
 
-#include "ash/launcher/launcher_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
 #include "base/compiler_specific.h"
 #include "ui/aura/window_observer.h"
 
@@ -18,26 +18,25 @@
 
 namespace test {
 
-// Test implementation of LauncherDelegate.
+// Test implementation of ShelfDelegate.
 // Tests may create icons for windows by calling AddLauncherItem
-class TestLauncherDelegate : public LauncherDelegate,
-                             public aura::WindowObserver {
+class TestShelfDelegate : public ShelfDelegate, public aura::WindowObserver {
  public:
-  explicit TestLauncherDelegate(ShelfModel* model);
-  virtual ~TestLauncherDelegate();
+  explicit TestShelfDelegate(ShelfModel* model);
+  virtual ~TestShelfDelegate();
 
   void AddLauncherItem(aura::Window* window);
   void AddLauncherItem(aura::Window* window, LauncherItemStatus status);
   void RemoveLauncherItemForWindow(aura::Window* window);
 
-  static TestLauncherDelegate* instance() { return instance_; }
+  static TestShelfDelegate* instance() { return instance_; }
 
   // WindowObserver implementation
   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
   virtual void OnWindowHierarchyChanging(
       const HierarchyChangeParams& params) OVERRIDE;
 
-  // LauncherDelegate implementation.
+  // ShelfDelegate implementation.
   virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
   virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
   virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
@@ -48,14 +47,14 @@
   virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE;
 
  private:
-  static TestLauncherDelegate* instance_;
+  static TestShelfDelegate* instance_;
 
   ShelfModel* model_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestLauncherDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestShelfDelegate);
 };
 
 }  // namespace test
 }  // namespace ash
 
-#endif  // ASH_TEST_TEST_LAUNCHER_DELEGATE_H_
+#endif  // ASH_TEST_TEST_SHELF_DELEGATE_H_
diff --git a/ash/test/test_shelf_item_delegate.cc b/ash/test/test_shelf_item_delegate.cc
new file mode 100644
index 0000000..b09d9d7
--- /dev/null
+++ b/ash/test/test_shelf_item_delegate.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 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.
+
+#include "ash/test/test_shelf_item_delegate.h"
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace test {
+
+TestShelfItemDelegate::TestShelfItemDelegate(aura::Window* window)
+    : window_(window) {
+}
+
+TestShelfItemDelegate::~TestShelfItemDelegate() {
+}
+
+bool TestShelfItemDelegate::ItemSelected(const ui::Event& event) {
+  if (window_) {
+    if (window_->type() == aura::client::WINDOW_TYPE_PANEL)
+      wm::MoveWindowToEventRoot(window_, event);
+    window_->Show();
+    wm::ActivateWindow(window_);
+  }
+  return false;
+}
+
+base::string16 TestShelfItemDelegate::GetTitle() {
+  return window_ ? window_->title() : base::string16();
+}
+
+ui::MenuModel* TestShelfItemDelegate::CreateContextMenu(
+    aura::Window* root_window) {
+  return NULL;
+}
+
+ShelfMenuModel* TestShelfItemDelegate::CreateApplicationMenu(int event_flags) {
+  return NULL;
+}
+
+bool TestShelfItemDelegate::IsDraggable() {
+  return true;
+}
+
+bool TestShelfItemDelegate::ShouldShowTooltip() {
+  return true;
+}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/test/test_shelf_item_delegate.h b/ash/test/test_shelf_item_delegate.h
new file mode 100644
index 0000000..af8a26a
--- /dev/null
+++ b/ash/test/test_shelf_item_delegate.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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 ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_
+#define ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_
+
+#include "ash/shelf/shelf_item_delegate.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+namespace test {
+
+// Test implementation of ShelfItemDelegate.
+class TestShelfItemDelegate : public ShelfItemDelegate {
+ public:
+  explicit TestShelfItemDelegate(aura::Window* window);
+  virtual ~TestShelfItemDelegate();
+
+  // ShelfItemDelegate:
+  virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
+  virtual base::string16 GetTitle() OVERRIDE;
+  virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE;
+  virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
+  virtual bool IsDraggable() OVERRIDE;
+  virtual bool ShouldShowTooltip() OVERRIDE;
+
+ private:
+  aura::Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestShelfItemDelegate);
+};
+
+}  // namespace test
+}  // namespace ash
+
+#endif  // ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index 40284d9..fcf9562 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -15,8 +15,8 @@
 #include "ash/shell.h"
 #include "ash/shell/keyboard_controller_proxy_stub.h"
 #include "ash/shell_window_ids.h"
-#include "ash/test/test_launcher_delegate.h"
 #include "ash/test/test_session_state_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/test/test_system_tray_delegate.h"
 #include "ash/test/test_user_wallpaper_delegate.h"
 #include "ash/wm/window_state.h"
@@ -101,8 +101,8 @@
   return new app_list::test::AppListTestViewDelegate;
 }
 
-LauncherDelegate* TestShellDelegate::CreateLauncherDelegate(ShelfModel* model) {
-  return new TestLauncherDelegate(model);
+ShelfDelegate* TestShellDelegate::CreateShelfDelegate(ShelfModel* model) {
+  return new TestShelfDelegate(model);
 }
 
 SystemTrayDelegate* TestShellDelegate::CreateSystemTrayDelegate() {
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index 3edd95c..1a34462 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -41,7 +41,7 @@
       CreateKeyboardControllerProxy() OVERRIDE;
   virtual content::BrowserContext* GetCurrentBrowserContext() OVERRIDE;
   virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE;
-  virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) OVERRIDE;
+  virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) OVERRIDE;
   virtual SystemTrayDelegate* CreateSystemTrayDelegate() OVERRIDE;
   virtual UserWallpaperDelegate* CreateUserWallpaperDelegate() OVERRIDE;
   virtual CapsLockDelegate* CreateCapsLockDelegate() OVERRIDE;
diff --git a/ash/touch/touch_uma.cc b/ash/touch/touch_uma.cc
index 29f6185..730911f 100644
--- a/ash/touch/touch_uma.cc
+++ b/ash/touch/touch_uma.cc
@@ -55,8 +55,10 @@
   UMA_ET_GESTURE_PINCH_UPDATE_3,
   UMA_ET_GESTURE_PINCH_UPDATE_4P,
   UMA_ET_GESTURE_LONG_TAP,
+  UMA_ET_GESTURE_SHOW_PRESS,
+  UMA_ET_GESTURE_TAP_CANCEL,
   // NOTE: Add new event types only immediately above this line. Make sure to
-  // update the enum list in tools/histogram/histograms.xml accordingly.
+  // update the enum list in tools/metrics/histogram/histograms.xml accordingly.
   UMA_ET_COUNT
 };
 
@@ -157,6 +159,10 @@
         return UMA_ET_GESTURE_MULTIFINGER_SWIPE_3;
       return UMA_ET_GESTURE_MULTIFINGER_SWIPE;
     }
+    case ui::ET_GESTURE_TAP_CANCEL:
+      return UMA_ET_GESTURE_TAP_CANCEL;
+    case ui::ET_GESTURE_SHOW_PRESS:
+      return UMA_ET_GESTURE_SHOW_PRESS;
     case ui::ET_SCROLL:
       return UMA_ET_SCROLL;
     case ui::ET_SCROLL_FLING_START:
@@ -164,6 +170,7 @@
     case ui::ET_SCROLL_FLING_CANCEL:
       return UMA_ET_SCROLL_FLING_CANCEL;
     default:
+      NOTREACHED();
       return UMA_ET_UNKNOWN;
   }
 }
@@ -292,7 +299,7 @@
     if (details->last_start_time_.count(event.touch_id())) {
       base::TimeDelta duration = event.time_stamp() -
                                  details->last_start_time_[event.touch_id()];
-      UMA_HISTOGRAM_COUNTS_100("Ash.TouchDuration", duration.InMilliseconds());
+      UMA_HISTOGRAM_TIMES("Ash.TouchDuration", duration);
 
       // Look for touches that were [almost] stationary for a long time.
       const double kLongStationaryTouchDuration = 10;
diff --git a/ash/wm/app_list_controller.cc b/ash/wm/app_list_controller.cc
index df3f724..2f0ce04 100644
--- a/ash/wm/app_list_controller.cc
+++ b/ash/wm/app_list_controller.cc
@@ -290,6 +290,10 @@
           ash::internal::kShellWindowId_MenuContainer);
       if (menu_container->Contains(target))
         return;
+      aura::Window* keyboard_container = root_controller->GetContainer(
+          ash::internal::kShellWindowId_VirtualKeyboardContainer);
+      if (keyboard_container->Contains(target))
+        return;
     }
   }
 
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button.cc b/ash/wm/caption_buttons/alternate_frame_size_button.cc
new file mode 100644
index 0000000..3c5669f
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button.cc
@@ -0,0 +1,211 @@
+// Copyright 2013 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.
+
+#include "ash/wm/caption_buttons/alternate_frame_size_button.h"
+
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "ash/touch/touch_uma.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/workspace/snap_sizer.h"
+#include "ui/gfx/vector2d.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+// The default delay between the user pressing the size button and the buttons
+// adjacent to the size button morphing into buttons for snapping left and
+// right.
+const int kSetButtonsToSnapModeDelayMs = 150;
+
+// The amount that a user can overshoot the snap left / snap right button and
+// keep the snap left / snap right button pressed.
+const int kPressedHitBoundsExpandX = 200;
+const int kPressedHitBoundsExpandY = 50;
+
+}  // namespace
+
+namespace ash {
+
+AlternateFrameSizeButton::AlternateFrameSizeButton(
+    views::ButtonListener* listener,
+    views::Widget* frame,
+    AlternateFrameSizeButtonDelegate* delegate)
+    : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
+      frame_(frame),
+      delegate_(delegate),
+      set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs),
+      in_snap_mode_(false),
+      snap_type_(SNAP_NONE) {
+}
+
+AlternateFrameSizeButton::~AlternateFrameSizeButton() {
+}
+
+bool AlternateFrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
+  // The minimize and close buttons are set to snap left and right when snapping
+  // is enabled. Do not enable snapping if the minimize button is not visible.
+  // The close button is always visible.
+  if (IsTriggerableEvent(event) &&
+      !in_snap_mode_ &&
+      delegate_->IsMinimizeButtonVisible()) {
+    StartSetButtonsToSnapModeTimer(event);
+  }
+  FrameCaptionButton::OnMousePressed(event);
+  return true;
+}
+
+bool AlternateFrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
+  UpdatePressedButton(event);
+  FrameCaptionButton::OnMouseDragged(event);
+  return true;
+}
+
+void AlternateFrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) {
+  if (!IsTriggerableEvent(event) || !CommitSnap(event))
+    FrameCaptionButton::OnMouseReleased(event);
+}
+
+void AlternateFrameSizeButton::OnMouseCaptureLost() {
+  SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+  FrameCaptionButton::OnMouseCaptureLost();
+}
+
+void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
+  if (event->details().touch_points() > 1) {
+    SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+    return;
+  }
+
+  if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
+    StartSetButtonsToSnapModeTimer(*event);
+    // Go through FrameCaptionButton's handling so that the button gets pressed.
+    FrameCaptionButton::OnGestureEvent(event);
+    return;
+  }
+
+  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
+      event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+    UpdatePressedButton(*event);
+    event->SetHandled();
+    return;
+  }
+
+  if (event->type() == ui::ET_GESTURE_TAP ||
+      event->type() == ui::ET_GESTURE_SCROLL_END ||
+      event->type() == ui::ET_SCROLL_FLING_START ||
+      event->type() == ui::ET_GESTURE_END) {
+    if (CommitSnap(*event)) {
+      if (event->type() == ui::ET_GESTURE_TAP) {
+        TouchUMA::GetInstance()->RecordGestureAction(
+            TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
+      }
+      event->SetHandled();
+      return;
+    }
+  }
+
+  FrameCaptionButton::OnGestureEvent(event);
+}
+
+void AlternateFrameSizeButton::StartSetButtonsToSnapModeTimer(
+    const ui::LocatedEvent& event) {
+  set_buttons_to_snap_mode_timer_event_location_ = event.location();
+  if (set_buttons_to_snap_mode_delay_ms_ == 0) {
+    SetButtonsToSnapMode();
+  } else {
+    set_buttons_to_snap_mode_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_),
+        this,
+        &AlternateFrameSizeButton::SetButtonsToSnapMode);
+  }
+}
+
+void AlternateFrameSizeButton::SetButtonsToSnapMode() {
+  if (in_snap_mode_)
+    return;
+  in_snap_mode_ = true;
+  delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
+                            CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
+                            AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+}
+
+void AlternateFrameSizeButton::UpdatePressedButton(
+    const ui::LocatedEvent& event) {
+  if (!in_snap_mode_) {
+    // Set the buttons adjacent to the size button to snap left and right early
+    // if the user drags past the drag threshold.
+    // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap
+    // mode as a result of an unsupported drag type (e.g. only the right mouse
+    // button is pressed).
+    gfx::Vector2d delta(
+        event.location() - set_buttons_to_snap_mode_timer_event_location_);
+    if (!set_buttons_to_snap_mode_timer_.IsRunning() ||
+        !views::View::ExceededDragThreshold(delta)) {
+      return;
+    }
+    SetButtonsToSnapMode();
+  }
+
+  gfx::Point event_location_in_screen(event.location());
+  views::View::ConvertPointToScreen(this, &event_location_in_screen);
+
+  gfx::Insets pressed_button_hittest_insets(-kPressedHitBoundsExpandY,
+                                            -kPressedHitBoundsExpandX,
+                                            -kPressedHitBoundsExpandY,
+                                            -kPressedHitBoundsExpandX);
+  const FrameCaptionButton* pressed_button = delegate_->PressButtonAt(
+      event_location_in_screen, pressed_button_hittest_insets);
+  snap_type_ = SNAP_NONE;
+  if (pressed_button) {
+    switch (pressed_button->icon()) {
+      case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
+        snap_type_ = SNAP_LEFT;
+        break;
+      case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
+        snap_type_ = SNAP_RIGHT;
+        break;
+      case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
+        // snap_type_ = SNAP_NONE
+        break;
+      case CAPTION_BUTTON_ICON_MINIMIZE:
+      case CAPTION_BUTTON_ICON_CLOSE:
+        NOTREACHED();
+        break;
+    }
+  }
+}
+
+bool AlternateFrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
+  // The position of |event| may be different than the position of the previous
+  // event.
+  UpdatePressedButton(event);
+
+  if (in_snap_mode_ &&
+      (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
+    using internal::SnapSizer;
+    SnapSizer::SnapWindow(ash::wm::GetWindowState(frame_->GetNativeWindow()),
+                          snap_type_ == SNAP_LEFT ?
+                              SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE);
+    ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
+        snap_type_ == SNAP_LEFT ?
+            ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
+            ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
+    SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_NO);
+    return true;
+  }
+  SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+  return false;
+}
+
+void AlternateFrameSizeButton::SetButtonsToNormalMode(
+    AlternateFrameSizeButtonDelegate::Animate animate) {
+  in_snap_mode_ = false;
+  snap_type_ = SNAP_NONE;
+  set_buttons_to_snap_mode_timer_.Stop();
+  delegate_->SetButtonsToNormal(animate);
+}
+
+}  // namespace ash
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button.h b/ash/wm/caption_buttons/alternate_frame_size_button.h
new file mode 100644
index 0000000..586eabb
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button.h
@@ -0,0 +1,100 @@
+// Copyright 2013 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 ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_
+#define ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/alternate_frame_size_button_delegate.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
+#include "ash/wm/workspace/snap_types.h"
+#include "base/timer/timer.h"
+
+namespace views {
+class Widget;
+}
+
+namespace ash {
+class AlternateFrameSizeButtonDelegate;
+
+// The maximize/restore button when using the alternate button style.
+// When the mouse is pressed over the size button or the size button is touched:
+// - The minimize and close buttons are set to snap left and snap right
+//   respectively.
+// - The pressed button is updated during the drag to reflect the button
+//   underneath the mouse cursor. (The size button is potentially unpressed).
+// When the drag terminates, the action for the pressed button is executed.
+// For the sake of simplicity, the size button is the event handler for a click
+// starting on the size button and the entire drag (including when the size
+// button is unpressed).
+class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton {
+ public:
+  AlternateFrameSizeButton(views::ButtonListener* listener,
+                           views::Widget* frame,
+                           AlternateFrameSizeButtonDelegate* delegate);
+
+  virtual ~AlternateFrameSizeButton();
+
+  // views::CustomButton overrides:
+  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseCaptureLost() OVERRIDE;
+  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+  void set_delay_to_set_buttons_to_snap_mode(int delay_ms) {
+    set_buttons_to_snap_mode_delay_ms_ = delay_ms;
+  }
+
+ private:
+  // Starts |set_buttons_to_snap_mode_timer_|.
+  void StartSetButtonsToSnapModeTimer(const ui::LocatedEvent& event);
+
+  // Sets the buttons adjacent to the size button to snap left and right.
+  void SetButtonsToSnapMode();
+
+  // Updates the pressed button based on |event_location|.
+  void UpdatePressedButton(const ui::LocatedEvent& event);
+
+  // Snaps |frame_| according to |snap_type_|. Returns true if |frame_| was
+  // snapped.
+  bool CommitSnap(const ui::LocatedEvent& event);
+
+  // Sets the buttons adjacent to the size button to minimize and close again.
+  // Clears any state set while snapping was enabled. |animate| indicates
+  // whether the buttons should animate back to their original icons.
+  void SetButtonsToNormalMode(
+      AlternateFrameSizeButtonDelegate::Animate animate);
+
+  // Widget that the size button acts on.
+  views::Widget* frame_;
+
+  // Not owned.
+  AlternateFrameSizeButtonDelegate* delegate_;
+
+  // Location of the event which started |set_buttons_to_snap_mode_timer_| in
+  // view coordinates.
+  gfx::Point set_buttons_to_snap_mode_timer_event_location_;
+
+  // The delay between the user pressing the size button and the buttons
+  // adjacent to the size button morphing into buttons for snapping left and
+  // right.
+  int set_buttons_to_snap_mode_delay_ms_;
+
+  base::OneShotTimer<AlternateFrameSizeButton> set_buttons_to_snap_mode_timer_;
+
+  // Whether the buttons adjacent to the size button snap the window left and
+  // right.
+  bool in_snap_mode_;
+
+  // The action of the currently pressed button. If |snap_type_| == SNAP_NONE,
+  // the size button's default action is run when clicked.
+  SnapType snap_type_;
+
+  DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButton);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h b/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h
new file mode 100644
index 0000000..c1472b2
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h
@@ -0,0 +1,57 @@
+// Copyright 2013 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 ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_
+#define ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
+
+namespace gfx {
+class Insets;
+class Point;
+}
+
+namespace ash {
+class FrameCaptionButton;
+
+// Delegate interface for AlternateFrameSizeButton.
+class ASH_EXPORT AlternateFrameSizeButtonDelegate {
+ public:
+  enum Animate {
+    ANIMATE_YES,
+    ANIMATE_NO
+  };
+
+  // Returns whether the minimize button is visible.
+  virtual bool IsMinimizeButtonVisible() const = 0;
+
+  // Reset the caption button views::Button::ButtonState back to normal. If
+  // |animate| is ANIMATE_YES, the buttons will crossfade back to their
+  // original icons.
+  virtual void SetButtonsToNormal(Animate animate) = 0;
+
+  // Sets the minimize and close button icons. The buttons will crossfade to
+  // their new icons if |animate| is ANIMATE_YES.
+  virtual void SetButtonIcons(CaptionButtonIcon left_button_action,
+                              CaptionButtonIcon right_button_action,
+                              Animate animate) = 0;
+
+  // Presses the button at |position_in_screen| and unpresses any other pressed
+  // caption buttons.
+  // |pressed_button_hittest_insets| indicates how much the hittest insets for
+  // the currently pressed button should be expanded if no button was found at
+  // |position_in_screen| using the normal button hittest insets.
+  // Returns the button which was pressed.
+  virtual const FrameCaptionButton* PressButtonAt(
+      const gfx::Point& position_in_screen,
+      const gfx::Insets& pressed_button_hittest_insets) const = 0;
+
+ protected:
+  virtual ~AlternateFrameSizeButtonDelegate() {}
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc b/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc
new file mode 100644
index 0000000..75c0f50
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc
@@ -0,0 +1,367 @@
+// Copyright 2013 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.
+
+#include "ash/wm/caption_buttons/alternate_frame_size_button.h"
+
+#include "ash/ash_switches.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
+#include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/workspace/snap_sizer.h"
+#include "base/command_line.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/window.h"
+#include "ui/events/gestures/gesture_configuration.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace ash {
+namespace test {
+
+namespace {
+
+class TestWidgetDelegate : public views::WidgetDelegateView {
+ public:
+  TestWidgetDelegate() {}
+  virtual ~TestWidgetDelegate() {}
+
+  // Overridden from views::WidgetDelegate:
+  virtual views::View* GetContentsView() OVERRIDE {
+    return this;
+  }
+  virtual bool CanResize() const OVERRIDE {
+    return true;
+  }
+  virtual bool CanMaximize() const OVERRIDE {
+    return true;
+  }
+
+  ash::FrameCaptionButtonContainerView* caption_button_container() {
+    return caption_button_container_;
+  }
+
+ private:
+  // Overridden from views::View:
+  virtual void Layout() OVERRIDE {
+    caption_button_container_->Layout();
+
+    // Right align the caption button container.
+    gfx::Size preferred_size = caption_button_container_->GetPreferredSize();
+    caption_button_container_->SetBounds(width() - preferred_size.width(), 0,
+        preferred_size.width(), preferred_size.height());
+  }
+
+  virtual void ViewHierarchyChanged(
+      const ViewHierarchyChangedDetails& details) OVERRIDE {
+    if (details.is_add && details.child == this) {
+      caption_button_container_ = new FrameCaptionButtonContainerView(
+          GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+      AddChildView(caption_button_container_);
+    }
+  }
+
+  // Not owned.
+  ash::FrameCaptionButtonContainerView* caption_button_container_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
+};
+
+}  // namespace
+
+class AlternateFrameSizeButtonTest : public AshTestBase {
+ public:
+  AlternateFrameSizeButtonTest() {}
+  virtual ~AlternateFrameSizeButtonTest() {}
+
+  // Returns the center point of |view| in screen coordinates.
+  gfx::Point CenterPointInScreen(views::View* view) {
+    return view->GetBoundsInScreen().CenterPoint();
+  }
+
+  // Returns true if the window is snapped to |edge|.
+  bool IsSnapped(internal::SnapSizer::Edge edge) const {
+    ash::wm::WindowShowType show_type = window_state()->window_show_type();
+    if (edge == internal::SnapSizer::LEFT_EDGE)
+      return show_type == ash::wm::SHOW_TYPE_LEFT_SNAPPED;
+    else
+      return show_type == ash::wm::SHOW_TYPE_RIGHT_SNAPPED;
+  }
+
+  // Returns true if all three buttons are in the normal state.
+  bool AllButtonsInNormalState() const {
+    return minimize_button_->state() == views::Button::STATE_NORMAL &&
+        size_button_->state() == views::Button::STATE_NORMAL &&
+        close_button_->state() == views::Button::STATE_NORMAL;
+  }
+
+  // Creates a widget with |delegate|. The returned widget takes ownership of
+  // |delegate|.
+  views::Widget* CreateWidget(views::WidgetDelegate* delegate) {
+    views::Widget* widget = new views::Widget;
+    views::Widget::InitParams params(
+        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.context = CurrentContext();
+    params.delegate = delegate;
+    params.bounds = gfx::Rect(10, 10, 100, 100);
+    widget->Init(params);
+    widget->Show();
+    return widget;
+  }
+
+  // AshTestBase overrides:
+  virtual void SetUp() OVERRIDE {
+    AshTestBase::SetUp();
+
+    CommandLine* command_line = CommandLine::ForCurrentProcess();
+    command_line->AppendSwitch(
+        switches::kAshEnableAlternateFrameCaptionButtonStyle);
+    CHECK(!command_line->HasSwitch(switches::kAshMultipleSnapWindowWidths));
+
+    TestWidgetDelegate* delegate = new TestWidgetDelegate();
+    window_state_ = ash::wm::GetWindowState(
+        CreateWidget(delegate)->GetNativeWindow());
+
+    FrameCaptionButtonContainerView::TestApi test(
+        delegate->caption_button_container());
+
+    minimize_button_ = test.minimize_button();
+    size_button_ = test.size_button();
+    static_cast<AlternateFrameSizeButton*>(
+        size_button_)->set_delay_to_set_buttons_to_snap_mode(0);
+    close_button_ = test.close_button();
+  }
+
+  ash::wm::WindowState* window_state() { return window_state_; }
+  const ash::wm::WindowState* window_state() const { return window_state_; }
+
+  FrameCaptionButton* minimize_button() { return minimize_button_; }
+  FrameCaptionButton* size_button() { return size_button_; }
+  FrameCaptionButton* close_button() { return close_button_; }
+
+ private:
+  // Not owned.
+  ash::wm::WindowState* window_state_;
+  FrameCaptionButton* minimize_button_;
+  FrameCaptionButton* size_button_;
+  FrameCaptionButton* close_button_;
+
+  DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButtonTest);
+};
+
+// Tests that pressing the left mouse button or tapping down on the size button
+// puts the button into the pressed state.
+TEST_F(AlternateFrameSizeButtonTest, PressedState) {
+  aura::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressLeftButton();
+  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+  generator.ReleaseLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
+
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressTouchId(3);
+  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+  generator.ReleaseTouchId(3);
+  RunAllPendingInMessageLoop();
+  EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
+}
+
+// Tests that clicking on the size button toggles between the maximized and
+// normal state.
+TEST_F(AlternateFrameSizeButtonTest, ClickSizeButtonTogglesMaximize) {
+  EXPECT_FALSE(window_state()->IsMaximized());
+
+  aura::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.ClickLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(window_state()->IsMaximized());
+
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.ClickLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_FALSE(window_state()->IsMaximized());
+
+  generator.GestureTapAt(CenterPointInScreen(size_button()));
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(window_state()->IsMaximized());
+
+  generator.GestureTapAt(CenterPointInScreen(size_button()));
+  RunAllPendingInMessageLoop();
+  EXPECT_FALSE(window_state()->IsMaximized());
+}
+
+// Test that clicking + dragging to a button adjacent to the size button snaps
+// the window left or right.
+TEST_F(AlternateFrameSizeButtonTest, ButtonDrag) {
+  EXPECT_TRUE(window_state()->IsNormalShowState());
+  EXPECT_FALSE(window_state()->IsSnapped());
+
+  // 1) Test by dragging the mouse.
+  // Snap right.
+  aura::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressLeftButton();
+  generator.MoveMouseTo(CenterPointInScreen(close_button()));
+  generator.ReleaseLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
+
+  // Snap left.
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressLeftButton();
+  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
+  generator.ReleaseLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+  // 2) Test with scroll gestures.
+  // Snap right.
+  generator.GestureScrollSequence(
+      CenterPointInScreen(size_button()),
+      CenterPointInScreen(close_button()),
+      base::TimeDelta::FromMilliseconds(100),
+      3);
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
+
+  // Snap left.
+  generator.GestureScrollSequence(
+      CenterPointInScreen(size_button()),
+      CenterPointInScreen(minimize_button()),
+      base::TimeDelta::FromMilliseconds(100),
+      3);
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+  // 3) Test with tap gestures.
+  const int touch_default_radius =
+      ui::GestureConfiguration::default_radius();
+  ui::GestureConfiguration::set_default_radius(0);
+  // Snap right.
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
+  // Snap left.
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+  ui::GestureConfiguration::set_default_radius(touch_default_radius);
+}
+
+// Test that clicking, dragging, and overshooting the minimize button a bit
+// horizontally still snaps the window left.
+TEST_F(AlternateFrameSizeButtonTest, SnapLeftOvershootMinimize) {
+  EXPECT_TRUE(window_state()->IsNormalShowState());
+  EXPECT_FALSE(window_state()->IsSnapped());
+
+  aura::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+
+  generator.PressLeftButton();
+  // Move to the minimize button.
+  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
+  // Overshoot the minimize button.
+  generator.MoveMouseBy(-minimize_button()->width(), 0);
+  generator.ReleaseLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+}
+
+// Test that right clicking the size button has no effect.
+TEST_F(AlternateFrameSizeButtonTest, RightMouseButton) {
+  EXPECT_TRUE(window_state()->IsNormalShowState());
+  EXPECT_FALSE(window_state()->IsSnapped());
+
+  aura::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressRightButton();
+  generator.ReleaseRightButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(window_state()->IsNormalShowState());
+  EXPECT_FALSE(window_state()->IsSnapped());
+}
+
+// Test that upon releasing the mouse button after having pressed the size
+// button
+// - The state of all the caption buttons is reset.
+// - The icon displayed by all of the caption buttons is reset.
+TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) {
+  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
+  EXPECT_TRUE(AllButtonsInNormalState());
+
+  // Pressing the size button should result in the size button being pressed and
+  // the minimize and close button icons changing.
+  aura::test::EventGenerator& generator = GetEventGenerator();
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressLeftButton();
+  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
+  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+  // Dragging the mouse over the minimize button should press the minimize
+  // button and the minimize and close button icons should stay changed.
+  generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
+  EXPECT_EQ(views::Button::STATE_PRESSED, minimize_button()->state());
+  EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
+  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+  // Release the mouse, snapping the window left.
+  generator.ReleaseLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+  // None of the buttons should stay pressed and the buttons should have their
+  // regular icons.
+  EXPECT_TRUE(AllButtonsInNormalState());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
+
+  // Repeat test but release button where it does not affect the window's state
+  // because the code path is different.
+  generator.MoveMouseTo(CenterPointInScreen(size_button()));
+  generator.PressLeftButton();
+  EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
+  EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+  EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+  const gfx::Rect& kWorkAreaBoundsInScreen =
+      ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
+  generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
+
+  // None of the buttons should be pressed because we are really far away from
+  // any of the caption buttons. The minimize and close button icons should
+  // be changed because the mouse is pressed.
+  EXPECT_TRUE(AllButtonsInNormalState());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+  // Release the mouse. The window should stay snapped left.
+  generator.ReleaseLeftButton();
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+  // The buttons should stay unpressed and the buttons should now have their
+  // regular icons.
+  EXPECT_TRUE(AllButtonsInNormalState());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
+  EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
+}
+
+}  // namespace test
+}  // namespace ash
diff --git a/ash/wm/caption_buttons/caption_button_types.h b/ash/wm/caption_buttons/caption_button_types.h
new file mode 100644
index 0000000..b34a861
--- /dev/null
+++ b/ash/wm/caption_buttons/caption_button_types.h
@@ -0,0 +1,30 @@
+// Copyright 2013 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 ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_
+#define ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_
+
+namespace ash {
+
+// These are the types of maximization we know.
+enum MaximizeBubbleFrameState {
+  FRAME_STATE_NONE = 0,
+  FRAME_STATE_FULL = 1,  // This is the full maximized state.
+  FRAME_STATE_SNAP_LEFT = 2,
+  FRAME_STATE_SNAP_RIGHT = 3
+};
+
+// These are the icon types that a caption button can have. The size button's
+// action (SnapType) can be different from its icon.
+enum CaptionButtonIcon {
+  CAPTION_BUTTON_ICON_MINIMIZE,
+  CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
+  CAPTION_BUTTON_ICON_CLOSE,
+  CAPTION_BUTTON_ICON_LEFT_SNAPPED,
+  CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
+};
+
+} // namespace ash
+
+#endif  // ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_
diff --git a/ash/wm/caption_buttons/frame_caption_button.cc b/ash/wm/caption_buttons/frame_caption_button.cc
new file mode 100644
index 0000000..5a6a835
--- /dev/null
+++ b/ash/wm/caption_buttons/frame_caption_button.cc
@@ -0,0 +1,177 @@
+// Copyright 2013 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.
+
+#include "ash/wm/caption_buttons/frame_caption_button.h"
+
+#include "grit/ash_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/animation/slide_animation.h"
+#include "ui/gfx/canvas.h"
+
+namespace ash {
+
+namespace {
+
+// The duration of the crossfade animation when swapping the button's icon.
+const int kCrossfadeDurationMs = 200;
+
+}  // namespace
+
+// static
+const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton";
+
+FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener,
+                                       CaptionButtonIcon icon)
+    : ImageButton(listener),
+      icon_(icon),
+      style_(STYLE_SHORT_RESTORED),
+      last_paint_scale_(1.0f),
+      animation_(new gfx::SlideAnimation(this)) {
+  animation_->Reset(1);
+  UpdateImages();
+}
+
+FrameCaptionButton::~FrameCaptionButton() {
+}
+
+void FrameCaptionButton::SetIcon(CaptionButtonIcon icon, Animate animate) {
+  if (icon_ == icon)
+    return;
+
+  if (animate == ANIMATE_YES) {
+    gfx::Canvas canvas(size(), last_paint_scale_, false);
+    OnPaint(&canvas);
+    crossfade_image_ = gfx::ImageSkia(canvas.ExtractImageRep());
+
+    icon_ = icon;
+    UpdateImages();
+
+    animation_->Reset(0);
+    animation_->SetSlideDuration(kCrossfadeDurationMs);
+    animation_->Show();
+  } else {
+    animation_->Reset(1);
+    icon_ = icon;
+    UpdateImages();
+  }
+}
+
+void FrameCaptionButton::SetStyle(Style style) {
+  if (style_ == style)
+    return;
+  animation_->Reset(1);
+  style_ = style;
+  UpdateImages();
+}
+
+const char* FrameCaptionButton::GetClassName() const {
+  return kViewClassName;
+}
+
+void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
+  last_paint_scale_ = canvas->image_scale();
+  int alpha = static_cast<int>(animation_->GetCurrentValue() * 255);
+  int crossfade_alpha = 255 - alpha;
+  if (crossfade_alpha > 0 && !crossfade_image_.isNull()) {
+    gfx::Canvas composed_canvas(size(), last_paint_scale_, false);
+    SkPaint paint;
+    paint.setAlpha(crossfade_alpha);
+    paint.setXfermodeMode(SkXfermode::kPlus_Mode);
+    composed_canvas.DrawImageInt(crossfade_image_, 0, 0, paint);
+    paint.setAlpha(alpha);
+    ImageButton::OnPaint(&composed_canvas);
+
+    canvas->DrawImageInt(
+        gfx::ImageSkia(composed_canvas.ExtractImageRep()), 0, 0);
+  } else {
+    ImageButton::OnPaint(canvas);
+  }
+}
+
+void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) {
+  // ImageButton does not become pressed when the user drags off and then back
+  // onto the button. Make FrameCaptionButton pressed in this case because this
+  // behavior is more consistent with AlternateFrameSizeButton.
+  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
+      event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+    if (HitTestPoint(event->location())) {
+      SetState(STATE_PRESSED);
+      RequestFocus();
+      event->StopPropagation();
+    } else {
+      SetState(STATE_NORMAL);
+    }
+  } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
+    if (HitTestPoint(event->location())) {
+      SetState(STATE_HOVERED);
+      NotifyClick(*event);
+      event->StopPropagation();
+    }
+  }
+  ImageButton::OnGestureEvent(event);
+}
+
+void FrameCaptionButton::StateChanged() {
+  if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
+    animation_->Reset(1);
+}
+
+void FrameCaptionButton::UpdateImages() {
+  switch (icon_) {
+    case CAPTION_BUTTON_ICON_MINIMIZE:
+      SetImages(IDR_AURA_WINDOW_MINIMIZE_SHORT,
+                IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
+                IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
+      break;
+   case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
+   case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
+   case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
+     if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
+       SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
+                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
+                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
+     } else if (style_ == STYLE_SHORT_RESTORED) {
+       SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
+                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
+                 IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
+     } else {
+       SetImages(IDR_AURA_WINDOW_MAXIMIZE,
+                 IDR_AURA_WINDOW_MAXIMIZE_H,
+                 IDR_AURA_WINDOW_MAXIMIZE_P);
+     }
+     break;
+   case CAPTION_BUTTON_ICON_CLOSE:
+     if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
+       SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
+                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
+                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
+     } else if (style_ == STYLE_SHORT_RESTORED) {
+       SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
+                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
+                 IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
+     } else {
+       SetImages(IDR_AURA_WINDOW_CLOSE,
+                 IDR_AURA_WINDOW_CLOSE_H,
+                 IDR_AURA_WINDOW_CLOSE_P);
+     }
+     break;
+  }
+
+  SchedulePaint();
+}
+
+void FrameCaptionButton::SetImages(int normal_image_id,
+                                   int hot_image_id,
+                                   int pushed_image_id) {
+  ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
+  SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_image_id));
+  SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hot_image_id));
+  SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pushed_image_id));
+}
+
+void FrameCaptionButton::AnimationProgressed(const gfx::Animation* animation) {
+  SchedulePaint();
+}
+
+}  // namespace ash
diff --git a/ash/wm/caption_buttons/frame_caption_button.h b/ash/wm/caption_buttons/frame_caption_button.h
new file mode 100644
index 0000000..61ed922
--- /dev/null
+++ b/ash/wm/caption_buttons/frame_caption_button.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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 ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_
+#define ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/views/controls/button/image_button.h"
+
+namespace gfx {
+class SlideAnimation;
+}
+
+namespace ash {
+
+// Base class for the window caption buttons (minimize, maximize, restore,
+// close).
+class ASH_EXPORT FrameCaptionButton : public views::ImageButton {
+ public:
+  enum Animate {
+    ANIMATE_YES,
+    ANIMATE_NO
+  };
+
+  enum Style {
+    // Restored tabbed browser windows.
+    STYLE_TALL_RESTORED,
+
+    // All other restored windows.
+    STYLE_SHORT_RESTORED,
+
+    // Maximized or fullscreen windows.
+    STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN
+  };
+
+  static const char kViewClassName[];
+
+  FrameCaptionButton(views::ButtonListener* listener, CaptionButtonIcon icon);
+  virtual ~FrameCaptionButton();
+
+  // Sets the button's icon. If |animate| is ANIMATE_YES, the button crossfades
+  // to the new icon.
+  void SetIcon(CaptionButtonIcon icon, Animate animate);
+
+  // Sets the button's style. The transition to the new style is not animated.
+  void SetStyle(Style style);
+
+  // views::View overrides:
+  virtual const char* GetClassName() const OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+  CaptionButtonIcon icon() const {
+    return icon_;
+  }
+
+ protected:
+  // views::CustomButton overrides:
+  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+  virtual void StateChanged() OVERRIDE;
+
+ private:
+  // Updates the button's images based on the current icon and style.
+  void UpdateImages();
+
+  // Sets the button's images based on the given ids.
+  void SetImages(int normal_image_id, int hot_image_id, int pushed_image_id);
+
+  // gfx::AnimationDelegate override:
+  virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
+
+  // The button's current icon.
+  CaptionButtonIcon icon_;
+
+  // The button's current style.
+  Style style_;
+
+  // The scale at which the button was previously painted.
+  float last_paint_scale_;
+
+  // The image to crossfade from.
+  gfx::ImageSkia crossfade_image_;
+
+  scoped_ptr<gfx::SlideAnimation> animation_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameCaptionButton);
+};
+
+}  // namespace ash
+
+#endif  // ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_
diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view.cc b/ash/wm/caption_buttons/frame_caption_button_container_view.cc
index 40708b8..2e4d323 100644
--- a/ash/wm/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/wm/caption_buttons/frame_caption_button_container_view.cc
@@ -7,8 +7,9 @@
 #include "ash/ash_switches.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/wm/caption_buttons/alternate_frame_size_button.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
 #include "ash/wm/caption_buttons/frame_maximize_button.h"
-#include "ash/wm/window_state.h"
 #include "grit/ash_resources.h"
 #include "grit/ui_strings.h"  // Accessibility names
 #include "ui/base/hit_test.h"
@@ -16,7 +17,8 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/canvas.h"
-#include "ui/views/border.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/point.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
@@ -54,7 +56,7 @@
   bool alternate_style = switches::UseAlternateFrameCaptionButtonStyle();
 
   // Insert the buttons left to right.
-  minimize_button_ = new views::ImageButton(this);
+  minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
   minimize_button_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
   // Hide |minimize_button_| when using the non-alternate button style because
@@ -65,7 +67,7 @@
   AddChildView(minimize_button_);
 
   if (alternate_style)
-    size_button_ = new views::ImageButton(this);
+    size_button_ = new AlternateFrameSizeButton(this, frame, this);
   else
     size_button_ = new FrameMaximizeButton(this, frame);
   size_button_->SetAccessibleName(
@@ -73,7 +75,7 @@
   size_button_->SetVisible(frame_->widget_delegate()->CanMaximize());
   AddChildView(size_button_);
 
-  close_button_ = new views::ImageButton(this);
+  close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
   close_button_->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
   AddChildView(close_button_);
@@ -92,8 +94,7 @@
 }
 
 void FrameCaptionButtonContainerView::ResetWindowControls() {
-  minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
-  size_button_->SetState(views::CustomButton::STATE_NORMAL);
+  SetButtonsToNormal(ANIMATE_NO);
 }
 
 int FrameCaptionButtonContainerView::NonClientHitTest(
@@ -128,45 +129,19 @@
 }
 
 void FrameCaptionButtonContainerView::Layout() {
-  SetButtonImages(minimize_button_,
-                  IDR_AURA_WINDOW_MINIMIZE_SHORT,
-                  IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
-                  IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
+  FrameCaptionButton::Style style = FrameCaptionButton::STYLE_SHORT_RESTORED;
   if (header_style_ == HEADER_STYLE_SHORT) {
-    // The new assets only make sense if the window is maximized or fullscreen
-    // because we usually use a black header in this case.
-    if ((frame_->IsMaximized() || frame_->IsFullscreen()) &&
-        wm::GetWindowState(
-            frame_->GetNativeWindow())->tracked_by_workspace()) {
-      SetButtonImages(size_button_,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
-      SetButtonImages(close_button_,
-                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
-                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
-                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
-    } else {
-      SetButtonImages(size_button_,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
-                      IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
-      SetButtonImages(close_button_,
-                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
-                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
-                      IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
-    }
+    if (frame_->IsMaximized() || frame_->IsFullscreen())
+      style = FrameCaptionButton::STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN;
+    // Else: FrameCaptionButton::STYLE_SHORT_RESTORED;
   } else {
-    SetButtonImages(size_button_,
-                    IDR_AURA_WINDOW_MAXIMIZE,
-                    IDR_AURA_WINDOW_MAXIMIZE_H,
-                    IDR_AURA_WINDOW_MAXIMIZE_P);
-    SetButtonImages(close_button_,
-                    IDR_AURA_WINDOW_CLOSE,
-                    IDR_AURA_WINDOW_CLOSE_H,
-                    IDR_AURA_WINDOW_CLOSE_P);
+    style = FrameCaptionButton::STYLE_TALL_RESTORED;
   }
 
+  minimize_button_->SetStyle(style);
+  size_button_->SetStyle(style);
+  close_button_->SetStyle(style);
+
   int x = 0;
   for (int i = 0; i < child_count(); ++i) {
     views::View* child = child_at(i);
@@ -210,15 +185,14 @@
         ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
   }
 
+  // Abort any animations of the button icons.
+  SetButtonsToNormal(ANIMATE_NO);
+
   ash::UserMetricsAction action =
       ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
   if (sender == minimize_button_) {
-    // The minimize button may move out from under the cursor.
-    ResetWindowControls();
     frame_->Minimize();
   } else if (sender == size_button_) {
-    // The size button may move out from under the cursor.
-    ResetWindowControls();
     if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen.
       frame_->SetFullscreen(false);
       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
@@ -238,18 +212,65 @@
   ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(action);
 }
 
-void FrameCaptionButtonContainerView::SetButtonImages(
-    views::ImageButton* button,
-    int normal_image_id,
-    int hot_image_id,
-    int pushed_image_id) {
-  ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
-  button->SetImage(views::CustomButton::STATE_NORMAL,
-                   resource_bundle.GetImageSkiaNamed(normal_image_id));
-  button->SetImage(views::CustomButton::STATE_HOVERED,
-                   resource_bundle.GetImageSkiaNamed(hot_image_id));
-  button->SetImage(views::CustomButton::STATE_PRESSED,
-                   resource_bundle.GetImageSkiaNamed(pushed_image_id));
+bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
+  return minimize_button_->visible();
+}
+
+void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
+  SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
+      animate);
+  minimize_button_->SetState(views::Button::STATE_NORMAL);
+  size_button_->SetState(views::Button::STATE_NORMAL);
+  close_button_->SetState(views::Button::STATE_NORMAL);
+}
+
+void FrameCaptionButtonContainerView::SetButtonIcons(
+    CaptionButtonIcon minimize_button_icon,
+    CaptionButtonIcon close_button_icon,
+    Animate animate) {
+  FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
+      FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
+  minimize_button_->SetIcon(minimize_button_icon, fcb_animate);
+  close_button_->SetIcon(close_button_icon, fcb_animate);
+}
+
+const FrameCaptionButton*
+FrameCaptionButtonContainerView::PressButtonAt(
+    const gfx::Point& position_in_screen,
+    const gfx::Insets& pressed_hittest_outer_insets) const {
+  DCHECK(switches::UseAlternateFrameCaptionButtonStyle());
+  gfx::Point position(position_in_screen);
+  views::View::ConvertPointFromScreen(this, &position);
+
+  FrameCaptionButton* buttons[] = {
+    close_button_, size_button_, minimize_button_
+  };
+  FrameCaptionButton* pressed_button = NULL;
+  for (size_t i = 0; i < arraysize(buttons); ++i) {
+    FrameCaptionButton* button = buttons[i];
+    if (!button->visible())
+      continue;
+
+    if (button->state() == views::Button::STATE_PRESSED) {
+      gfx::Rect expanded_bounds = button->bounds();
+      expanded_bounds.Inset(pressed_hittest_outer_insets);
+      if (expanded_bounds.Contains(position)) {
+        pressed_button = button;
+        // Do not break in order to give preference to buttons which are
+        // closer to |position_in_screen| than the currently pressed button.
+        // TODO(pkotwicz): Make the caption buttons not overlap.
+      }
+    } else if (ConvertPointToViewAndHitTest(this, button, position)) {
+      pressed_button = button;
+      break;
+    }
+  }
+
+  for (size_t i = 0; i < arraysize(buttons); ++i) {
+    buttons[i]->SetState(buttons[i] == pressed_button ?
+        views::Button::STATE_PRESSED : views::Button::STATE_NORMAL);
+  }
+  return pressed_button;
 }
 
 }  // namespace ash
diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view.h b/ash/wm/caption_buttons/frame_caption_button_container_view.h
index f8e1a92..4fd663b 100644
--- a/ash/wm/caption_buttons/frame_caption_button_container_view.h
+++ b/ash/wm/caption_buttons/frame_caption_button_container_view.h
@@ -6,23 +6,25 @@
 #define ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_
 
 #include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/alternate_frame_size_button_delegate.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
 namespace views {
-class ImageButton;
 class Widget;
 }
 
 namespace ash {
+class FrameCaptionButton;
 class FrameMaximizeButton;
 
 // Container view for the frame caption buttons. It performs the appropriate
 // action when a caption button is clicked.
 class ASH_EXPORT FrameCaptionButtonContainerView
     : public views::View,
-      public views::ButtonListener {
+      public views::ButtonListener,
+      public AlternateFrameSizeButtonDelegate {
  public:
   static const char kViewClassName[];
 
@@ -54,15 +56,15 @@
         : container_view_(container_view) {
     }
 
-    views::ImageButton* minimize_button() const {
+    FrameCaptionButton* minimize_button() const {
       return container_view_->minimize_button_;
     }
 
-    views::ImageButton* size_button() const {
+    FrameCaptionButton* size_button() const {
       return container_view_->size_button_;
     }
 
-    views::ImageButton* close_button() const {
+    FrameCaptionButton* close_button() const {
       return container_view_->close_button_;
     }
 
@@ -102,11 +104,15 @@
   virtual void ButtonPressed(views::Button* sender,
                              const ui::Event& event) OVERRIDE;
 
-  // Sets the images for a button based on the given ids.
-  void SetButtonImages(views::ImageButton* button,
-                       int normal_image_id,
-                       int hot_image_id,
-                       int pushed_image_id);
+  // AlternateFrameSizeButton::Delegate overrides:
+  virtual bool IsMinimizeButtonVisible() const OVERRIDE;
+  virtual void SetButtonsToNormal(Animate animate) OVERRIDE;
+  virtual void SetButtonIcons(CaptionButtonIcon minimize_button_icon,
+                              CaptionButtonIcon close_button_icon,
+                              Animate animate) OVERRIDE;
+  virtual const FrameCaptionButton* PressButtonAt(
+      const gfx::Point& position_in_screen,
+      const gfx::Insets& pressed_hittest_outer_insets) const OVERRIDE;
 
   // The widget that the buttons act on.
   views::Widget* frame_;
@@ -118,9 +124,9 @@
 
   // The buttons. In the normal button style, at most one of |minimize_button_|
   // and |size_button_| is visible.
-  views::ImageButton* minimize_button_;
-  views::ImageButton* size_button_;
-  views::ImageButton* close_button_;
+  FrameCaptionButton* minimize_button_;
+  FrameCaptionButton* size_button_;
+  FrameCaptionButton* close_button_;
 
   DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView);
 };
diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc
index 6737b92..5891231 100644
--- a/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc
+++ b/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc
@@ -6,13 +6,12 @@
 
 #include "ash/ash_switches.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
 #include "base/command_line.h"
 #include "grit/ash_resources.h"
 #include "ui/aura/root_window.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/views/border.h"
-#include "ui/views/controls/button/custom_button.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -68,8 +67,8 @@
 
   // Tests that |leftmost| and |rightmost| are at |container|'s edges.
   bool CheckButtonsAtEdges(FrameCaptionButtonContainerView* container,
-                           const views::CustomButton& leftmost,
-                           const views::CustomButton& rightmost) {
+                           const ash::FrameCaptionButton& leftmost,
+                           const ash::FrameCaptionButton& rightmost) {
     gfx::Rect expected(container->GetPreferredSize());
 
     gfx::Rect container_size(container->GetPreferredSize());
@@ -89,7 +88,7 @@
   }
 
   // Returns true if the images for |button|'s states match the passed in ids.
-  bool ImagesMatch(views::ImageButton* button,
+  bool ImagesMatch(ash::FrameCaptionButton* button,
                    int normal_image_id,
                    int hovered_image_id,
                    int pressed_image_id) {
@@ -244,7 +243,6 @@
     FrameCaptionButtonContainerViewTest::SetUp();
     CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kAshEnableAlternateFrameCaptionButtonStyle);
-    ASSERT_TRUE(switches::UseAlternateFrameCaptionButtonStyle());
   }
 
  private:
@@ -254,6 +252,11 @@
 // Test how the alternate button style affects which buttons are visible in the
 // default case.
 TEST_F(FrameCaptionButtonContainerViewTestAlternateStyle, ButtonVisibility) {
+  // Using the alternate caption button style is dependant on all snapped
+  // windows being 50% of the screen's width.
+  if (!switches::UseAlternateFrameCaptionButtonStyle())
+    return;
+
   // Both the minimize button and the maximize button should be visible when
   // both minimizing and maximizing are allowed when using the alternate
   // button style.
diff --git a/ash/wm/caption_buttons/frame_maximize_button.cc b/ash/wm/caption_buttons/frame_maximize_button.cc
index 14526a0..5a610e5 100644
--- a/ash/wm/caption_buttons/frame_maximize_button.cc
+++ b/ash/wm/caption_buttons/frame_maximize_button.cc
@@ -4,7 +4,6 @@
 
 #include "ash/wm/caption_buttons/frame_maximize_button.h"
 
-#include "ash/launcher/launcher.h"
 #include "ash/screen_ash.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
@@ -82,7 +81,7 @@
 
 FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener,
                                          views::Widget* frame)
-    : ImageButton(listener),
+    : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
       frame_(frame),
       observing_frame_(false),
       is_snap_enabled_(false),
@@ -527,16 +526,10 @@
 
 void FrameMaximizeButton::Snap(SnapSizer* snap_sizer) {
   Shell* shell = Shell::GetInstance();
-  wm::WindowState* window_state = wm::GetWindowState(frame_->GetNativeWindow());
   switch (snap_type_) {
     case SNAP_LEFT:
     case SNAP_RIGHT: {
-      // Others might also have set up a restore rectangle already. If so, we
-      // should not overwrite the restore rectangle.
-      gfx::Rect current_bounds_in_screen = frame_->GetWindowBoundsInScreen();
       snap_sizer->SnapWindowToTargetBounds();
-      if (!window_state->HasRestoreBounds())
-        window_state->SetRestoreBoundsInScreen(current_bounds_in_screen);
       shell->delegate()->RecordUserMetricsAction(
           snap_type_ == SNAP_LEFT ?
               UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
diff --git a/ash/wm/caption_buttons/frame_maximize_button.h b/ash/wm/caption_buttons/frame_maximize_button.h
index c60f7a5..a1bb991 100644
--- a/ash/wm/caption_buttons/frame_maximize_button.h
+++ b/ash/wm/caption_buttons/frame_maximize_button.h
@@ -6,13 +6,13 @@
 #define ASH_WM_CAPTION_BUTTONS_FRAME_MAXIMIZE_BUTTON_H_
 
 #include "ash/ash_export.h"
-#include "ash/wm/caption_buttons/maximize_bubble_frame_state.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
 #include "ash/wm/workspace/snap_types.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
 #include "base/timer/timer.h"
 #include "ui/aura/window_observer.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/widget/widget_observer.h"
 
 namespace views {
@@ -30,7 +30,7 @@
 class MaximizeBubbleController;
 
 // Button used for the maximize control on the frame. Handles snapping logic.
-class ASH_EXPORT FrameMaximizeButton : public views::ImageButton,
+class ASH_EXPORT FrameMaximizeButton : public FrameCaptionButton,
                                        public views::WidgetObserver,
                                        public aura::WindowObserver {
  public:
diff --git a/ash/wm/caption_buttons/maximize_bubble_controller.h b/ash/wm/caption_buttons/maximize_bubble_controller.h
index 9a842e5..53a07dd 100644
--- a/ash/wm/caption_buttons/maximize_bubble_controller.h
+++ b/ash/wm/caption_buttons/maximize_bubble_controller.h
@@ -6,7 +6,7 @@
 #define ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_CONTROLLER_H_
 
 #include "ash/ash_export.h"
-#include "ash/wm/caption_buttons/maximize_bubble_frame_state.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
 #include "ash/wm/workspace/snap_types.h"
 #include "base/memory/scoped_ptr.h"
 
diff --git a/ash/wm/caption_buttons/maximize_bubble_frame_state.h b/ash/wm/caption_buttons/maximize_bubble_frame_state.h
deleted file mode 100644
index 1737818..0000000
--- a/ash/wm/caption_buttons/maximize_bubble_frame_state.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2013 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 ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_
-#define ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_
-
-namespace ash {
-
-// These are the types of maximization we know.
-enum MaximizeBubbleFrameState {
-  FRAME_STATE_NONE = 0,
-  FRAME_STATE_FULL = 1,  // This is the full maximized state.
-  FRAME_STATE_SNAP_LEFT = 2,
-  FRAME_STATE_SNAP_RIGHT = 3
-};
-
-} // namespace views
-
-#endif  // ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_
diff --git a/ash/wm/custom_frame_view_ash.cc b/ash/wm/custom_frame_view_ash.cc
index ef2bc10..268a4d2 100644
--- a/ash/wm/custom_frame_view_ash.cc
+++ b/ash/wm/custom_frame_view_ash.cc
@@ -4,13 +4,20 @@
 
 #include "ash/wm/custom_frame_view_ash.h"
 
+#include "ash/ash_switches.h"
 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/wm/caption_buttons/frame_maximize_button.h"
 #include "ash/wm/caption_buttons/frame_maximize_button_observer.h"
 #include "ash/wm/frame_border_hit_test_controller.h"
 #include "ash/wm/header_painter.h"
 #include "ash/wm/immersive_fullscreen_controller.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_state_delegate.h"
+#include "base/command_line.h"
 #include "grit/ash_resources.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/rect.h"
@@ -30,6 +37,87 @@
   return *title_font;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameViewAshWindowStateDelegate
+
+// Handles a user's fullscreen request (Shift+F4/F4). Puts the window into
+// immersive fullscreen if the kAshEnableImmersiveFullscreenForAllWindows
+// flag is set.
+class CustomFrameViewAshWindowStateDelegate
+    : public ash::wm::WindowStateDelegate,
+      public ash::wm::WindowStateObserver,
+      public aura::WindowObserver {
+ public:
+  CustomFrameViewAshWindowStateDelegate(
+      ash::wm::WindowState* window_state,
+      ash::CustomFrameViewAsh* custom_frame_view)
+      : window_state_(NULL) {
+#if defined(OS_CHROMEOS)
+    // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for
+    // Windows Ash.
+    if (CommandLine::ForCurrentProcess()->HasSwitch(
+            ash::switches::kAshEnableImmersiveFullscreenForAllWindows)) {
+      immersive_fullscreen_controller_.reset(
+          new ash::ImmersiveFullscreenController);
+      custom_frame_view->InitImmersiveFullscreenControllerForView(
+          immersive_fullscreen_controller_.get());
+
+      // Add a window state observer to exit fullscreen properly in case
+      // fullscreen is exited without going through
+      // WindowState::ToggleFullscreen(). This is the case when exiting
+      // immersive fullscreen via the "Restore" window control.
+      // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048
+      window_state_ = window_state;
+      window_state_->AddObserver(this);
+      window_state_->window()->AddObserver(this);
+    }
+#endif
+  }
+  virtual ~CustomFrameViewAshWindowStateDelegate() {
+    if (window_state_) {
+      window_state_->RemoveObserver(this);
+      window_state_->window()->RemoveObserver(this);
+    }
+  }
+ private:
+  // Overridden from ash::wm::WindowStateDelegate:
+  virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE {
+    bool enter_fullscreen = !window_state->IsFullscreen();
+    if (enter_fullscreen) {
+      window_state->window()->SetProperty(aura::client::kShowStateKey,
+                                           ui::SHOW_STATE_FULLSCREEN);
+    } else {
+      window_state->Restore();
+    }
+    if (immersive_fullscreen_controller_)
+      immersive_fullscreen_controller_->SetEnabled(enter_fullscreen);
+    return true;
+  }
+  // Overridden from aura::WindowObserver:
+  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
+    window_state_->RemoveObserver(this);
+    window_state_->window()->RemoveObserver(this);
+    window_state_ = NULL;
+  }
+  // Overridden from ash::wm::WindowStateObserver:
+  virtual void OnWindowShowTypeChanged(
+      ash::wm::WindowState* window_state,
+      ash::wm::WindowShowType old_type) OVERRIDE {
+    if (!window_state->IsFullscreen() &&
+        !window_state->IsMinimized() &&
+        immersive_fullscreen_controller_.get() &&
+        immersive_fullscreen_controller_->IsEnabled()) {
+      immersive_fullscreen_controller_->SetEnabled(false);
+    }
+  }
+
+  ash::wm::WindowState* window_state_;
+  scoped_ptr<ash::ImmersiveFullscreenController>
+      immersive_fullscreen_controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshWindowStateDelegate);
+};
+
 }  // namespace
 
 namespace ash {
@@ -177,7 +265,7 @@
 
 void CustomFrameViewAsh::HeaderView::OnPaint(gfx::Canvas* canvas) {
   int theme_image_id = 0;
-  if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO))
+  if (frame_->IsMaximized() || frame_->IsFullscreen())
     theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
   else if (paint_as_active_)
     theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
@@ -302,6 +390,15 @@
   // |header_view_| is set as the non client view's overlay view so that it can
   // overlay the web contents in immersive fullscreen.
   frame->non_client_view()->SetOverlayView(new OverlayView(header_view_));
+
+  // A delegate for a more complex way of fullscreening the window may already
+  // be set. This is the case for packaged apps.
+  wm::WindowState* window_state = wm::GetWindowState(frame->GetNativeWindow());
+  if (!window_state->HasDelegate()) {
+    window_state->SetDelegate(scoped_ptr<wm::WindowStateDelegate>(
+        new CustomFrameViewAshWindowStateDelegate(
+            window_state, this)).Pass());
+  }
 }
 
 CustomFrameViewAsh::~CustomFrameViewAsh() {
diff --git a/ash/wm/dock/docked_window_layout_manager.cc b/ash/wm/dock/docked_window_layout_manager.cc
index f4a2e26..3298f05 100644
--- a/ash/wm/dock/docked_window_layout_manager.cc
+++ b/ash/wm/dock/docked_window_layout_manager.cc
@@ -21,15 +21,21 @@
 #include "base/auto_reset.h"
 #include "base/command_line.h"
 #include "base/metrics/histogram.h"
+#include "grit/ash_resources.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPaint.h"
 #include "ui/aura/client/activation_client.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/window_tree_client.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
+#include "ui/base/resource/resource_bundle.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/rect.h"
+#include "ui/views/background.h"
 
 namespace ash {
 namespace internal {
@@ -45,24 +51,90 @@
 const int DockedWindowLayoutManager::kIdealWidth = 250;
 const int kMinimumHeight = 250;
 const int kSlideDurationMs = 120;
-const int kFadeDurationMs = 720;
+const int kFadeDurationMs = 60;
+const int kMinimizeDurationMs = 720;
 
-namespace {
-
-const SkColor kDockBackgroundColor = SkColorSetARGB(0xff, 0x10, 0x10, 0x10);
-const float kDockBackgroundOpacity = 0.5f;
-
-class DockedBackgroundWidget : public views::Widget {
+class DockedBackgroundWidget : public views::Widget,
+                               public internal::BackgroundAnimatorDelegate {
  public:
-  explicit DockedBackgroundWidget(aura::Window* container) {
+  explicit DockedBackgroundWidget(aura::Window* container)
+      : alignment_(DOCKED_ALIGNMENT_NONE),
+        background_animator_(this, 0, kLauncherBackgroundAlpha),
+        alpha_(0),
+        opaque_background_(ui::LAYER_SOLID_COLOR) {
     InitWidget(container);
   }
 
+  // Sets widget bounds and sizes opaque background layer to fill the widget.
+  void SetBackgroundBounds(const gfx::Rect bounds, DockedAlignment alignment) {
+    SetBounds(bounds);
+    opaque_background_.SetBounds(gfx::Rect(bounds.size()));
+    alignment_ = alignment;
+  }
+
+  // Sets the docked area background type and starts transition animation.
+  void SetPaintsBackground(
+      ShelfBackgroundType background_type,
+      BackgroundAnimatorChangeType change_type) {
+    float target_opacity =
+        (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
+    scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
+    if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
+      opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
+          opaque_background_.GetAnimator()));
+      opaque_background_animation->SetTransitionDuration(
+          base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
+    }
+    opaque_background_.SetOpacity(target_opacity);
+
+    // TODO(varkha): use ui::Layer on both opaque_background and normal
+    // background retire background_animator_ at all. It would be simpler.
+    // See also ShelfWidget::SetPaintsBackground.
+    background_animator_.SetPaintsBackground(
+        background_type != SHELF_BACKGROUND_DEFAULT,
+        change_type);
+    SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
+  }
+
+  // views::Widget:
+  virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE {
+    const gfx::ImageSkia& launcher_background(
+        alignment_ == DOCKED_ALIGNMENT_LEFT ?
+            launcher_background_left_ : launcher_background_right_);
+    gfx::Rect rect = gfx::Rect(GetWindowBoundsInScreen().size());
+    SkPaint paint;
+    paint.setAlpha(alpha_);
+    canvas->DrawImageInt(
+        launcher_background,
+        0, 0, launcher_background.width(), launcher_background.height(),
+        alignment_ == DOCKED_ALIGNMENT_LEFT ?
+            rect.width() - launcher_background.width() : 0, 0,
+        launcher_background.width(), rect.height(),
+        false,
+        paint);
+    canvas->DrawImageInt(
+        launcher_background,
+        alignment_ == DOCKED_ALIGNMENT_LEFT ?
+            0 : launcher_background.width() - 1, 0,
+        1, launcher_background.height(),
+        alignment_ == DOCKED_ALIGNMENT_LEFT ?
+            0 : launcher_background.width(), 0,
+        rect.width() - launcher_background.width(), rect.height(),
+        false,
+        paint);
+  }
+
+  // BackgroundAnimatorDelegate:
+  virtual void UpdateBackground(int alpha) OVERRIDE {
+    alpha_ = alpha;
+    SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
+  }
+
  private:
   void InitWidget(aura::Window* parent) {
     views::Widget::InitParams params;
     params.type = views::Widget::InitParams::TYPE_POPUP;
-    params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.can_activate = false;
     params.keep_on_top = false;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -71,17 +143,41 @@
     set_focus_on_creation(false);
     Init(params);
     GetNativeWindow()->SetProperty(internal::kStayInSameRootWindowKey, true);
-    DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow());
-    views::View* content_view = new views::View;
-    content_view->set_background(
-        views::Background::CreateSolidBackground(kDockBackgroundColor));
-    SetContentsView(content_view);
+    opaque_background_.SetColor(SK_ColorBLACK);
+    opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
+    opaque_background_.SetOpacity(0.0f);
+    GetNativeWindow()->layer()->Add(&opaque_background_);
     Hide();
+
+    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+    gfx::ImageSkia launcher_background =
+        *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_BACKGROUND);
+    launcher_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage(
+        launcher_background, SkBitmapOperations::ROTATION_90_CW);
+    launcher_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage(
+        launcher_background, SkBitmapOperations::ROTATION_270_CW);
   }
 
+  DockedAlignment alignment_;
+
+  // The animator for the background transitions.
+  internal::BackgroundAnimator background_animator_;
+
+  // The alpha to use for drawing image assets covering the docked background.
+  int alpha_;
+
+  // Solid black background that can be made fully opaque.
+  ui::Layer opaque_background_;
+
+  // Backgrounds created from shelf background by 90 or 270 degree rotation.
+  gfx::ImageSkia launcher_background_left_;
+  gfx::ImageSkia launcher_background_right_;
+
   DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget);
 };
 
+namespace {
+
 // Returns true if a window is a popup or a transient child.
 bool IsPopupOrTransient(const aura::Window* window) {
   return (window->type() == aura::client::WINDOW_TYPE_POPUP ||
@@ -97,10 +193,10 @@
 
 void UndockWindow(aura::Window* window) {
   gfx::Rect previous_bounds = window->bounds();
-  aura::Window* previous_parent = window->parent();
+  aura::Window* old_parent = window->parent();
   aura::client::ParentWindowWithContext(window, window, gfx::Rect());
-  if (window->parent() != previous_parent)
-    wm::ReparentTransientChildrenOfChild(window->parent(), window);
+  if (window->parent() != old_parent)
+    wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent());
   // Start maximize or fullscreen (affecting packaged apps) animation from
   // previous window bounds.
   window->layer()->SetBounds(previous_bounds);
@@ -224,7 +320,46 @@
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager public implementation:
+// A class that observes launcher shelf for bounds changes.
+class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver {
+ public:
+  explicit ShelfWindowObserver(
+      DockedWindowLayoutManager* docked_layout_manager)
+      : docked_layout_manager_(docked_layout_manager) {
+    DCHECK(docked_layout_manager_->launcher()->shelf_widget());
+    docked_layout_manager_->launcher()->shelf_widget()->GetNativeView()
+        ->AddObserver(this);
+  }
+
+  virtual ~ShelfWindowObserver() {
+    if (docked_layout_manager_->launcher() &&
+        docked_layout_manager_->launcher()->shelf_widget())
+      docked_layout_manager_->launcher()->shelf_widget()->GetNativeView()
+          ->RemoveObserver(this);
+  }
+
+  // aura::WindowObserver:
+  virtual void OnWindowBoundsChanged(aura::Window* window,
+                                     const gfx::Rect& old_bounds,
+                                     const gfx::Rect& new_bounds) OVERRIDE {
+    shelf_bounds_in_screen_ = ScreenAsh::ConvertRectToScreen(
+        window->parent(), new_bounds);
+    docked_layout_manager_->OnShelfBoundsChanged();
+  }
+
+  const gfx::Rect& shelf_bounds_in_screen() const {
+    return shelf_bounds_in_screen_;
+  }
+
+ private:
+  DockedWindowLayoutManager* docked_layout_manager_;
+  gfx::Rect shelf_bounds_in_screen_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// DockedWindowLayoutManager public implementation:
 DockedWindowLayoutManager::DockedWindowLayoutManager(
     aura::Window* dock_container, WorkspaceController* workspace_controller)
     : dock_container_(dock_container),
@@ -252,6 +387,12 @@
 }
 
 void DockedWindowLayoutManager::Shutdown() {
+  if (launcher_ && launcher_->shelf_widget()) {
+    ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher(
+        launcher_->shelf_widget()->GetNativeWindow());
+    shelf_layout_manager->RemoveObserver(this);
+    shelf_observer_.reset();
+  }
   launcher_ = NULL;
   for (size_t i = 0; i < dock_container_->children().size(); ++i) {
     aura::Window* child = dock_container_->children()[i];
@@ -332,6 +473,12 @@
 void DockedWindowLayoutManager::SetLauncher(ash::Launcher* launcher) {
   DCHECK(!launcher_);
   launcher_ = launcher;
+  if (launcher_->shelf_widget()) {
+    ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher(
+        launcher_->shelf_widget()->GetNativeWindow());
+    shelf_layout_manager->AddObserver(this);
+    shelf_observer_.reset(new ShelfWindowObserver(this));
+  }
 }
 
 DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow(
@@ -416,8 +563,13 @@
   return true;
 }
 
+void DockedWindowLayoutManager::OnShelfBoundsChanged() {
+  Relayout();
+  UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, aura::LayoutManager implementation:
+// DockedWindowLayoutManager, aura::LayoutManager implementation:
 void DockedWindowLayoutManager::OnWindowResized() {
   MaybeMinimizeChildrenExcept(dragged_window_);
   Relayout();
@@ -482,10 +634,16 @@
     const gfx::Rect& requested_bounds) {
   // Whenever one of our windows is moved or resized enforce layout.
   SetChildBoundsDirect(child, requested_bounds);
+  if (IsPopupOrTransient(child))
+    return;
+  ShelfLayoutManager* shelf_layout = internal::ShelfLayoutManager::ForLauncher(
+      dock_container_);
+  if (shelf_layout)
+    shelf_layout->UpdateVisibilityState();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, ash::ShellObserver implementation:
+// DockedWindowLayoutManager, ash::ShellObserver implementation:
 
 void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
   Relayout();
@@ -551,7 +709,15 @@
 }
 
 /////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, WindowStateObserver implementation:
+// DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation:
+void DockedWindowLayoutManager::OnBackgroundUpdated(
+    ShelfBackgroundType background_type,
+    BackgroundAnimatorChangeType change_type) {
+  background_widget_->SetPaintsBackground(background_type, change_type);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// DockedWindowLayoutManager, WindowStateObserver implementation:
 
 void DockedWindowLayoutManager::OnWindowShowTypeChanged(
     wm::WindowState* window_state,
@@ -565,18 +731,19 @@
     return;
   if (window_state->IsMinimized()) {
     MinimizeDockedWindow(window_state);
-  } else if (window_state->IsMaximizedOrFullscreen()) {
-    // Reparenting changes the source bounds for the animation if a window is
-    // visible so hide it here and show later when it is already in the desktop.
-    UndockWindow(window);
-    RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN);
+  } else if (window_state->IsMaximizedOrFullscreen() ||
+             window_state->IsSnapped()) {
+    if (window != dragged_window_) {
+      UndockWindow(window);
+      RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN);
+    }
   } else if (old_type == wm::SHOW_TYPE_MINIMIZED) {
     RestoreDockedWindow(window_state);
   }
 }
 
 /////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, WindowObserver implementation:
+// DockedWindowLayoutManager, WindowObserver implementation:
 
 void DockedWindowLayoutManager::OnWindowBoundsChanged(
     aura::Window* window,
@@ -615,7 +782,8 @@
 
 
 ////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, aura::client::ActivationChangeObserver implementation:
+// DockedWindowLayoutManager, aura::client::ActivationChangeObserver
+// implementation:
 
 void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
                                                   aura::Window* lost_active) {
@@ -635,7 +803,7 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager private implementation:
+// DockedWindowLayoutManager private implementation:
 
 void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
     aura::Window* child) {
@@ -653,10 +821,17 @@
     if (window == child || !IsUsedByLayout(window))
       continue;
     int room_needed = GetWindowHeightCloseTo(window, 0) + kMinDockGap;
-    if (available_room > room_needed)
+    if (available_room > room_needed) {
       available_room -= room_needed;
-    else
+    } else {
+      // Slow down minimizing animations. Lock duration so that it is not
+      // overridden by other ScopedLayerAnimationSettings down the stack.
+      ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+      settings.SetTransitionDuration(
+          base::TimeDelta::FromMilliseconds(kMinimizeDurationMs));
+      settings.LockTransitionDuration();
       wm::GetWindowState(window)->Minimize();
+    }
   }
 }
 
@@ -673,7 +848,7 @@
     wm::WindowState* window_state) {
   aura::Window* window = window_state->window();
   DCHECK(!IsPopupOrTransient(window));
-  // Always place restored window at the top shuffling the other windows down.
+  // Always place restored window at the bottom shuffling the other windows up.
   // TODO(varkha): add a separate container for docked windows to keep track
   // of ordering.
   gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
@@ -687,7 +862,7 @@
     return;
   }
   gfx::Rect bounds(window->bounds());
-  bounds.set_y(work_area.y() - bounds.height());
+  bounds.set_y(work_area.bottom());
   window->SetBounds(bounds);
   window->Show();
   MaybeMinimizeChildrenExcept(window);
@@ -805,8 +980,10 @@
   }
 
   // Position docked windows as well as the window being dragged.
-  const gfx::Rect work_area =
+  gfx::Rect work_area =
       Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
+  if (shelf_observer_)
+    work_area.Subtract(shelf_observer_->shelf_bounds_in_screen());
   int available_room = CalculateWindowHeightsAndRemainingRoom(work_area,
                                                               &visible_windows);
   FanOutChildren(work_area,
@@ -983,14 +1160,14 @@
       observer_list_,
       OnDockBoundsChanging(bounds, reason));
   // Show or hide background for docked area.
-  background_widget_->SetBounds(docked_bounds_);
-  if (docked_width_ > 0) {
+  gfx::Rect background_bounds(docked_bounds_);
+  if (shelf_observer_)
+    background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen());
+  background_widget_->SetBackgroundBounds(background_bounds, alignment_);
+  if (docked_width_ > 0)
     background_widget_->Show();
-    background_widget_->GetNativeWindow()->layer()->SetOpacity(
-        kDockBackgroundOpacity);
-  } else {
+  else
     background_widget_->Hide();
-  }
 }
 
 void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
diff --git a/ash/wm/dock/docked_window_layout_manager.h b/ash/wm/dock/docked_window_layout_manager.h
index 9cb3826..8894bed 100644
--- a/ash/wm/dock/docked_window_layout_manager.h
+++ b/ash/wm/dock/docked_window_layout_manager.h
@@ -41,6 +41,7 @@
 class Launcher;
 
 namespace internal {
+class DockedBackgroundWidget;
 class DockedWindowLayoutManagerObserver;
 class DockedWindowResizerTest;
 class ShelfLayoutManager;
@@ -74,6 +75,7 @@
       public aura::WindowObserver,
       public aura::client::ActivationChangeObserver,
       public keyboard::KeyboardControllerObserver,
+      public ShelfLayoutManagerObserver,
       public wm::WindowStateObserver {
  public:
   // Maximum width of the docked windows area.
@@ -132,6 +134,9 @@
   // Returns true if currently dragged window is docked at the screen edge.
   bool is_dragged_window_docked() const { return is_dragged_window_docked_; }
 
+  // Updates docked layout when launcher shelf bounds change.
+  void OnShelfBoundsChanged();
+
   // aura::LayoutManager:
   virtual void OnWindowResized() OVERRIDE;
   virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
@@ -148,6 +153,11 @@
                                         aura::Window* root_window) OVERRIDE;
   virtual void OnShelfAlignmentChanged(aura::Window* root_window) OVERRIDE;
 
+  // ShelfLayoutManagerObserver:
+  virtual void OnBackgroundUpdated(
+      ShelfBackgroundType background_type,
+      BackgroundAnimatorChangeType change_type) OVERRIDE;
+
   // wm::WindowStateObserver:
   virtual void OnWindowShowTypeChanged(wm::WindowState* window_state,
                                        wm::WindowShowType old_type) OVERRIDE;
@@ -165,6 +175,7 @@
                                  aura::Window* lost_active) OVERRIDE;
 
  private:
+  class ShelfWindowObserver;
   friend class DockedWindowLayoutManagerTest;
   friend class DockedWindowResizerTest;
 
@@ -255,6 +266,7 @@
 
   // The launcher to respond to launcher alignment changes.
   Launcher* launcher_;
+
   // Workspace controller that can be checked for fullscreen mode.
   WorkspaceController* workspace_controller_;
   // Tracks if any window in the same root window is in fullscreen mode.
@@ -279,8 +291,11 @@
   // Used in UMA metrics.
   base::Time last_action_time_;
 
+  // Observes launcher shelf for bounds changes.
+  scoped_ptr<ShelfWindowObserver> shelf_observer_;
+
   // Widget used to paint a background for the docked area.
-  scoped_ptr<views::Widget> background_widget_;
+  scoped_ptr<DockedBackgroundWidget> background_widget_;
 
   // Observers of dock bounds changes.
   ObserverList<DockedWindowLayoutManagerObserver> observer_list_;
diff --git a/ash/wm/dock/docked_window_layout_manager_unittest.cc b/ash/wm/dock/docked_window_layout_manager_unittest.cc
index 9d31f4d..4b626e4 100644
--- a/ash/wm/dock/docked_window_layout_manager_unittest.cc
+++ b/ash/wm/dock/docked_window_layout_manager_unittest.cc
@@ -18,7 +18,7 @@
 #include "ash/test/launcher_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/wm/coordinate_conversion.h"
 #include "ash/wm/panels/panel_layout_manager.h"
 #include "ash/wm/window_resizer.h"
@@ -48,7 +48,7 @@
   virtual void SetUp() OVERRIDE {
     AshTestBase::SetUp();
     UpdateDisplay("600x600");
-    ASSERT_TRUE(test::TestLauncherDelegate::instance());
+    ASSERT_TRUE(test::TestShelfDelegate::instance());
 
     shelf_view_test_.reset(new test::ShelfViewTestAPI(
         test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view()));
@@ -72,9 +72,9 @@
     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
         NULL, window_type_, 0, bounds);
     if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
-      test::TestLauncherDelegate* launcher_delegate =
-          test::TestLauncherDelegate::instance();
-      launcher_delegate->AddLauncherItem(window);
+      test::TestShelfDelegate* shelf_delegate =
+          test::TestShelfDelegate::instance();
+      shelf_delegate->AddLauncherItem(window);
       PanelLayoutManager* manager =
           static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
               layout_manager());
@@ -89,9 +89,9 @@
     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
         delegate, window_type_, 0, bounds);
     if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
-      test::TestLauncherDelegate* launcher_delegate =
-          test::TestLauncherDelegate::instance();
-      launcher_delegate->AddLauncherItem(window);
+      test::TestShelfDelegate* shelf_delegate =
+          test::TestShelfDelegate::instance();
+      shelf_delegate->AddLauncherItem(window);
       PanelLayoutManager* manager =
           static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
               layout_manager());
diff --git a/ash/wm/dock/docked_window_resizer.cc b/ash/wm/dock/docked_window_resizer.cc
index 3380287..cd06bee 100644
--- a/ash/wm/dock/docked_window_resizer.cc
+++ b/ash/wm/dock/docked_window_resizer.cc
@@ -75,23 +75,14 @@
   }
   gfx::Point offset;
   gfx::Rect bounds(CalculateBoundsForDrag(details_, location));
-  bool set_tracked_by_workspace = MaybeSnapToEdge(bounds, &offset);
-
-  // Temporarily clear kWindowTrackedByWorkspaceKey for windows that are snapped
-  // to screen edges e.g. when they are docked. This prevents the windows from
-  // getting snapped to other nearby windows during the drag.
-  wm::WindowState* window_state = wm::GetWindowState(GetTarget());
-  bool was_tracked_by_workspace = window_state->tracked_by_workspace();
-  if (set_tracked_by_workspace)
-    window_state->SetTrackedByWorkspace(false);
-  gfx::Point modified_location(location.x() + offset.x(),
-                               location.y() + offset.y());
+  MaybeSnapToEdge(bounds, &offset);
+  gfx::Point modified_location(location);
+  modified_location.Offset(offset.x(), offset.y());
 
   base::WeakPtr<DockedWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr());
   next_window_resizer_->Drag(modified_location, event_flags);
   if (!resizer)
     return;
-  window_state->SetTrackedByWorkspace(was_tracked_by_workspace);
 
   DockedWindowLayoutManager* new_dock_layout =
       GetDockedLayoutManagerAtPoint(last_location_);
@@ -125,25 +116,12 @@
 }
 
 void DockedWindowResizer::CompleteDrag(int event_flags) {
-  // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they
-  // don't get forced into the workspace that may be shrunken because of docked
-  // windows.
-  wm::WindowState* window_state = wm::GetWindowState(GetTarget());
-  bool was_tracked_by_workspace = window_state->tracked_by_workspace();
-  window_state->SetTrackedByWorkspace(false);
   // The root window can change when dragging into a different screen.
   next_window_resizer_->CompleteDrag(event_flags);
   FinishedDragging();
-  window_state->SetTrackedByWorkspace(was_tracked_by_workspace);
 }
 
 void DockedWindowResizer::RevertDrag() {
-  // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they
-  // don't get forced into the workspace that may be shrunken because of docked
-  // windows.
-  wm::WindowState* window_state = wm::GetWindowState(GetTarget());
-  bool was_tracked_by_workspace = window_state->tracked_by_workspace();
-  window_state->SetTrackedByWorkspace(false);
   next_window_resizer_->RevertDrag();
   // Restore docked state to what it was before the drag if necessary.
   if (is_docked_ != was_docked_) {
@@ -154,7 +132,6 @@
       dock_layout_->UndockDraggedWindow();
   }
   FinishedDragging();
-  window_state->SetTrackedByWorkspace(was_tracked_by_workspace);
 }
 
 aura::Window* DockedWindowResizer::GetTarget() {
@@ -188,11 +165,11 @@
   is_docked_ = was_docked_;
 }
 
-bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
+void DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
                                           gfx::Point* offset) {
   // Windows only snap magnetically when they were previously docked.
   if (!was_docked_)
-    return false;
+    return;
   DockedAlignment dock_alignment = dock_layout_->CalculateAlignment();
   gfx::Rect dock_bounds = ScreenAsh::ConvertRectFromScreen(
       GetTarget()->parent(),
@@ -207,18 +184,15 @@
     const int distance = bounds.x() - dock_bounds.x();
     if (distance < kSnapToDockDistance && distance > 0) {
       offset->set_x(-distance);
-      return true;
+      return;
     }
   }
   if (dock_alignment == DOCKED_ALIGNMENT_RIGHT ||
       dock_alignment == DOCKED_ALIGNMENT_NONE) {
     const int distance = dock_bounds.right() - bounds.right();
-    if (distance < kSnapToDockDistance && distance > 0) {
+    if (distance < kSnapToDockDistance && distance > 0)
       offset->set_x(distance);
-      return true;
-    }
   }
-  return false;
 }
 
 void DockedWindowResizer::StartedDragging() {
@@ -246,7 +220,9 @@
     aura::Window* docked_container = Shell::GetContainer(
         GetTarget()->GetRootWindow(),
         kShellWindowId_DockedContainer);
-    wm::ReparentChildWithTransientChildren(docked_container, GetTarget());
+    wm::ReparentChildWithTransientChildren(GetTarget(),
+                                           GetTarget()->parent(),
+                                           docked_container);
   }
   if (is_docked_)
     dock_layout_->DockDraggedWindow(GetTarget());
@@ -313,7 +289,9 @@
   if ((is_resized || !is_attached_panel) &&
       is_docked_ != (window->parent() == dock_container)) {
     if (is_docked_) {
-      wm::ReparentChildWithTransientChildren(dock_container, window);
+      wm::ReparentChildWithTransientChildren(window,
+                                             window->parent(),
+                                             dock_container);
       action = DOCKED_ACTION_DOCK;
     } else if (window->parent()->id() == kShellWindowId_DockedContainer) {
       // Reparent the window back to workspace.
@@ -326,8 +304,11 @@
       // Reparenting will cause Relayout and possible dock shrinking.
       aura::Window* previous_parent = window->parent();
       aura::client::ParentWindowWithContext(window, window, near_last_location);
-      if (window->parent() != previous_parent)
-        wm::ReparentTransientChildrenOfChild(window->parent(), window);
+      if (window->parent() != previous_parent) {
+        wm::ReparentTransientChildrenOfChild(window,
+                                             previous_parent,
+                                             window->parent());
+      }
       action = was_docked_ ? DOCKED_ACTION_UNDOCK : DOCKED_ACTION_NONE;
     }
   } else {
diff --git a/ash/wm/dock/docked_window_resizer.h b/ash/wm/dock/docked_window_resizer.h
index b5e5ece..929259e 100644
--- a/ash/wm/dock/docked_window_resizer.h
+++ b/ash/wm/dock/docked_window_resizer.h
@@ -54,9 +54,9 @@
   DockedWindowResizer(WindowResizer* next_window_resizer,
                       const Details& details);
 
-  // Checks if the provided window bounds should snap to the side of a screen.
-  // If so the offset returned gives the necessary adjustment to snap.
-  bool MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset);
+  // If the provided window bounds should snap to the side of a screen,
+  // returns the offset that gives the necessary adjustment to snap.
+  void MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset);
 
   // Tracks the window's initial position and attachment at the start of a drag
   // and informs the DockLayoutManager that a drag has started if necessary.
diff --git a/ash/wm/dock/docked_window_resizer_unittest.cc b/ash/wm/dock/docked_window_resizer_unittest.cc
index 0c1a4c6..82b726e 100644
--- a/ash/wm/dock/docked_window_resizer_unittest.cc
+++ b/ash/wm/dock/docked_window_resizer_unittest.cc
@@ -17,15 +17,17 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/cursor_manager_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/wm/coordinate_conversion.h"
 #include "ash/wm/dock/docked_window_layout_manager.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/panels/panel_layout_manager.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
+#include "ash/wm/workspace/snap_sizer.h"
 #include "base/command_line.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/window_tree_client.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/base/hit_test.h"
@@ -76,9 +78,9 @@
         0,
         bounds);
     if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
-      test::TestLauncherDelegate* launcher_delegate =
-          test::TestLauncherDelegate::instance();
-      launcher_delegate->AddLauncherItem(window);
+      test::TestShelfDelegate* shelf_delegate =
+          test::TestShelfDelegate::instance();
+      shelf_delegate->AddLauncherItem(window);
       PanelLayoutManager* manager =
           static_cast<PanelLayoutManager*>(
               Shell::GetContainer(window->GetRootWindow(),
@@ -89,6 +91,28 @@
     return window;
   }
 
+  aura::Window* CreateModalWindow(const gfx::Rect& bounds) {
+    aura::Window* window = new aura::Window(&delegate_);
+    window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
+    window->SetType(aura::client::WINDOW_TYPE_NORMAL);
+    window->Init(ui::LAYER_TEXTURED);
+    window->Show();
+
+    if (bounds.IsEmpty()) {
+      ParentWindowInPrimaryRootWindow(window);
+    } else {
+      gfx::Display display =
+          Shell::GetScreen()->GetDisplayMatching(bounds);
+      aura::Window* root = ash::Shell::GetInstance()->display_controller()->
+          GetRootWindowForDisplayId(display.id());
+      gfx::Point origin = bounds.origin();
+      wm::ConvertPointFromScreen(root, &origin);
+      window->SetBounds(gfx::Rect(origin, bounds.size()));
+      aura::client::ParentWindowWithContext(window, root, bounds);
+    }
+    return window;
+  }
+
   static WindowResizer* CreateSomeWindowResizer(
       aura::Window* window,
       const gfx::Point& point_in_parent,
@@ -238,7 +262,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -253,7 +277,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), +4);
 
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -294,7 +318,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0);
 
-  // The window should be attached and snapped to the left dock.
+  // The window should be docked at the left edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().x(),
             window->GetBoundsInScreen().x());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -309,7 +333,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), -4);
 
-  // The window should be attached and snapped to the left dock.
+  // The window should be docked at the left edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().x(),
             window->GetBoundsInScreen().x());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -341,7 +365,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -381,7 +405,7 @@
       gfx::Rect(0, 0, ideal_width() + 10, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
-  // The window should be attached and docked at the right edge.
+  // The window should be docked at the right edge.
   // Its width should shrink to ideal width.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
@@ -423,7 +447,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -449,7 +473,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -473,7 +497,7 @@
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 50);
 
-  // Both windows should be attached and snapped to the right edge.
+  // Both windows should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -509,7 +533,7 @@
   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
 
-  // w1 should be attached and snapped to the right edge.
+  // w1 should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -554,7 +578,7 @@
   gfx::Rect initial_bounds(w2->bounds());
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w2.get(), 50);
 
-  // The first window should be attached and snapped to the right edge.
+  // The first window should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -575,7 +599,7 @@
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -621,7 +645,7 @@
   EXPECT_EQ(root_windows[0], window->GetRootWindow());
 
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
-  // The window should be attached and snapped to the right edge.
+  // The window should be docked at the right edge.
   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
             window->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -686,7 +710,7 @@
             ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
-  // A window should be attached and snapped to the right edge.
+  // A window should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -696,7 +720,7 @@
   EXPECT_EQ(w1->bounds().width(), docked_width(manager));
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
-  // Both windows should now be attached and snapped to the right edge.
+  // Both windows should now be docked at the right edge.
   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
             w2->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
@@ -758,7 +782,7 @@
             ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
-  // A window should be attached and snapped to the right edge.
+  // A window should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -857,7 +881,7 @@
             ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w1.get(), 20);
-  // A window should be attached and snapped to the left edge.
+  // A window should be docked at the left edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().x(),
             w1->GetBoundsInScreen().x());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -956,7 +980,7 @@
             ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
-  // A window should be attached and snapped to the right edge.
+  // A window should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -966,7 +990,7 @@
   EXPECT_EQ(w1->bounds().width(), docked_width(manager));
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
-  // Both windows should now be attached and snapped to the right edge.
+  // Both windows should now be docked at the right edge.
   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
             w2->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
@@ -1097,7 +1121,7 @@
   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
-  // A window should be attached and snapped to the right edge.
+  // A window should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -1226,7 +1250,7 @@
             ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).width());
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
-  // A window should be attached and snapped to the right edge.
+  // A window should be docked at the right edge.
   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
             w1->GetBoundsInScreen().right());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -1278,7 +1302,7 @@
 
   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20);
 
-  // A window should be attached and snapped to the right edge.
+  // A window should be docked at the right edge.
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
   EXPECT_EQ(internal::kShellWindowId_DockedContainer, child->parent()->id());
 
@@ -1302,6 +1326,105 @@
             child->GetBoundsInScreen().origin().ToString());
 }
 
+// Tests that reparenting windows during the drag does not affect system modal
+// windows that are transient children of the dragged windows.
+TEST_P(DockedWindowResizerTest, DragWindowWithModalTransientChild) {
+  if (!SupportsHostWindowResize())
+    return;
+
+  // Create a window.
+  scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
+  gfx::Rect bounds(window->bounds());
+
+  // Start dragging the window.
+  ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
+  gfx::Vector2d move_vector(40, test_panels() ? -60 : 60);
+  DragMove(move_vector.x(), move_vector.y());
+  EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
+
+  // While still dragging create a modal window and make it a transient child of
+  // the |window|.
+  scoped_ptr<aura::Window> child(CreateModalWindow(gfx::Rect(20, 20, 150, 20)));
+  window->AddTransientChild(child.get());
+  EXPECT_EQ(window.get(), child->transient_parent());
+  EXPECT_EQ(internal::kShellWindowId_SystemModalContainer,
+            child->parent()->id());
+
+  // End the drag, the |window| should have moved (if it is a panel it will
+  // no longer be attached to the shelf since we dragged it above).
+  DragEnd();
+  bounds.Offset(move_vector);
+  EXPECT_EQ(bounds.ToString(), window->GetBoundsInScreen().ToString());
+
+  // The original |window| should be in the default container (not docked or
+  // attached).
+  EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
+  // The transient |child| should still be in system modal container.
+  EXPECT_EQ(internal::kShellWindowId_SystemModalContainer,
+            child->parent()->id());
+  // The |child| should not have moved.
+  EXPECT_EQ(gfx::Point(20, 20).ToString(),
+            child->GetBoundsInScreen().origin().ToString());
+  // The |child| should still be a transient child of |window|.
+  EXPECT_EQ(window.get(), child->transient_parent());
+}
+
+// Tests that side snapping a window undocks it, closes the dock and then snaps.
+TEST_P(DockedWindowResizerTest, SideSnapDocked) {
+  if (!SupportsHostWindowResize() || test_panels())
+    return;
+
+  scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
+  wm::WindowState* window_state = wm::GetWindowState(w1.get());
+  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
+  // A window should be docked at the right edge.
+  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
+            w1->GetBoundsInScreen().right());
+  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
+  DockedWindowLayoutManager* manager =
+      static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
+  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
+  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
+  EXPECT_TRUE(window_state->IsDocked());
+  EXPECT_FALSE(window_state->IsSnapped());
+
+  // Side snap at right edge.
+  internal::SnapSizer::SnapWindow(window_state,
+                                  internal::SnapSizer::RIGHT_EDGE);
+  // The window should be snapped at the right edge and the dock should close.
+  gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()));
+  EXPECT_EQ(0, docked_width(manager));
+  EXPECT_EQ(work_area.height(), w1->bounds().height());
+  EXPECT_EQ(work_area.right(), w1->bounds().right());
+  EXPECT_EQ(internal::kShellWindowId_DefaultContainer, w1->parent()->id());
+  EXPECT_FALSE(window_state->IsDocked());
+  EXPECT_TRUE(window_state->IsSnapped());
+
+  // Dock again.
+  DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
+  // A window should be docked at the right edge.
+  EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
+            w1->GetBoundsInScreen().right());
+  EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
+  EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
+  EXPECT_EQ(w1->bounds().width(), docked_width(manager));
+  EXPECT_TRUE(window_state->IsDocked());
+  EXPECT_FALSE(window_state->IsSnapped());
+
+  // Side snap at left edge.
+  internal::SnapSizer::SnapWindow(window_state,
+                                  internal::SnapSizer::LEFT_EDGE);
+  // The window should be snapped at the right edge and the dock should close.
+  EXPECT_EQ(work_area.ToString(),
+            ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).ToString());
+  EXPECT_EQ(0, docked_width(manager));
+  EXPECT_EQ(work_area.height(), w1->bounds().height());
+  EXPECT_EQ(work_area.x(), w1->bounds().x());
+  EXPECT_EQ(internal::kShellWindowId_DefaultContainer, w1->parent()->id());
+  EXPECT_FALSE(window_state->IsDocked());
+  EXPECT_TRUE(window_state->IsSnapped());
+}
+
 // Tests run twice - on both panels and normal windows
 INSTANTIATE_TEST_CASE_P(NormalOrPanel,
                         DockedWindowResizerTest,
diff --git a/ash/wm/gestures/long_press_affordance_handler.cc b/ash/wm/gestures/long_press_affordance_handler.cc
index 45a995e..2a96f07 100644
--- a/ash/wm/gestures/long_press_affordance_handler.cc
+++ b/ash/wm/gestures/long_press_affordance_handler.cc
@@ -65,6 +65,7 @@
   params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
   params.keep_on_top = true;
   params.accept_events = false;
+  params.can_activate = false;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.context = root_window;
   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
diff --git a/ash/wm/gestures/shelf_gesture_handler.cc b/ash/wm/gestures/shelf_gesture_handler.cc
index 9dcd3f2..f9fb1b8 100644
--- a/ash/wm/gestures/shelf_gesture_handler.cc
+++ b/ash/wm/gestures/shelf_gesture_handler.cc
@@ -43,7 +43,7 @@
 
   ShelfLayoutManager* shelf = controller->GetShelfLayoutManager();
 
-  const aura::Window* fullscreen = controller->GetTopmostFullscreenWindow();
+  const aura::Window* fullscreen = controller->GetWindowForFullscreenMode();
   if (fullscreen &&
       ash::wm::GetWindowState(fullscreen)->hide_shelf_when_fullscreen()) {
     return false;
diff --git a/ash/wm/header_painter.cc b/ash/wm/header_painter.cc
index fb0b2c4..bf87015 100644
--- a/ash/wm/header_painter.cc
+++ b/ash/wm/header_painter.cc
@@ -9,7 +9,6 @@
 #include "ash/root_window_controller.h"
 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/wm/solo_window_tracker.h"
-#include "ash/wm/window_state.h"
 #include "base/logging.h"  // DCHECK
 #include "grit/ash_resources.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -165,10 +164,8 @@
 
 HeaderPainter::~HeaderPainter() {
   // Sometimes we are destroyed before the window closes, so ensure we clean up.
-  if (window_) {
+  if (window_)
     window_->RemoveObserver(this);
-    wm::GetWindowState(window_)->RemoveObserver(this);
-  }
 }
 
 void HeaderPainter::Init(
@@ -203,7 +200,6 @@
   // Observer removes itself in OnWindowDestroying() below, or in the destructor
   // if we go away before the window.
   window_->AddObserver(this);
-  wm::GetWindowState(window_)->AddObserver(this);
 
   // Solo-window header updates are handled by the WorkspaceLayoutManager when
   // this window is added to the desktop.
@@ -262,17 +258,6 @@
   return kThemeFrameImageInsetX;
 }
 
-bool HeaderPainter::ShouldUseMinimalHeaderStyle(Themed header_themed) const {
-  // Use the minimalistic header style whenever |frame_| is maximized or
-  // fullscreen EXCEPT:
-  // - If the user has installed a theme with custom images for the header.
-  // - For windows which are not tracked by the workspace code (which are used
-  //   for tab dragging).
-  return (frame_->IsMaximized() || frame_->IsFullscreen()) &&
-      header_themed == THEMED_NO &&
-      wm::GetWindowState(frame_->GetNativeWindow())->tracked_by_workspace();
-}
-
 void HeaderPainter::PaintHeader(gfx::Canvas* canvas,
                                 HeaderMode header_mode,
                                 int theme_frame_id,
@@ -433,7 +418,7 @@
   views::WidgetDelegate* delegate = frame_->widget_delegate();
   if (delegate && delegate->ShouldShowWindowTitle()) {
     gfx::Rect title_bounds = GetTitleBounds(title_font);
-    SkColor title_color = frame_->IsMaximized() ?
+    SkColor title_color = (frame_->IsMaximized() || frame_->IsFullscreen()) ?
         kMaximizedWindowTitleTextColor : kNonMaximizedWindowTitleTextColor;
     canvas->DrawStringInt(delegate->GetWindowTitle(),
                           title_font,
@@ -489,16 +474,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// WindowStateObserver overrides:
-void HeaderPainter::OnTrackedByWorkspaceChanged(wm::WindowState* window_state,
-                                                bool old) {
-  // When 'TrackedByWorkspace' changes, we are going to paint the header
-  // differently. Schedule a paint to ensure everything is updated correctly.
-  if (window_state->tracked_by_workspace())
-    header_view_->SchedulePaint();
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // aura::WindowObserver overrides:
 
 void HeaderPainter::OnWindowDestroying(aura::Window* destroying) {
@@ -507,7 +482,6 @@
   // Must be removed here and not in the destructor, as the aura::Window is
   // already destroyed when our destructor runs.
   window_->RemoveObserver(this);
-  wm::GetWindowState(window_)->RemoveObserver(this);
 
   window_ = NULL;
 }
@@ -550,11 +524,7 @@
 }
 
 int HeaderPainter::GetHeaderCornerRadius() const {
-  // Use square corners for maximized and fullscreen windows when they are
-  // tracked by the workspace code. (Windows which are not tracked by the
-  // workspace code are used for tab dragging.)
-  bool square_corners = ((frame_->IsMaximized() || frame_->IsFullscreen())) &&
-      wm::GetWindowState(frame_->GetNativeWindow())->tracked_by_workspace();
+  bool square_corners = (frame_->IsMaximized() || frame_->IsFullscreen());
   const int kCornerRadius = 2;
   return square_corners ? 0 : kCornerRadius;
 }
@@ -571,8 +541,8 @@
     return kFullyOpaque;
   }
 
-  // The header is fully opaque when using the minimalistic header style.
-  if (ShouldUseMinimalHeaderStyle(THEMED_NO))
+  // Maximized and fullscreen windows are fully opaque.
+  if (frame_->IsMaximized() || frame_->IsFullscreen())
     return kFullyOpaque;
 
   // Solo header is very transparent.
diff --git a/ash/wm/header_painter.h b/ash/wm/header_painter.h
index ea326af..accc6e3 100644
--- a/ash/wm/header_painter.h
+++ b/ash/wm/header_painter.h
@@ -6,7 +6,6 @@
 #define ASH_WM_HEADER_PAINTER_H_
 
 #include "ash/ash_export.h"
-#include "ash/wm/window_state_observer.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"  // OVERRIDE
 #include "base/gtest_prod_util.h"
@@ -36,8 +35,7 @@
 
 // Helper class for painting the window header.
 class ASH_EXPORT HeaderPainter : public aura::WindowObserver,
-                                 public gfx::AnimationDelegate,
-                                 public wm::WindowStateObserver {
+                                 public gfx::AnimationDelegate {
  public:
   // Opacity values for the window header in various states, from 0 to 255.
   static int kActiveWindowOpacity;
@@ -49,11 +47,6 @@
     INACTIVE
   };
 
-  enum Themed {
-    THEMED_YES,
-    THEMED_NO
-  };
-
   HeaderPainter();
   virtual ~HeaderPainter();
 
@@ -92,9 +85,6 @@
   // Returns the amount that the theme background should be inset.
   int GetThemeBackgroundXInset() const;
 
-  // Returns true if the header should be painted using a minimalistic style.
-  bool ShouldUseMinimalHeaderStyle(Themed header_themed) const;
-
   // Paints the header.
   // |theme_frame_overlay_id| is 0 if no overlay image should be used.
   void PaintHeader(gfx::Canvas* canvas,
@@ -124,6 +114,11 @@
     header_height_ = header_height;
   }
 
+  // Returns the header height.
+  int header_height() const {
+    return header_height_;
+  }
+
   // Schedule a re-paint of the entire title.
   void SchedulePaintForTitle(const gfx::Font& title_font);
 
@@ -136,27 +131,12 @@
                                      const gfx::Rect& old_bounds,
                                      const gfx::Rect& new_bounds) OVERRIDE;
 
-  // ash::WindowStateObserver override:
-  virtual void OnTrackedByWorkspaceChanged(wm::WindowState* window_state,
-                                           bool old) OVERRIDE;
-
   // Overridden from gfx::AnimationDelegate
   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, CreateAndDeleteSingleWindow);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeader);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderWithApp);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderWithPanel);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderModal);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderConstrained);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderNotDrawn);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderMultiDisplay);
   FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, GetHeaderOpacity);
   FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, TitleIconAlignment);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, ChildWindowVisibility);
-  FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest,
-                           NoCrashShutdownWithAlwaysOnTopWindow);
 
   // Returns the header bounds in the coordinates of |header_view_|. The header
   // is assumed to be positioned at the top left corner of |header_view_| and to
diff --git a/ash/wm/header_painter_unittest.cc b/ash/wm/header_painter_unittest.cc
index 551658a..8ce8917 100644
--- a/ash/wm/header_painter_unittest.cc
+++ b/ash/wm/header_painter_unittest.cc
@@ -123,29 +123,6 @@
                                  0));
 }
 
-// Test that the minimal header style is used in the proper situations.
-TEST_F(HeaderPainterTest, MinimalHeaderStyle) {
-  // Create a widget and a painter for it.
-  scoped_ptr<Widget> w(CreateTestWidget());
-  scoped_ptr<HeaderPainter> p(CreateTestPainter(w.get()));
-  w->Show();
-
-  // Regular non-maximized windows should not use the minimal header style.
-  EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO));
-
-  // Regular maximized windows should use the minimal header style.
-  w->Maximize();
-  EXPECT_TRUE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO));
-
-  // Test cases where the maximized window should not use the minimal header
-  // style.
-  EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_YES));
-
-  wm::GetWindowState(w->GetNativeWindow())->SetTrackedByWorkspace(false);
-  EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO));
-  wm::GetWindowState(w->GetNativeWindow())->SetTrackedByWorkspace(true);
-}
-
 // Ensure the title text is vertically aligned with the window icon.
 TEST_F(HeaderPainterTest, TitleIconAlignment) {
   scoped_ptr<Widget> w(CreateTestWidget());
diff --git a/ash/wm/immersive_fullscreen_controller.cc b/ash/wm/immersive_fullscreen_controller.cc
index ba6eb3c..28aa1dc 100644
--- a/ash/wm/immersive_fullscreen_controller.cc
+++ b/ash/wm/immersive_fullscreen_controller.cc
@@ -287,8 +287,12 @@
 
     if (reveal_state_ == REVEALED) {
       // Reveal was unsuccessful. Reacquire the revealed locks if appropriate.
-      UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+      UpdateLocatedEventRevealedLock(NULL);
       UpdateFocusRevealedLock();
+    } else {
+      // Clearing focus is important because it closes focus-related popups like
+      // the touch selection handles.
+      widget_->GetFocusManager()->ClearFocus();
     }
   } else {
     // Stop cursor-at-top tracking.
@@ -332,7 +336,7 @@
   }
   gfx::Point cursor_pos(0, bottommost_in_screen + 100);
   aura::Env::GetInstance()->set_last_mouse_location(cursor_pos);
-  UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+  UpdateLocatedEventRevealedLock(NULL);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -349,30 +353,29 @@
     return;
   }
 
-  // Mouse hover should not initiate revealing the top-of-window views while
-  // |native_window_| is inactive.
-  if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive())
-    return;
+  // Mouse hover can initiate revealing the top-of-window views while |widget_|
+  // is inactive.
 
-  // Mouse hover should not initiate revealing the top-of-window views while
-  // a window has mouse capture.
-  if (aura::client::GetCaptureWindow(native_window_))
-    return;
-
-  if (IsRevealed())
-    UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO);
-
-  // Trigger a reveal if the cursor pauses at the top of the screen for a
-  // while.
-  if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
+  if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
+    top_edge_hover_timer_.Stop();
+    UpdateLocatedEventRevealedLock(event);
+  } else if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
+    // Trigger a reveal if the cursor pauses at the top of the screen for a
+    // while.
     UpdateTopEdgeHoverTimer(event);
+  }
 }
 
 void ImmersiveFullscreenController::OnTouchEvent(ui::TouchEvent* event) {
   if (!enabled_ || event->type() != ui::ET_TOUCH_PRESSED)
     return;
 
-  UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO);
+  // Touch should not initiate revealing the top-of-window views while |widget_|
+  // is inactive.
+  if (!widget_->IsActive())
+    return;
+
+  UpdateLocatedEventRevealedLock(event);
 }
 
 void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) {
@@ -380,15 +383,17 @@
     return;
 
   // Touch gestures should not initiate revealing the top-of-window views while
-  // |native_window_| is inactive.
-  if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive())
+  // |widget_| is inactive.
+  if (!widget_->IsActive())
     return;
 
   switch (event->type()) {
     case ui::ET_GESTURE_SCROLL_BEGIN:
       if (ShouldHandleGestureEvent(GetEventLocationInScreen(*event))) {
         gesture_begun_ = true;
-        event->SetHandled();
+        // Do not consume the event. Otherwise, we end up consuming all
+        // ui::ET_GESTURE_SCROLL_BEGIN events in the top-of-window views
+        // when the top-of-window views are revealed.
       }
       break;
     case ui::ET_GESTURE_SCROLL_UPDATE:
@@ -436,21 +441,7 @@
 void ImmersiveFullscreenController::OnWidgetActivationChanged(
     views::Widget* widget,
     bool active) {
-  // Mouse hover should not initiate revealing the top-of-window views while
-  // |native_window_| is inactive.
-  top_edge_hover_timer_.Stop();
-
   UpdateFocusRevealedLock();
-
-  // Allow the top-of-window views to stay revealed if all of the revealed locks
-  // were released in the process of activating |widget| but the mouse is still
-  // hovered above the top-of-window views. For instance, if the bubble which
-  // has been keeping the top-of-window views revealed is hidden but the mouse
-  // is hovered above the top-of-window views, the top-of-window views should
-  // stay revealed. We cannot call UpdateLocatedEventRevealedLock() from
-  // BubbleManager::UpdateRevealedLock() because |widget| is not yet active
-  // at that time.
-  UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_YES);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -520,19 +511,17 @@
     return;
   observers_enabled_ = enable;
 
-  views::Widget* widget =
-      views::Widget::GetWidgetForNativeWindow(native_window_);
-  views::FocusManager* focus_manager = widget->GetFocusManager();
+  views::FocusManager* focus_manager = widget_->GetFocusManager();
 
   if (enable) {
-    widget->AddObserver(this);
+    widget_->AddObserver(this);
     focus_manager->AddFocusChangeListener(this);
     Shell::GetInstance()->AddPreTargetHandler(this);
     native_window_->AddObserver(this);
 
     RecreateBubbleManager();
   } else {
-    widget->RemoveObserver(this);
+    widget_->RemoveObserver(this);
     focus_manager->RemoveFocusChangeListener(this);
     Shell::GetInstance()->RemovePreTargetHandler(this);
     native_window_->RemoveObserver(this);
@@ -550,12 +539,22 @@
 void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer(
     ui::MouseEvent* event) {
   DCHECK(enabled_);
-  // Stop the timer if the top-of-window views are already revealed.
-  if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
-    top_edge_hover_timer_.Stop();
+  DCHECK(reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED);
+
+  // Check whether |native_window_| is the event target's parent window instead
+  // of checking for activation. This allows the timer to be started when
+  // |widget_| is inactive but prevents starting the timer if the mouse is over
+  // a portion of the top edge obscured by an unrelated widget.
+  if (!top_edge_hover_timer_.IsRunning() &&
+      !native_window_->Contains(static_cast<aura::Window*>(event->target()))) {
     return;
   }
 
+  // Mouse hover should not initiate revealing the top-of-window views while a
+  // window has mouse capture.
+  if (aura::client::GetCaptureWindow(native_window_))
+    return;
+
   gfx::Point location_in_screen = GetEventLocationInScreen(*event);
   if (ShouldIgnoreMouseEventAtLocation(location_in_screen))
     return;
@@ -591,8 +590,7 @@
 }
 
 void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock(
-    ui::LocatedEvent* event,
-    AllowRevealWhileClosing allow_reveal_while_closing) {
+    ui::LocatedEvent* event) {
   if (!enabled_)
     return;
   DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent());
@@ -601,18 +599,14 @@
   // views are sliding closed or are closed with the following exceptions:
   // - Hovering at y = 0 which is handled in OnMouseEvent().
   // - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent().
-  if (reveal_state_ == CLOSED ||
-      (reveal_state_ == SLIDING_CLOSED &&
-       allow_reveal_while_closing == ALLOW_REVEAL_WHILE_CLOSING_NO)) {
+  if (reveal_state_ == CLOSED || reveal_state_ == SLIDING_CLOSED)
     return;
-  }
 
-  // Neither the mouse nor touch should keep the top-of-window views revealed if
-  // |native_window_| is not active.
-  if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) {
-    located_event_revealed_lock_.reset();
-    return;
-  }
+  // For the sake of simplicity, ignore |widget_|'s activation in computing
+  // whether the top-of-window views should stay revealed. Ideally, the
+  // top-of-window views would stay revealed only when the mouse cursor is
+  // hovered above a non-obscured portion of the top-of-window views. The
+  // top-of-window views may be partially obscured when |widget_| is inactive.
 
   // Ignore all events while a window has capture. This keeps the top-of-window
   // views revealed during a drag.
@@ -679,10 +673,8 @@
     return;
 
   bool hold_lock = false;
-  views::Widget* widget =
-      views::Widget::GetWidgetForNativeWindow(native_window_);
-  if (widget->IsActive()) {
-    views::View* focused_view = widget->GetFocusManager()->GetFocusedView();
+  if (widget_->IsActive()) {
+    views::View* focused_view = widget_->GetFocusManager()->GetFocusedView();
     if (top_container_->Contains(focused_view))
       hold_lock = true;
   } else {
@@ -729,7 +721,7 @@
 
   // Swipes while |native_window_| is inactive should have been filtered out in
   // OnGestureEvent().
-  DCHECK(views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive());
+  DCHECK(widget_->IsActive());
 
   if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
     if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) {
@@ -743,11 +735,13 @@
       located_event_revealed_lock_.reset();
       focus_revealed_lock_.reset();
 
-      if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED)
+      if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
+        widget_->GetFocusManager()->ClearFocus();
         return true;
+      }
 
       // Ending the reveal was unsuccessful. Reaquire the locks if appropriate.
-      UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+      UpdateLocatedEventRevealedLock(NULL);
       UpdateFocusRevealedLock();
     }
   }
@@ -808,7 +802,7 @@
 
   // The user may not have moved the mouse since the reveal was initiated.
   // Update the revealed lock to reflect the mouse's current state.
-  UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+  UpdateLocatedEventRevealedLock(NULL);
 }
 
 void ImmersiveFullscreenController::MaybeEndReveal(Animate animate) {
@@ -880,6 +874,7 @@
 
 bool ImmersiveFullscreenController::ShouldHandleGestureEvent(
     const gfx::Point& location) const {
+  DCHECK(widget_->IsActive());
   if (reveal_state_ == REVEALED) {
     std::vector<gfx::Rect> hit_bounds_in_screen(
         delegate_->GetVisibleBoundsInScreen());
diff --git a/ash/wm/immersive_fullscreen_controller.h b/ash/wm/immersive_fullscreen_controller.h
index 9d0f757..7e0ca78 100644
--- a/ash/wm/immersive_fullscreen_controller.h
+++ b/ash/wm/immersive_fullscreen_controller.h
@@ -143,10 +143,6 @@
  private:
   friend class ImmersiveFullscreenControllerTest;
 
-  enum AllowRevealWhileClosing {
-    ALLOW_REVEAL_WHILE_CLOSING_YES,
-    ALLOW_REVEAL_WHILE_CLOSING_NO
-  };
   enum Animate {
     ANIMATE_NO,
     ANIMATE_SLOW,
@@ -176,12 +172,7 @@
   // Updates |located_event_revealed_lock_| based on the current mouse state and
   // the current touch state.
   // |event| is NULL if the source event is not known.
-  // |allow_reveal_while_closing| indicates whether the mouse and touch
-  // are allowed to initiate a reveal while the top-of-window views are sliding
-  // closed.
-  void UpdateLocatedEventRevealedLock(
-      ui::LocatedEvent* event,
-      AllowRevealWhileClosing allow_reveal_while_closing);
+  void UpdateLocatedEventRevealedLock(ui::LocatedEvent* event);
 
   // Acquires |located_event_revealed_lock_| if it is not already held.
   void AcquireLocatedEventRevealedLock();
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index 9d0b21b..672e976 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -138,27 +138,12 @@
         new MockImmersiveFullscreenControllerDelegate(top_container_));
     controller_.reset(new ImmersiveFullscreenController);
     controller_->Init(delegate_.get(), widget_, top_container_);
-    SetAnimationsDisabled(true);
+    controller_->SetupForTest();
 
     // The mouse is moved so that it is not over |top_container_| by
     // AshTestBase.
   }
 
-  // Enable or disable the ImmersiveFullscreenController's animations. When the
-  // ImmersiveFullscreenController's animations are disabled, some behavior is
-  // slightly different. In particular, the behavior is different when there
-  // is a transfer in which lock keeps the top-of-window views revealed (eg
-  // bubble keeps top-of-window views revealed -> mouse keeps top-of-window
-  // views revealed). It is necessary to temporarily enable the
-  // ImmersiveFullscreenController's animations to get the correct behavior in
-  // tests.
-  void SetAnimationsDisabled(bool disabled) {
-    controller_->animations_disabled_for_test_ = disabled;
-    // Force any in progress animations to finish.
-    if (disabled)
-      controller_->animation_->End();
-  }
-
   // Attempt to reveal the top-of-window views via |modality|.
   // The top-of-window views can only be revealed via mouse hover or a gesture.
   void AttemptReveal(Modality modality) {
@@ -398,7 +383,7 @@
                               top_container_bounds_in_screen.bottom() + 50);
   EXPECT_FALSE(controller()->IsRevealed());
 
-  // The mouse position cannot cause a reveal when TopContainerView's widget
+  // The mouse position cannot cause a reveal when the top container's widget
   // has capture.
   views::Widget* widget = top_container()->GetWidget();
   widget->SetCapture(top_container());
@@ -406,7 +391,7 @@
   EXPECT_FALSE(controller()->IsRevealed());
   widget->ReleaseCapture();
 
-  // The mouse position cannot end the reveal while TopContainerView's widget
+  // The mouse position cannot end the reveal while the top container's widget
   // has capture.
   AttemptReveal(MODALITY_MOUSE);
   EXPECT_TRUE(controller()->IsRevealed());
@@ -420,6 +405,64 @@
   EXPECT_FALSE(controller()->IsRevealed());
 }
 
+// Test mouse event processing for top-of-screen reveal triggering when the
+// top container's widget is inactive.
+TEST_F(ImmersiveFullscreenControllerTest, Inactive) {
+  // Set up initial state.
+  views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
+      NULL,
+      CurrentContext(),
+      gfx::Rect(0, 0, 200, 200));
+  popup_widget->Show();
+  ASSERT_FALSE(top_container()->GetWidget()->IsActive());
+
+  controller()->SetEnabled(true);
+  ASSERT_TRUE(controller()->IsEnabled());
+  ASSERT_FALSE(controller()->IsRevealed());
+
+  gfx::Rect top_container_bounds_in_screen =
+      top_container()->GetBoundsInScreen();
+  gfx::Rect popup_bounds_in_screen = popup_widget->GetWindowBoundsInScreen();
+  ASSERT_EQ(top_container_bounds_in_screen.origin().ToString(),
+            popup_bounds_in_screen.origin().ToString());
+  ASSERT_GT(top_container_bounds_in_screen.right(),
+            popup_bounds_in_screen.right());
+
+  // The top-of-window views should stay hidden if the cursor is at the top edge
+  // but above an obscured portion of the top-of-window views.
+  MoveMouse(popup_bounds_in_screen.x(),
+            top_container_bounds_in_screen.y());
+  EXPECT_FALSE(controller()->IsRevealed());
+
+  // The top-of-window views should reveal if the cursor is at the top edge and
+  // above an unobscured portion of the top-of-window views.
+  MoveMouse(top_container_bounds_in_screen.right() - 1,
+            top_container_bounds_in_screen.y());
+  EXPECT_TRUE(controller()->IsRevealed());
+
+  // The top-of-window views should stay revealed if the cursor is moved off
+  // of the top edge.
+  MoveMouse(top_container_bounds_in_screen.right() - 1,
+            top_container_bounds_in_screen.bottom() - 1);
+  EXPECT_TRUE(controller()->IsRevealed());
+
+  // Moving way off of the top-of-window views should end the immersive reveal.
+  MoveMouse(top_container_bounds_in_screen.right() - 1,
+            top_container_bounds_in_screen.bottom() + 50);
+  EXPECT_FALSE(controller()->IsRevealed());
+
+  // Moving way off of the top-of-window views in a region where the
+  // top-of-window views are obscured should also end the immersive reveal.
+  // Ideally, the immersive reveal would end immediately when the cursor moves
+  // to an obscured portion of the top-of-window views.
+  MoveMouse(top_container_bounds_in_screen.right() - 1,
+            top_container_bounds_in_screen.y());
+  EXPECT_TRUE(controller()->IsRevealed());
+  MoveMouse(top_container_bounds_in_screen.x(),
+            top_container_bounds_in_screen.bottom() + 50);
+  EXPECT_FALSE(controller()->IsRevealed());
+}
+
 // Test mouse event processing for top-of-screen reveal triggering when the user
 // has a vertical display layout (primary display above/below secondary display)
 // and the immersive fullscreen window is on the bottom display.
@@ -495,6 +538,20 @@
   // the bottom region of the secondary display.
   event_generator.MoveMouseTo(x, y_top_edge - 20);
   EXPECT_FALSE(controller()->IsRevealed());
+
+  // Test that it is possible to reveal the top-of-window views by overshooting
+  // the top edge slightly when the top container's widget is not active.
+  views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
+      NULL,
+      CurrentContext(),
+      gfx::Rect(0, 200, 100, 100));
+  popup_widget->Show();
+  ASSERT_FALSE(top_container()->GetWidget()->IsActive());
+  ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
+      popup_widget->GetWindowBoundsInScreen()));
+  event_generator.MoveMouseTo(x, y_top_edge + 1);
+  MoveMouse(x, y_top_edge - 2);
+  EXPECT_TRUE(controller()->IsRevealed());
 }
 
 // Test behavior when the mouse becomes hovered without moving.
@@ -517,9 +574,9 @@
   EXPECT_FALSE(controller()->IsRevealed());
 
   // 2) Test that if the mouse becomes hovered without moving because of a
-  // reveal in ImmersiveFullscreenController::controller()->SetEnabled(true)
-  // and there are no locks keeping the top-of-window views revealed, that mouse
-  // hover does not prevent the top-of-window views from closing.
+  // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
+  // locks keeping the top-of-window views revealed, that mouse hover does not
+  // prevent the top-of-window views from closing.
   controller()->SetEnabled(false);
   SetHovered(true);
   EXPECT_FALSE(controller()->IsRevealed());
@@ -527,10 +584,9 @@
   EXPECT_FALSE(controller()->IsRevealed());
 
   // 3) Test that if the mouse becomes hovered without moving because of a
-  // reveal in ImmersiveFullscreenController::controller()->SetEnabled(true)
-  // and there is a lock keeping the top-of-window views revealed, that the
-  // top-of-window views do not hide till the mouse moves off of the
-  // top-of-window views.
+  // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
+  // lock keeping the top-of-window views revealed, that the top-of-window views
+  // do not hide till the mouse moves off of the top-of-window views.
   controller()->SetEnabled(false);
   SetHovered(true);
   lock.reset(controller()->GetRevealedLock(
@@ -593,7 +649,11 @@
   EXPECT_TRUE(controller()->IsRevealed());
   AttemptUnreveal(MODALITY_GESTURE);
   EXPECT_FALSE(controller()->IsRevealed());
-  top_container()->GetFocusManager()->ClearFocus();
+
+  // The top-of-window views should no longer have focus. Clearing focus is
+  // important because it closes focus-related popup windows like the touch
+  // selection handles.
+  EXPECT_FALSE(top_container()->HasFocus());
 
   // If some other code is holding onto a lock, a gesture should not be able to
   // end the reveal.
@@ -676,10 +736,9 @@
   EXPECT_FALSE(controller()->IsRevealed());
 }
 
-// Test how activation affects whether the top-of-window views are revealed.
-// The behavior when a bubble is activated is tested in
-// ImmersiveFullscreenControllerTest.Bubbles.
-TEST_F(ImmersiveFullscreenControllerTest, Activation) {
+// Test how transient windows affect whether the top-of-window views are
+// revealed.
+TEST_F(ImmersiveFullscreenControllerTest, Transient) {
   views::Widget* top_container_widget = top_container()->GetWidget();
 
   controller()->SetEnabled(true);
@@ -692,36 +751,34 @@
   transient_params.ownership =
       views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   transient_params.parent = top_container_widget->GetNativeView();
-  transient_params.bounds = gfx::Rect(0, 0, 100, 100);
+  transient_params.bounds = gfx::Rect(0, 100, 100, 100);
   scoped_ptr<views::Widget> transient_widget(new views::Widget());
   transient_widget->Init(transient_params);
-  transient_widget->Show();
 
   EXPECT_FALSE(controller()->IsRevealed());
-  top_container_widget->Activate();
   AttemptReveal(MODALITY_MOUSE);
   EXPECT_TRUE(controller()->IsRevealed());
-  transient_widget->Activate();
+  transient_widget->Show();
   SetHovered(false);
   EXPECT_TRUE(controller()->IsRevealed());
   transient_widget.reset();
   EXPECT_FALSE(controller()->IsRevealed());
 
-  // 2) Test that activating a non-transient window ends the reveal if any.
+  // 2) Test that activating a non-transient window does not keep the
+  // top-of-window views revealed.
   views::Widget::InitParams non_transient_params;
   non_transient_params.ownership =
       views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   non_transient_params.context = top_container_widget->GetNativeView();
-  non_transient_params.bounds = gfx::Rect(0, 0, 100, 100);
+  non_transient_params.bounds = gfx::Rect(0, 100, 100, 100);
   scoped_ptr<views::Widget> non_transient_widget(new views::Widget());
   non_transient_widget->Init(non_transient_params);
-  non_transient_widget->Show();
 
   EXPECT_FALSE(controller()->IsRevealed());
-  top_container_widget->Activate();
   AttemptReveal(MODALITY_MOUSE);
   EXPECT_TRUE(controller()->IsRevealed());
-  non_transient_widget->Activate();
+  non_transient_widget->Show();
+  SetHovered(false);
   EXPECT_FALSE(controller()->IsRevealed());
 }
 
@@ -774,11 +831,7 @@
   bubble_widget3->Show();
   SetHovered(true);
   EXPECT_TRUE(controller()->IsRevealed());
-
-  SetAnimationsDisabled(false);
-  // Activating |top_container_widget| will close |bubble_widget3|.
   top_container_widget->Activate();
-  SetAnimationsDisabled(true);
   EXPECT_TRUE(controller()->IsRevealed());
 
   // 3) Test that the top-of-window views stay revealed as long as at least one
diff --git a/ash/wm/mru_window_tracker_unittest.cc b/ash/wm/mru_window_tracker_unittest.cc
index 0cebd9e..924060d 100644
--- a/ash/wm/mru_window_tracker_unittest.cc
+++ b/ash/wm/mru_window_tracker_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
diff --git a/ash/wm/overview/window_overview.cc b/ash/wm/overview/window_overview.cc
index e7d914b..4542f18 100644
--- a/ash/wm/overview/window_overview.cc
+++ b/ash/wm/overview/window_overview.cc
@@ -21,6 +21,7 @@
 #include "ui/aura/window.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/event.h"
+#include "ui/views/background.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 79d428f..db5255b 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -300,6 +300,7 @@
   }
 
   if (mode == WindowSelector::CYCLE) {
+    cycle_start_time_ = base::Time::Now();
     event_handler_.reset(new WindowSelectorEventFilter(this));
     if (timer_enabled_)
       start_overview_timer_.Reset();
@@ -327,6 +328,11 @@
   // Clearing the window list resets the ignored_by_shelf flag on the windows.
   windows_.clear();
   UpdateShelfVisibility();
+
+  if (!cycle_start_time_.is_null()) {
+    UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.CycleTime",
+        base::Time::Now() - cycle_start_time_);
+  }
 }
 
 void WindowSelector::Step(WindowSelector::Direction direction) {
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index 84b5aa9..faee73d 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -118,6 +118,9 @@
   base::DelayTimer<WindowSelector> start_overview_timer_;
   scoped_ptr<WindowOverview> window_overview_;
 
+  // The time when window cycling was started.
+  base::Time cycle_start_time_;
+
   // Weak pointer to the selector delegate which will be called when a
   // selection is made.
   WindowSelectorDelegate* delegate_;
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index a148357..fcbb9ae 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -72,18 +72,6 @@
 
 void WindowSelectorController::OnWindowSelected(aura::Window* window) {
   window_selector_.reset();
-
-  // If there is a fullscreen window on this display and it was not selected
-  // it should exit fullscreen mode.
-  internal::RootWindowController* controller =
-      internal::GetRootWindowController(window->GetRootWindow());
-  aura::Window* fullscreen_window = NULL;
-  if (controller)
-    fullscreen_window = controller->GetTopmostFullscreenWindow();
-  if (fullscreen_window && fullscreen_window != window) {
-    wm::GetWindowState(fullscreen_window)->ToggleFullscreen();
-  }
-
   wm::ActivateWindow(window);
   last_selection_time_ = base::Time::Now();
   Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(false);
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index a8e8d08..988a586 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -11,7 +11,7 @@
 #include "ash/test/launcher_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/overview/window_selector.h"
 #include "ash/wm/overview/window_selector_controller.h"
@@ -105,7 +105,7 @@
 
   virtual void SetUp() OVERRIDE {
     test::AshTestBase::SetUp();
-    ASSERT_TRUE(test::TestLauncherDelegate::instance());
+    ASSERT_TRUE(test::TestShelfDelegate::instance());
 
     shelf_view_test_.reset(new test::ShelfViewTestAPI(
         test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view()));
@@ -127,7 +127,7 @@
   aura::Window* CreatePanelWindow(const gfx::Rect& bounds) {
     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
         NULL, aura::client::WINDOW_TYPE_PANEL, 0, bounds);
-    test::TestLauncherDelegate::instance()->AddLauncherItem(window);
+    test::TestShelfDelegate::instance()->AddLauncherItem(window);
     shelf_view_test()->RunMessageLoopUntilAnimationsDone();
     return window;
   }
@@ -284,12 +284,13 @@
   EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
   EXPECT_FALSE(panel1->IsVisible());
 
-  // Entering overview and selecting another window should exit fullscreen.
+  // Entering overview and selecting another window, the previous window remains
+  // fullscreen.
   // TODO(flackr): Currently the panel remains hidden, but should become visible
   // again.
   ToggleOverview();
   ClickWindow(window2.get());
-  EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
+  EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
 }
 
 // Tests that the shelf dimming state is removed while in overview and restored
diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc
index bafe29f..3c01e52 100644
--- a/ash/wm/panels/panel_frame_view.cc
+++ b/ash/wm/panels/panel_frame_view.cc
@@ -29,6 +29,7 @@
       title_font_(gfx::Font(views::NativeWidgetAura::GetWindowTitleFont())),
       frame_border_hit_test_controller_(
           new FrameBorderHitTestController(frame_)) {
+  DCHECK(!frame_->widget_delegate()->CanMaximize());
   if (frame_type != FRAME_NONE)
     InitHeaderPainter();
 }
@@ -115,9 +116,7 @@
     return;
   bool paint_as_active = ShouldPaintAsActive();
   int theme_frame_id = 0;
-  if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO))
-    theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
-  else if (paint_as_active)
+  if (paint_as_active)
     theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
   else
     theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
diff --git a/ash/wm/panels/panel_layout_manager.cc b/ash/wm/panels/panel_layout_manager.cc
index ee6a75e..ec7027b 100644
--- a/ash/wm/panels/panel_layout_manager.cc
+++ b/ash/wm/panels/panel_layout_manager.cc
@@ -353,9 +353,10 @@
     // back to appropriate container and ignore it.
     // TODO(varkha): Updating bounds during a drag can cause problems and a more
     // general solution is needed. See http://crbug.com/251813 .
+    aura::Window* old_parent = child->parent();
     aura::client::ParentWindowWithContext(
         child, child, child->GetRootWindow()->GetBoundsInScreen());
-    wm::ReparentTransientChildrenOfChild(child->parent(), child);
+    wm::ReparentTransientChildrenOfChild(child, old_parent, child->parent());
     DCHECK(child->parent()->id() != kShellWindowId_PanelContainer);
     return;
   }
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 0a71814..5351afa 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -21,7 +21,7 @@
 #include "ash/test/launcher_test_api.h"
 #include "ash/test/shelf_view_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/window_util.h"
 #include "base/basictypes.h"
@@ -50,7 +50,7 @@
 
   virtual void SetUp() OVERRIDE {
     test::AshTestBase::SetUp();
-    ASSERT_TRUE(test::TestLauncherDelegate::instance());
+    ASSERT_TRUE(test::TestShelfDelegate::instance());
 
     shelf_view_test_.reset(new test::ShelfViewTestAPI(
         GetShelfView(Launcher::ForPrimaryDisplay())));
@@ -67,12 +67,11 @@
         aura::client::WINDOW_TYPE_PANEL,
         0,
         bounds);
-    test::TestLauncherDelegate* launcher_delegate =
-        test::TestLauncherDelegate::instance();
-    launcher_delegate->AddLauncherItem(window);
-    PanelLayoutManager* manager =
-        static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
-                                         layout_manager());
+    test::TestShelfDelegate* shelf_delegate =
+        test::TestShelfDelegate::instance();
+    shelf_delegate->AddLauncherItem(window);
+    PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(
+        GetPanelContainer(window)->layout_manager());
     manager->Relayout();
     shelf_view_test()->RunMessageLoopUntilAnimationsDone();
     return window;
diff --git a/ash/wm/panels/panel_window_resizer.cc b/ash/wm/panels/panel_window_resizer.cc
index d7c0652..739544f 100644
--- a/ash/wm/panels/panel_window_resizer.cc
+++ b/ash/wm/panels/panel_window_resizer.cc
@@ -198,9 +198,10 @@
     // is reparented to a container in the root window that has that window.
     aura::Window* target = GetTarget();
     aura::Window* target_root = target->GetRootWindow();
+    aura::Window* old_parent = target->parent();
     aura::client::ParentWindowWithContext(
         target, target_root, target_root->GetBoundsInScreen());
-    wm::ReparentTransientChildrenOfChild(target->parent(), target);
+    wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent());
   }
 }
 
@@ -215,9 +216,10 @@
     // is reparented to a container in the root window that has that location.
     aura::Window* target = GetTarget();
     aura::Window* target_root = target->GetRootWindow();
+    aura::Window* old_parent = target->parent();
     aura::client::ParentWindowWithContext(
         target, target_root, gfx::Rect(last_location_, gfx::Size()));
-    wm::ReparentTransientChildrenOfChild(target->parent(), GetTarget());
+    wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent());
   }
 
   // If we started the drag in one root window and moved into another root
diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc
index 0e96afd..f264b2b 100644
--- a/ash/wm/panels/panel_window_resizer_unittest.cc
+++ b/ash/wm/panels/panel_window_resizer_unittest.cc
@@ -16,7 +16,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/cursor_manager_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/wm/drag_window_resizer.h"
 #include "ash/wm/panels/panel_layout_manager.h"
 #include "ash/wm/window_state.h"
@@ -41,7 +41,7 @@
     UpdateDisplay("600x400");
     test::ShellTestApi test_api(Shell::GetInstance());
     model_ = test_api.shelf_model();
-    launcher_delegate_ = test::TestLauncherDelegate::instance();
+    shelf_delegate_ = test::TestShelfDelegate::instance();
   }
 
   virtual void TearDown() OVERRIDE {
@@ -65,7 +65,7 @@
         aura::client::WINDOW_TYPE_PANEL,
         0,
         bounds);
-    launcher_delegate_->AddLauncherItem(window);
+    shelf_delegate_->AddLauncherItem(window);
     PanelLayoutManager* manager =
         static_cast<PanelLayoutManager*>(
             Shell::GetContainer(window->GetRootWindow(),
@@ -188,7 +188,7 @@
   scoped_ptr<WindowResizer> resizer_;
   internal::PanelLayoutManager* panel_layout_manager_;
   ShelfModel* model_;
-  test::TestLauncherDelegate* launcher_delegate_;
+  test::TestShelfDelegate* shelf_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTest);
 };
diff --git a/ash/wm/session_state_animator.cc b/ash/wm/session_state_animator.cc
index 38091bf..de8cd42 100644
--- a/ash/wm/session_state_animator.cc
+++ b/ash/wm/session_state_animator.cc
@@ -613,22 +613,5 @@
   }
 }
 
-void SessionStateAnimator::CreateForeground() {
-  if (foreground_)
-    return;
-  aura::Window* window = Shell::GetContainer(
-      Shell::GetPrimaryRootWindow(),
-      internal::kShellWindowId_PowerButtonAnimationContainer);
-  HideWindowImmediately(window, NULL);
-  foreground_.reset(
-      new ColoredWindowController(window, "SessionStateAnimatorForeground"));
-  foreground_->SetColor(SK_ColorWHITE);
-  foreground_->GetWidget()->Show();
-}
-
-void SessionStateAnimator::DropForeground() {
-  foreground_.reset();
-}
-
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/wm/session_state_animator.h b/ash/wm/session_state_animator.h
index 19ead2f..4cb183e 100644
--- a/ash/wm/session_state_animator.h
+++ b/ash/wm/session_state_animator.h
@@ -6,7 +6,6 @@
 #define ASH_WM_SESSION_STATE_ANIMATOR_H_
 
 #include "ash/ash_export.h"
-#include "ash/wm/workspace/colored_window_controller.h"
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/timer/timer.h"
@@ -140,12 +139,6 @@
   static void GetContainers(int container_mask,
                             aura::Window::Windows* containers);
 
-  // Create |foreground_| layer if it doesn't already exist, but makes it
-  // completely transparent.
-  void CreateForeground();
-  // Destroy |foreground_| when it is not needed anymore.
-  void DropForeground();
-
   // Apply animation |type| to all containers included in |container_mask| with
   // specified |speed|.
   void StartAnimation(int container_mask,
@@ -178,10 +171,6 @@
                              AnimationSpeed speed,
                              ui::LayerAnimationObserver* observer);
 
-  // White foreground that is used during shutdown animation to "fade
-  // everything into white".
-  scoped_ptr<ColoredWindowController> foreground_;
-
   DISALLOW_COPY_AND_ASSIGN(SessionStateAnimator);
 };
 
diff --git a/ash/wm/solo_window_tracker.cc b/ash/wm/solo_window_tracker.cc
index 71299d2..633d3bd 100644
--- a/ash/wm/solo_window_tracker.cc
+++ b/ash/wm/solo_window_tracker.cc
@@ -60,7 +60,7 @@
       window->layer() &&
       window->layer()->type() != ui::LAYER_NOT_DRAWN &&
       window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE &&
-      !window->GetProperty(ash::kConstrainedWindowKey);
+      !window->GetProperty(aura::client::kConstrainedWindowKey);
 }
 
 // Schedule's a paint of the window's entire bounds.
diff --git a/ash/wm/solo_window_tracker_unittest.cc b/ash/wm/solo_window_tracker_unittest.cc
index 5792008..431ae00 100644
--- a/ash/wm/solo_window_tracker_unittest.cc
+++ b/ash/wm/solo_window_tracker_unittest.cc
@@ -29,10 +29,14 @@
 class WindowRepaintChecker : public aura::WindowObserver {
  public:
   explicit WindowRepaintChecker(aura::Window* window)
-      : is_paint_scheduled_(false) {
-    window->AddObserver(this);
+      : window_(window),
+        is_paint_scheduled_(false) {
+    window_->AddObserver(this);
   }
+
   virtual ~WindowRepaintChecker() {
+    if (window_)
+      window_->RemoveObserver(this);
   }
 
   bool IsPaintScheduledAndReset() {
@@ -47,10 +51,12 @@
                                       const gfx::Rect& region) OVERRIDE {
     is_paint_scheduled_ = true;
   }
-  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
-    window->RemoveObserver(this);
+  virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
+    DCHECK_EQ(window_, window);
+    window_ = NULL;
   }
 
+  aura::Window* window_;
   bool is_paint_scheduled_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker);
@@ -272,7 +278,7 @@
 
   // Create a fake constrained window.
   scoped_ptr<aura::Window> w2(CreateWindowInPrimary());
-  w2->SetProperty(ash::kConstrainedWindowKey, true);
+  w2->SetProperty(aura::client::kConstrainedWindowKey, true);
   w2->Show();
 
   // Despite two windows, the first window should still be considered "solo"
diff --git a/ash/wm/sticky_keys.cc b/ash/wm/sticky_keys.cc
index efdfd16..7910a07 100644
--- a/ash/wm/sticky_keys.cc
+++ b/ash/wm/sticky_keys.cc
@@ -172,6 +172,7 @@
       current_state_(DISABLED),
       event_from_myself_(false),
       preparing_to_enable_(false),
+      scroll_delta_(0),
       delegate_(delegate) {
 }
 
@@ -223,11 +224,28 @@
   if (event_from_myself_ || current_state_ == DISABLED)
     return false;
   DCHECK(current_state_ == ENABLED || current_state_ == LOCKED);
-
   preparing_to_enable_ = false;
-  AppendModifier(event);
-  if (current_state_ == ENABLED) {
+
+  // We detect a direction change if the current |scroll_delta_| is assigned
+  // and the offset of the current scroll event has the opposing sign.
+  bool direction_changed = false;
+  if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) {
+    int offset = event->y_offset();
+    if (scroll_delta_)
+      direction_changed = offset * scroll_delta_ <= 0;
+    scroll_delta_ = offset;
+  }
+
+  if (!direction_changed)
+    AppendModifier(event);
+
+  // We want to modify all the scroll events in the scroll sequence, which ends
+  // with a fling start event. We also stop when the scroll sequence changes
+  // direction.
+  if (current_state_ == ENABLED &&
+      (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
     current_state_ = DISABLED;
+    scroll_delta_ = 0;
     DispatchEventAndReleaseModifier(event);
     return true;
   }
@@ -268,8 +286,9 @@
     case TARGET_MODIFIER_UP:
       if (preparing_to_enable_) {
         preparing_to_enable_ = false;
+        scroll_delta_ = 0;
         current_state_ = ENABLED;
-        modifier_up_event_.reset(event->Copy());
+        modifier_up_event_.reset(new ui::KeyEvent(*event));
         return true;
       }
       return false;
diff --git a/ash/wm/sticky_keys.h b/ash/wm/sticky_keys.h
index 7b8f6f1..1a27892 100644
--- a/ash/wm/sticky_keys.h
+++ b/ash/wm/sticky_keys.h
@@ -222,6 +222,11 @@
   // the ENABLED state.
   bool preparing_to_enable_;
 
+  // Tracks the scroll direction of the current scroll sequence. Sticky keys
+  // stops modifying the scroll events of the sequence when the direction
+  // changes. If no sequence is tracked, the value is 0.
+  int scroll_delta_;
+
   // The modifier up key event to be sent on non modifier key on ENABLED state.
   scoped_ptr<ui::KeyEvent> modifier_up_event_;
 
diff --git a/ash/wm/sticky_keys_unittest.cc b/ash/wm/sticky_keys_unittest.cc
index e8e8975..6f11347 100644
--- a/ash/wm/sticky_keys_unittest.cc
+++ b/ash/wm/sticky_keys_unittest.cc
@@ -44,7 +44,7 @@
  private:
   // ui::EventHandler overrides:
   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
-    events_.push_back(event->Copy());
+    events_.push_back(new ui::KeyEvent(*event));
   }
 
   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
@@ -92,7 +92,7 @@
       delegate_->OnShortcutPressed();
     }
 
-    events_.push_back(event->Copy());
+    events_.push_back(new ui::KeyEvent(*event));
   }
 
   virtual void DispatchMouseEvent(ui::MouseEvent* event,
@@ -203,6 +203,21 @@
     return event;
   }
 
+  ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
+                                            bool is_cancel) {
+    scoped_xevent_.InitFlingScrollEvent(
+        kScrollDeviceId, // deviceid
+        0,               // x_velocity
+        fling_delta,     // y_velocity
+        0,               // x_velocity_ordinal
+        fling_delta,     // y_velocity_ordinal
+        is_cancel);      // is_cancel
+    ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
+    ui::Event::DispatcherApi dispatcher(event);
+    dispatcher.set_target(target_);
+    return event;
+  }
+
   // Creates a synthesized KeyEvent that is not backed by a native event.
   ui::KeyEvent* GenerateSynthesizedKeyEvent(
       bool is_key_press, ui::KeyboardCode code) {
@@ -501,9 +516,9 @@
   EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state());
 }
 
-TEST_F(StickyKeysTest, ScrollEvents) {
+TEST_F(StickyKeysTest, ScrollEventOneshot) {
   ui::SetUpScrollDeviceForTest(kScrollDeviceId);
-  // Australlian scrolling is enabled by default for some reason.
+  // Disable Australlian scrolling.
   ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
 
   scoped_ptr<ui::ScrollEvent> ev;
@@ -518,20 +533,32 @@
 
     // Enable sticky keys.
     EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state());
-    kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
-    sticky_key.HandleKeyEvent(kev.get());
-    kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
-    sticky_key.HandleKeyEvent(kev.get());
+    SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
     EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
 
-    // Test scroll event is correctly modified.
-    ev.reset(GenerateScrollEvent(scroll_deltas[i]));
+    // Test a scroll sequence. Sticky keys should only be disabled at the end
+    // of the scroll sequence. Fling cancel event starts the scroll sequence.
+    ev.reset(GenerateFlingScrollEvent(0, true));
+    sticky_key.HandleScrollEvent(ev.get());
+    EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+    EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+
+    // Scrolls should all be modified but not disable sticky keys.
+    for (int j = 0; j < 3; ++j) {
+      ev.reset(GenerateScrollEvent(scroll_deltas[i]));
+      sticky_key.HandleScrollEvent(ev.get());
+      EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+      EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+    }
+
+    // Fling start event ends scroll sequence.
+    ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
     sticky_key.HandleScrollEvent(ev.get());
     EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
     EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state());
 
     ASSERT_EQ(2U, mock_delegate->GetEventCount());
-    EXPECT_EQ(ui::ET_SCROLL, mock_delegate->GetEvent(0)->type());
+    EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type());
     EXPECT_FLOAT_EQ(scroll_deltas[i],
                     static_cast<const ui::ScrollEvent*>(
                         mock_delegate->GetEvent(0))->y_offset());
@@ -540,23 +567,78 @@
               static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
                   ->key_code());
   }
+}
+
+TEST_F(StickyKeysTest, ScrollDirectionChanged) {
+  ui::SetUpScrollDeviceForTest(kScrollDeviceId);
+  // Disable Australlian scrolling.
+  ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
+
+  scoped_ptr<ui::ScrollEvent> ev;
+  scoped_ptr<ui::KeyEvent> kev;
+  MockStickyKeysHandlerDelegate* mock_delegate =
+      new MockStickyKeysHandlerDelegate(this);
+  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
+
+  // Test direction change with both boundary value and negative value.
+  const int direction_change_values[2] = {0, -10};
+  for (int i = 0; i < 2; ++i) {
+    SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
+    EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+
+    // Fling cancel starts scroll sequence.
+    ev.reset(GenerateFlingScrollEvent(0, true));
+    sticky_key.HandleScrollEvent(ev.get());
+    EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+
+    // Test that changing directions in a scroll sequence will
+    // return sticky keys to DISABLED state.
+    for (int j = 0; j < 3; ++j) {
+      ev.reset(GenerateScrollEvent(10));
+      sticky_key.HandleScrollEvent(ev.get());
+      EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+      EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+    }
+
+    ev.reset(GenerateScrollEvent(direction_change_values[i]));
+    sticky_key.HandleScrollEvent(ev.get());
+    EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
+    EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state());
+  }
+}
+
+TEST_F(StickyKeysTest, ScrollEventLocked) {
+  ui::SetUpScrollDeviceForTest(kScrollDeviceId);
+  // Disable Australlian scrolling.
+  ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
+
+  scoped_ptr<ui::ScrollEvent> ev;
+  scoped_ptr<ui::KeyEvent> kev;
+  MockStickyKeysHandlerDelegate* mock_delegate =
+      new MockStickyKeysHandlerDelegate(this);
+  StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
 
   // Lock sticky keys.
-  for (int i = 0; i < 2; ++i) {
-    kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
-    sticky_key.HandleKeyEvent(kev.get());
-    kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
-    sticky_key.HandleKeyEvent(kev.get());
-  }
+  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
+  SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
+  EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state());
 
   // Test scroll events are correctly modified in locked state.
   for (int i = 0; i < 5; ++i) {
+    // Fling cancel starts scroll sequence.
+    ev.reset(GenerateFlingScrollEvent(0, true));
+    sticky_key.HandleScrollEvent(ev.get());
+
     ev.reset(GenerateScrollEvent(10));
     sticky_key.HandleScrollEvent(ev.get());
     EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
     ev.reset(GenerateScrollEvent(-10));
     sticky_key.HandleScrollEvent(ev.get());
     EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+
+    // Fling start ends scroll sequence.
+    ev.reset(GenerateFlingScrollEvent(-10, false));
+    sticky_key.HandleScrollEvent(ev.get());
   }
 
   EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state());
diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc
index 328c9ca..aaf6634 100644
--- a/ash/wm/system_gesture_event_filter_unittest.cc
+++ b/ash/wm/system_gesture_event_filter_unittest.cc
@@ -15,7 +15,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/test/display_manager_test_api.h"
 #include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
 #include "ash/volume_control_delegate.h"
 #include "ash/wm/gestures/long_press_affordance_handler.h"
 #include "ash/wm/window_state.h"
diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc
index 99889d2..2c2cdeb 100644
--- a/ash/wm/system_modal_container_layout_manager.cc
+++ b/ash/wm/system_modal_container_layout_manager.cc
@@ -21,6 +21,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/event.h"
 #include "ui/gfx/screen.h"
+#include "ui/views/background.h"
 #include "ui/views/corewm/compound_event_filter.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc
index 13abd4a..894e11b 100644
--- a/ash/wm/window_animations.cc
+++ b/ash/wm/window_animations.cc
@@ -59,9 +59,6 @@
 // TODO(sky): if we end up sticking with 0, nuke the code doing the rotation.
 const float kWindowAnimation_MinimizeRotate = 0.f;
 
-// Tween type when cross fading a workspace window.
-const gfx::Tween::Type kCrossFadeTweenType = gfx::Tween::EASE_IN_OUT;
-
 // Scales for AshWindow above/below current workspace.
 const float kLayerScaleAboveSize = 1.1f;
 const float kLayerScaleBelowSize = .9f;
@@ -108,8 +105,8 @@
 
   rotation_about_pivot->SetReversed(show);
 
-  base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
-      kLayerAnimationsForMinimizeDurationMS);
+  base::TimeDelta duration = window->layer()->GetAnimator()->
+      GetTransitionDuration();
 
   scoped_ptr<ui::LayerAnimationElement> transition(
       ui::LayerAnimationElement::CreateInterpolatedTransformElement(
@@ -139,6 +136,10 @@
 void AnimateShowWindow_Minimize(aura::Window* window) {
   window->layer()->set_delegate(window);
   window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+  ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+  base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
+      kLayerAnimationsForMinimizeDurationMS);
+  settings.SetTransitionDuration(duration);
   AddLayerAnimationsForMinimize(window, true);
 
   // Now that the window has been restored, we need to clear its animation style
@@ -324,7 +325,8 @@
   const bool old_on_top = (old_bounds.width() > new_bounds.width());
 
   // Shorten the animation if there's not much visual movement.
-  const base::TimeDelta duration = GetCrossFadeDuration(old_bounds, new_bounds);
+  const base::TimeDelta duration = GetCrossFadeDuration(window,
+                                                        old_bounds, new_bounds);
 
   // Scale up the old layer while translating to new position.
   {
@@ -416,23 +418,10 @@
   CrossFadeImpl(window, old_layer, gfx::Tween::EASE_OUT);
 }
 
-void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace,
-                                      aura::Window* window,
-                                      ui::Layer* old_layer) {
-  ui::Layer* layer_parent = new_workspace->layer()->parent();
-  layer_parent->Add(old_layer);
-  const bool restoring = old_layer->bounds().width() > window->bounds().width();
-  if (restoring)
-    layer_parent->StackAbove(old_layer, new_workspace->layer());
-  else
-    layer_parent->StackBelow(old_layer, new_workspace->layer());
-
-  CrossFadeImpl(window, old_layer, kCrossFadeTweenType);
-}
-
-base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds,
+base::TimeDelta GetCrossFadeDuration(aura::Window* window,
+                                     const gfx::Rect& old_bounds,
                                      const gfx::Rect& new_bounds) {
-  if (views::corewm::WindowAnimationsDisabled(NULL))
+  if (views::corewm::WindowAnimationsDisabled(window))
     return base::TimeDelta();
 
   int old_area = old_bounds.width() * old_bounds.height();
diff --git a/ash/wm/window_animations.h b/ash/wm/window_animations.h
index b0e4a2e..f084bcc 100644
--- a/ash/wm/window_animations.h
+++ b/ash/wm/window_animations.h
@@ -44,17 +44,10 @@
 ASH_EXPORT void CrossFadeToBounds(aura::Window* window,
                                   const gfx::Rect& new_bounds);
 
-// Cross fades |layer| (which is a clone of |window|s layer before it was
-// resized) to |window|s current bounds. |new_workspace| is the Window of the
-// workspace |window| was added to.
-// This takes ownership of |layer|.
-ASH_EXPORT void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace,
-                                                 aura::Window* window,
-                                                 ui::Layer* layer);
-
 // Returns the duration of the cross-fade animation based on the |old_bounds|
-// and |new_bounds| of the window.
-ASH_EXPORT base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds,
+// and |new_bounds| of the |window|.
+ASH_EXPORT base::TimeDelta GetCrossFadeDuration(aura::Window* window,
+                                                const gfx::Rect& old_bounds,
                                                 const gfx::Rect& new_bounds);
 
 ASH_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window,
diff --git a/ash/wm/window_animations_unittest.cc b/ash/wm/window_animations_unittest.cc
index c2ea1f6..eeeb120 100644
--- a/ash/wm/window_animations_unittest.cc
+++ b/ash/wm/window_animations_unittest.cc
@@ -6,13 +6,16 @@
 
 #include "ash/shell_window_ids.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/window_state.h"
 #include "ash/wm/workspace_controller.h"
 #include "base/time/time.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/animation/animation_container_element.h"
 
 using aura::Window;
@@ -33,6 +36,37 @@
   DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest);
 };
 
+// Listens to animation scheduled notifications. Remembers the transition
+// duration of the first sequence.
+class MinimizeAnimationObserver : public ui::LayerAnimationObserver {
+ public:
+  explicit MinimizeAnimationObserver(ui::LayerAnimator* animator)
+      : animator_(animator) {
+    animator_->AddObserver(this);
+    // RemoveObserver is called when the first animation is scheduled and so
+    // there should be no need for now to remove it in destructor.
+  };
+  base::TimeDelta duration() { return duration_; }
+
+ protected:
+  // ui::LayerAnimationObserver:
+  virtual void OnLayerAnimationScheduled(
+      ui::LayerAnimationSequence* sequence) OVERRIDE {
+    duration_ = animator_->GetTransitionDuration();
+    animator_->RemoveObserver(this);
+  }
+  virtual void OnLayerAnimationEnded(
+      ui::LayerAnimationSequence* sequence) OVERRIDE {}
+  virtual void OnLayerAnimationAborted(
+      ui::LayerAnimationSequence* sequence) OVERRIDE {}
+
+ private:
+  ui::LayerAnimator* animator_;
+  base::TimeDelta duration_;
+
+  DISALLOW_COPY_AND_ASSIGN(MinimizeAnimationObserver);
+};
+
 TEST_F(WindowAnimationsTest, HideShowBrightnessGrayscaleAnimation) {
   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
   window->Show();
@@ -133,5 +167,77 @@
       Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
 }
 
+TEST_F(WindowAnimationsTest, LockAnimationDuration) {
+  ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+  scoped_ptr<Window> window(CreateTestWindowInShellWithId(0));
+  Layer* layer = window->layer();
+  window->SetBounds(gfx::Rect(5, 10, 320, 240));
+  window->Show();
+
+  // Test that it is possible to override transition duration when it is not
+  // locked.
+  {
+    ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator());
+    settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
+    {
+      ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator());
+      // Duration is not locked so it gets overridden.
+      settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
+      wm::GetWindowState(window.get())->Minimize();
+      EXPECT_TRUE(layer->GetAnimator()->is_animating());
+      // Expect duration from the inner scope
+      EXPECT_EQ(50,
+                layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
+    }
+    window->Show();
+    layer->GetAnimator()->StopAnimating();
+  }
+
+  // Test that it is possible to lock transition duration
+  {
+    ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator());
+    settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
+    // Duration is locked in outer scope.
+    settings1.LockTransitionDuration();
+    {
+      ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator());
+      // Transition duration setting is ignored.
+      settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
+      wm::GetWindowState(window.get())->Minimize();
+      EXPECT_TRUE(layer->GetAnimator()->is_animating());
+      // Expect duration from the outer scope
+      EXPECT_EQ(1000,
+                layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
+    }
+    window->Show();
+    layer->GetAnimator()->StopAnimating();
+  }
+
+  // Test that duration respects default.
+  {
+    // Query default duration.
+    MinimizeAnimationObserver observer(layer->GetAnimator());
+    wm::GetWindowState(window.get())->Minimize();
+    EXPECT_TRUE(layer->GetAnimator()->is_animating());
+    base::TimeDelta default_duration(observer.duration());
+    window->Show();
+    layer->GetAnimator()->StopAnimating();
+
+    ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
+    settings.LockTransitionDuration();
+    // Setting transition duration is ignored since duration is locked
+    settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
+    wm::GetWindowState(window.get())->Minimize();
+    EXPECT_TRUE(layer->GetAnimator()->is_animating());
+    // Expect default duration (200ms for stock ash minimizing animation).
+    EXPECT_EQ(default_duration.InMilliseconds(),
+              layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
+    window->Show();
+    layer->GetAnimator()->StopAnimating();
+  }
+}
+
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
index 428804f..d6bf24a 100644
--- a/ash/wm/window_cycle_list.cc
+++ b/ash/wm/window_cycle_list.cc
@@ -72,8 +72,6 @@
 }
 
 void WindowCycleList::OnWindowDestroyed(aura::Window* window) {
-  window->RemoveObserver(this);
-
   WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
   DCHECK(i != windows_.end());
   int removed_index = static_cast<int>(i - windows_.begin());
diff --git a/ash/wm/window_positioner.cc b/ash/wm/window_positioner.cc
index 48e70a2..9b31113 100644
--- a/ash/wm/window_positioner.cc
+++ b/ash/wm/window_positioner.cc
@@ -58,8 +58,7 @@
   if (disable_auto_positioning)
     return false;
   const wm::WindowState* window_state = wm::GetWindowState(window);
-  return window_state->tracked_by_workspace() &&
-      window_state->window_position_managed();
+  return !window_state->is_dragged() && window_state->window_position_managed();
 }
 
 // Check if a given |window| can be managed. This includes that it's state is
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 1a06a58..4bc1484 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/window_state.h"
 
+#include "ash/ash_switches.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_ash.h"
 #include "ash/shell_window_ids.h"
@@ -12,6 +13,8 @@
 #include "ash/wm/window_state_observer.h"
 #include "ash/wm/window_util.h"
 #include "ash/wm/wm_types.h"
+#include "base/auto_reset.h"
+#include "base/command_line.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
@@ -29,7 +32,6 @@
 
 WindowState::WindowState(aura::Window* window)
     : window_(window),
-      tracked_by_workspace_(true),
       window_position_managed_(false),
       bounds_changed_by_user_(false),
       panel_attached_(true),
@@ -42,13 +44,28 @@
       hide_shelf_when_fullscreen_(true),
       animate_to_fullscreen_(true),
       minimum_visibility_(false),
+      in_set_window_show_type_(false),
       window_show_type_(ToWindowShowType(GetShowState())) {
   window_->AddObserver(this);
+
+#if defined(OS_CHROMEOS)
+  // NOTE(pkotwicz): Animating to immersive fullscreen does not look good. When
+  // the kAshEnableImmersiveFullscreenForAllWindows flag is set most windows
+  // can be put into immersive fullscreen. It is not worth the added complexity
+  // to only animate to fullscreen if the window is put into immersive
+  // fullscreen.
+  animate_to_fullscreen_ = !CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kAshEnableImmersiveFullscreenForAllWindows);
+#endif
 }
 
 WindowState::~WindowState() {
 }
 
+bool WindowState::HasDelegate() const {
+  return delegate_;
+}
+
 void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) {
   DCHECK(!delegate_.get());
   delegate_ = delegate.Pass();
@@ -88,6 +105,11 @@
       window_->parent()->id() == internal::kShellWindowId_DockedContainer;
 }
 
+bool WindowState::IsSnapped() const {
+  return window_show_type_ == SHOW_TYPE_LEFT_SNAPPED ||
+      window_show_type_ == SHOW_TYPE_RIGHT_SNAPPED;
+}
+
 bool WindowState::CanMaximize() const {
   return window_->GetProperty(aura::client::kCanMaximizeKey);
 }
@@ -136,7 +158,7 @@
 }
 
 void WindowState::SnapRight(const gfx::Rect& bounds) {
-  SnapWindow(SHOW_TYPE_LEFT_SNAPPED, bounds);
+  SnapWindow(SHOW_TYPE_RIGHT_SNAPPED, bounds);
 }
 
 void WindowState::Minimize() {
@@ -235,54 +257,67 @@
   observer_list_.RemoveObserver(observer);
 }
 
-void WindowState::SetTrackedByWorkspace(bool tracked_by_workspace) {
-  if (tracked_by_workspace_ == tracked_by_workspace)
-    return;
-  bool old = tracked_by_workspace_;
-  tracked_by_workspace_ = tracked_by_workspace;
-  FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
-                    OnTrackedByWorkspaceChanged(this, old));
-}
-
 void WindowState::OnWindowPropertyChanged(aura::Window* window,
                                           const void* key,
                                           intptr_t old) {
   DCHECK_EQ(window, window_);
-  if (key == aura::client::kShowStateKey) {
-    window_show_type_ = ToWindowShowType(GetShowState());
-    ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old);
-    // TODO(oshima): Notify only when the state has changed.
-    // Doing so break a few tests now.
-    FOR_EACH_OBSERVER(
-        WindowStateObserver, observer_list_,
-        OnWindowShowTypeChanged(this, ToWindowShowType(old_state)));
-  }
-}
-
-void WindowState::OnWindowDestroying(aura::Window* window) {
-  window_->RemoveObserver(this);
+  if (key == aura::client::kShowStateKey)
+    SetWindowShowType(ToWindowShowType(GetShowState()));
 }
 
 void WindowState::SnapWindow(WindowShowType left_or_right,
                              const gfx::Rect& bounds) {
-  if (IsMaximizedOrFullscreen()) {
-    // Before we can set the bounds we need to restore the window.
-    // Restoring the window will set the window to its restored bounds.
-    // To avoid an unnecessary bounds changes (which may have side effects)
-    // we set the restore bounds to the bounds we want, restore the window,
-    // then reset the restore bounds. This way no unnecessary bounds
-    // changes occurs and the original restore bounds is remembered.
-    gfx::Rect restore_bounds_in_screen =
-        GetRestoreBoundsInScreen();
-    SetRestoreBoundsInParent(bounds);
-    Restore();
-    SetRestoreBoundsInScreen(restore_bounds_in_screen);
-  } else {
+  if (window_show_type_ == left_or_right) {
     window_->SetBounds(bounds);
+    return;
   }
+
+  // Compute the bounds that the window will restore to. If the window does not
+  // already have restore bounds, it will be restored (when un-snapped) to the
+  // last bounds that it had before getting snapped.
+  gfx::Rect restore_bounds_in_screen(HasRestoreBounds() ?
+      GetRestoreBoundsInScreen() : window_->GetBoundsInScreen());
+  // Set the window's restore bounds so that WorkspaceLayoutManager knows
+  // which width to use when the snapped window is moved to the edge.
+  SetRestoreBoundsInParent(bounds);
+
+  bool was_maximized = IsMaximizedOrFullscreen();
+  // Before we can set the bounds we need to restore the window.
+  // Restoring the window will set the window to its restored bounds set above.
+  // Restore will cause OnWindowPropertyChanged() so it needs to be done
+  // before notifying that the WindowShowType has changed to |left_or_right|.
+  if (was_maximized)
+    Restore();
   DCHECK(left_or_right == SHOW_TYPE_LEFT_SNAPPED ||
          left_or_right == SHOW_TYPE_RIGHT_SNAPPED);
-  window_show_type_ = left_or_right;
+  SetWindowShowType(left_or_right);
+  // TODO(varkha): Ideally the bounds should be changed in a LayoutManager upon
+  // observing the WindowShowType change.
+  // If the window is a child of kShellWindowId_DockedContainer such as during
+  // a drag, the window's bounds are not set in
+  // WorkspaceLayoutManager::OnWindowShowTypeChanged(). Set them here. Skip
+  // setting the bounds otherwise to avoid stopping the slide animation which
+  // was started as a result of OnWindowShowTypeChanged().
+  if (IsDocked())
+    window_->SetBounds(bounds);
+  SetRestoreBoundsInScreen(restore_bounds_in_screen);
+}
+
+void WindowState::SetWindowShowType(WindowShowType new_window_show_type) {
+  if (in_set_window_show_type_)
+    return;
+  base::AutoReset<bool> resetter(&in_set_window_show_type_, true);
+
+  ui::WindowShowState new_window_state =
+      ToWindowShowState(new_window_show_type);
+  if (new_window_state != GetShowState())
+    window_->SetProperty(aura::client::kShowStateKey, new_window_state);
+  WindowShowType old_window_show_type = window_show_type_;
+  window_show_type_ = new_window_show_type;
+  if (old_window_show_type != window_show_type_) {
+    FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
+                      OnWindowShowTypeChanged(this, old_window_show_type));
+  }
 }
 
 WindowState* GetActiveWindowState() {
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index 2bbdd03..618349c 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -49,6 +49,7 @@
   aura::Window* window() { return window_; }
   const aura::Window* window() const { return window_; }
 
+  bool HasDelegate() const;
   void SetDelegate(scoped_ptr<WindowStateDelegate> delegate);
 
   // Returns the window's current show state.
@@ -69,6 +70,7 @@
   bool IsNormalShowState() const;
   bool IsActive() const;
   bool IsDocked() const;
+  bool IsSnapped() const;
 
   // Checks if the window can change its state accordingly.
   bool CanMaximize() const;
@@ -172,12 +174,8 @@
   void AddObserver(WindowStateObserver* observer);
   void RemoveObserver(WindowStateObserver* observer);
 
-  // Whether the window is tracked by workspace code. Default is
-  // true. If set to false the workspace does not switch the current
-  // workspace, nor does it attempt to impose constraints on the
-  // bounds of the window. This is intended for tab dragging.
-  bool tracked_by_workspace() const { return tracked_by_workspace_; }
-  void SetTrackedByWorkspace(bool tracked_by_workspace);
+  // Whether the window is being dragged.
+  bool is_dragged() const { return !!window_resizer_; }
 
   // Whether or not the window's position can be managed by the
   // auto management logic.
@@ -250,18 +248,19 @@
   virtual void OnWindowPropertyChanged(aura::Window* window,
                                        const void* key,
                                        intptr_t old) OVERRIDE;
-  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
 
  private:
   // Snaps the window to left or right of the desktop with given bounds.
   void SnapWindow(WindowShowType left_or_right,
                   const gfx::Rect& bounds);
 
+  // Sets the window show type and updates the show state if necessary.
+  void SetWindowShowType(WindowShowType new_window_show_type);
+
   // The owner of this window settings.
   aura::Window* window_;
   scoped_ptr<WindowStateDelegate> delegate_;
 
-  bool tracked_by_workspace_;
   bool window_position_managed_;
   bool bounds_changed_by_user_;
   bool panel_attached_;
@@ -283,6 +282,9 @@
 
   ObserverList<WindowStateObserver> observer_list_;
 
+  // True when in SetWindowShowType(). This is used to avoid reentrance.
+  bool in_set_window_show_type_;
+
   WindowShowType window_show_type_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowState);
diff --git a/ash/wm/window_state_observer.h b/ash/wm/window_state_observer.h
index 75008a8..f177d5f 100644
--- a/ash/wm/window_state_observer.h
+++ b/ash/wm/window_state_observer.h
@@ -14,11 +14,6 @@
 
 class ASH_EXPORT WindowStateObserver {
  public:
-  // Called when the tracked_by_workspace has changed.
-  virtual void OnTrackedByWorkspaceChanged(
-      WindowState* window,
-      bool old_value) {}
-
   // Called when the window's show type has changed. This is different from
   // kWindowShowStatekey property change as this will be invoked when the window
   // gets left/right maximized, and auto positioned. |old_type| is the value
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 8d320a9..def4b6d 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "ash/ash_constants.h"
+#include "ash/screen_ash.h"
 #include "ash/shell.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_state.h"
@@ -62,12 +63,19 @@
   const gfx::Display display =
       Shell::GetScreen()->GetDisplayNearestWindow(window);
   gfx::Rect center = display.work_area();
-  gfx::Size size = window_state->HasRestoreBounds() ?
-      window_state->GetRestoreBoundsInScreen().size() :
-      window->bounds().size();
-  center.ClampToCenteredSize(size);
-  window_state->SetRestoreBoundsInScreen(center);
-  window_state->Restore();
+  gfx::Size size = window->bounds().size();
+  if (window_state->IsSnapped()) {
+    if (window_state->HasRestoreBounds())
+      size = window_state->GetRestoreBoundsInScreen().size();
+    center.ClampToCenteredSize(size);
+    window_state->SetRestoreBoundsInScreen(center);
+    window_state->Restore();
+  } else {
+    center = ScreenAsh::ConvertRectFromScreen(window->parent(),
+        center);
+    center.ClampToCenteredSize(size);
+    window->SetBounds(center);
+  }
 }
 
 void AdjustBoundsToEnsureMinimumWindowVisibility(const gfx::Rect& visible_area,
@@ -115,16 +123,22 @@
   return true;
 }
 
-void ReparentChildWithTransientChildren(aura::Window* window,
-                                        aura::Window* child) {
-  window->AddChild(child);
-  ReparentTransientChildrenOfChild(window, child);
+void ReparentChildWithTransientChildren(aura::Window* child,
+                                        aura::Window* old_parent,
+                                        aura::Window* new_parent) {
+  if (child->parent() == old_parent)
+    new_parent->AddChild(child);
+  ReparentTransientChildrenOfChild(child, old_parent, new_parent);
 }
 
-void ReparentTransientChildrenOfChild(aura::Window* window,
-                                      aura::Window* child) {
-  for (size_t i = 0; i < child->transient_children().size(); ++i)
-    ReparentChildWithTransientChildren(window, child->transient_children()[i]);
+void ReparentTransientChildrenOfChild(aura::Window* child,
+                                      aura::Window* old_parent,
+                                      aura::Window* new_parent) {
+  for (size_t i = 0; i < child->transient_children().size(); ++i) {
+    ReparentChildWithTransientChildren(child->transient_children()[i],
+                                       old_parent,
+                                       new_parent);
+  }
 }
 
 }  // namespace wm
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index fb12959..fd77505 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -68,13 +68,18 @@
 ASH_EXPORT bool MoveWindowToEventRoot(aura::Window* window,
                                       const ui::Event& event);
 
-// Adds |child| and all its transient children to |window|.
-void ReparentChildWithTransientChildren(aura::Window* window,
-                                        aura::Window* child);
+// Changes the parent of a |child| and all its transient children that are
+// themselves children of |old_parent| to |new_parent|.
+void ReparentChildWithTransientChildren(aura::Window* child,
+                                        aura::Window* old_parent,
+                                        aura::Window* new_parent);
 
-// Changes the parent of all transient children of a |child| to |window|.
-void ReparentTransientChildrenOfChild(aura::Window* window,
-                                       aura::Window* child);
+// Changes the parent of all transient children of a |child| to |new_parent|.
+// Does not change parent of the transient children that are not themselves
+// children of |old_parent|.
+void ReparentTransientChildrenOfChild(aura::Window* child,
+                                      aura::Window* old_parent,
+                                      aura::Window* new_parent);
 
 }  // namespace wm
 }  // namespace ash
diff --git a/ash/wm/workspace/colored_window_controller.cc b/ash/wm/workspace/colored_window_controller.cc
deleted file mode 100644
index bffff54..0000000
--- a/ash/wm/workspace/colored_window_controller.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "ash/wm/workspace/colored_window_controller.h"
-
-#include "ash/shell_window_ids.h"
-#include "ash/wm/window_state.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/root_window.h"
-#include "ui/gfx/canvas.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
-
-namespace ash {
-namespace internal {
-
-// View implementation responsible for rendering the background.
-class ColoredWindowController::View : public views::WidgetDelegateView {
- public:
-  explicit View(ColoredWindowController* controller);
-  virtual ~View();
-
-  // Closes the hosting widget.
-  void Close();
-
-  // WidgetDelegate overrides:
-  virtual views::View* GetContentsView() OVERRIDE;
-
- private:
-  ColoredWindowController* controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(View);
-};
-
-ColoredWindowController::View::View(ColoredWindowController* controller)
-    : controller_(controller) {
-}
-
-ColoredWindowController::View::~View() {
-  if (controller_)
-    controller_->view_ = NULL;
-}
-
-void ColoredWindowController::View::Close() {
-  controller_ = NULL;
-  GetWidget()->Close();
-}
-
-views::View* ColoredWindowController::View::GetContentsView() {
-  return this;
-}
-
-ColoredWindowController::ColoredWindowController(aura::Window* parent,
-                                                 const std::string& window_name)
-    : view_(new View(this)) {
-  views::Widget* widget = new views::Widget;
-  views::Widget::InitParams params(
-      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.delegate = view_;
-  params.parent = parent;
-  params.can_activate = false;
-  params.accept_events = false;
-  params.layer_type = ui::LAYER_SOLID_COLOR;
-  widget->Init(params);
-  // Do this so the parent doesn't attempt to enforce any bounds constraints on
-  // us.
-  wm::GetWindowState(widget->GetNativeView())->SetTrackedByWorkspace(false);
-  widget->GetNativeView()->SetProperty(aura::client::kAnimationsDisabledKey,
-                                       true);
-  widget->GetNativeView()->SetName(window_name);
-  // The bounds should match the parent exactly. We don't go through
-  // Widget::SetBounds() as that may try to place on a different display.
-  widget->GetNativeWindow()->SetBounds(gfx::Rect(parent->bounds()));
-}
-
-ColoredWindowController::~ColoredWindowController() {
-  if (view_)
-    view_->Close();
-}
-
-void ColoredWindowController::SetColor(SkColor color) {
-  if (view_)
-    view_->GetWidget()->GetNativeView()->layer()->SetColor(color);
-}
-
-views::Widget* ColoredWindowController::GetWidget() {
-  return view_ ? view_->GetWidget() : NULL;
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/wm/workspace/colored_window_controller.h b/ash/wm/workspace/colored_window_controller.h
deleted file mode 100644
index d70a83b..0000000
--- a/ash/wm/workspace/colored_window_controller.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2012 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 ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_
-#define ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_
-
-#include <string>
-
-#include "ash/ash_export.h"
-#include "base/basictypes.h"
-
-typedef unsigned int SkColor;
-
-namespace aura {
-class Window;
-}
-
-namespace views {
-class Widget;
-}
-
-namespace ash {
-namespace internal {
-
-// ColoredWindowController creates a Widget whose layer is LAYER_SOLID_COLOR.
-// The Widget is sized to the supplied Window and parented to the specified
-// Window. It is used for animations.
-class ASH_EXPORT ColoredWindowController {
- public:
-  ColoredWindowController(aura::Window* parent, const std::string& window_name);
-  ~ColoredWindowController();
-
-  // Changes the background color.
-  void SetColor(SkColor color);
-
-  views::Widget* GetWidget();
-
- private:
-  class View;
-
-  // View responsible for rendering the background. This is non-NULL if the
-  // widget containing it is deleted. It is owned by the widget.
-  View* view_;
-
-  DISALLOW_COPY_AND_ASSIGN(ColoredWindowController);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_
diff --git a/ash/wm/workspace/desktop_background_fade_controller.cc b/ash/wm/workspace/desktop_background_fade_controller.cc
deleted file mode 100644
index abbdf57..0000000
--- a/ash/wm/workspace/desktop_background_fade_controller.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2012 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.
-
-#include "ash/wm/workspace/desktop_background_fade_controller.h"
-
-#include "ash/wm/window_animations.h"
-#include "ash/wm/workspace/colored_window_controller.h"
-#include "base/time/time.h"
-#include "ui/aura/window.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-namespace internal {
-
-DesktopBackgroundFadeController::DesktopBackgroundFadeController(
-    aura::Window* parent,
-    aura::Window* position_above,
-    base::TimeDelta duration,
-    Direction direction) {
-  SkColor start_color, target_color;
-  gfx::Tween::Type tween_type;
-  if (direction == FADE_OUT) {
-    start_color = SkColorSetARGB(0, 0, 0, 0);
-    target_color = SK_ColorBLACK;
-    tween_type = gfx::Tween::EASE_IN_OUT;
-  } else {
-    start_color = SK_ColorBLACK;
-    target_color = SkColorSetARGB(0, 0, 0, 0);
-    tween_type = gfx::Tween::EASE_IN_OUT;
-  }
-
-  window_controller_.reset(
-      new ColoredWindowController(parent, "DesktopFade"));
-
-  // Force the window to be directly on top of the desktop.
-  aura::Window* fade_window = window_controller_->GetWidget()->GetNativeView();
-  parent->StackChildBelow(fade_window, position_above);
-  parent->StackChildAbove(fade_window, position_above);
-  window_controller_->SetColor(start_color);
-  views::corewm::SetWindowVisibilityAnimationTransition(
-      window_controller_->GetWidget()->GetNativeView(),
-      views::corewm::ANIMATE_NONE);
-  window_controller_->GetWidget()->Show();
-  {
-    ui::ScopedLayerAnimationSettings scoped_setter(
-        fade_window->layer()->GetAnimator());
-    scoped_setter.AddObserver(this);
-    scoped_setter.SetTweenType(tween_type);
-    scoped_setter.SetTransitionDuration(duration);
-    window_controller_->SetColor(target_color);
-  }
-}
-
-DesktopBackgroundFadeController::~DesktopBackgroundFadeController() {
-  StopObservingImplicitAnimations();
-}
-
-void DesktopBackgroundFadeController::OnImplicitAnimationsCompleted() {
-  window_controller_.reset();
-}
-
-}  // namespace internal
-}  // namespace ash
diff --git a/ash/wm/workspace/desktop_background_fade_controller.h b/ash/wm/workspace/desktop_background_fade_controller.h
deleted file mode 100644
index 3cc0137..0000000
--- a/ash/wm/workspace/desktop_background_fade_controller.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (c) 2012 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 ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_
-#define ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_
-
-#include "ash/ash_export.h"
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "ui/compositor/layer_animation_observer.h"
-
-namespace aura {
-class Window;
-}
-
-namespace base {
-class TimeDelta;
-}
-
-namespace ash {
-namespace internal {
-
-class ColoredWindowController;
-
-// DesktopBackgroundFadeController handles fading in or out the desktop. It is
-// used when maximizing or restoring a window. It is implemented as a colored
-// layer whose opacity varies. This results in fading in or out all the windows
-// the DesktopBackgroundFadeController is placed on top of. This is used
-// instead of varying the opacity for two reasons:
-// . The window showing background and the desktop workspace do not have a
-//   common parent that can be animated. This could be fixed, but wouldn't
-//   address the following.
-// . When restoring the window is moved back to the desktop workspace. If we
-//   animated the opacity of the desktop workspace the cross fade would be
-//   effected.
-class ASH_EXPORT DesktopBackgroundFadeController
-    : public ui::ImplicitAnimationObserver {
- public:
-  // Direction to fade.
-  enum Direction {
-    FADE_IN,
-    FADE_OUT,
-  };
-
-  // Creates a new DesktopBackgroundFadeController. |parent| is the Window to
-//   parent the newly created window to. The newly created window is stacked
-//   directly on top of |position_above|. The window animating the fade is
-//   destroyed as soon as the animation completes.
-  DesktopBackgroundFadeController(aura::Window* parent,
-                                  aura::Window* position_above,
-                                  base::TimeDelta duration,
-                                  Direction direction);
-  virtual ~DesktopBackgroundFadeController();
-
- private:
-  // ImplicitAnimationObserver overrides:
-  virtual void OnImplicitAnimationsCompleted() OVERRIDE;
-
-  scoped_ptr<ColoredWindowController> window_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopBackgroundFadeController);
-};
-
-}  // namespace internal
-}  // namespace ash
-
-#endif  // ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_
diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc
index 0e69c85..83a0e3f 100644
--- a/ash/wm/workspace/phantom_window_controller.cc
+++ b/ash/wm/workspace/phantom_window_controller.cc
@@ -15,11 +15,11 @@
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/views/background.h"
 #include "ui/views/painter.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
-
 namespace ash {
 namespace internal {
 
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index a0ae8cc..fe7d748 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -20,6 +20,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/ui_base_types.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/event.h"
 #include "ui/views/corewm/window_util.h"
 
@@ -71,7 +72,9 @@
       shelf_(NULL),
       window_(window),
       work_area_in_parent_(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
-          window->parent())) {
+          window->parent())),
+      is_fullscreen_(GetRootWindowController(
+          window->GetRootWindow())->GetWindowForFullscreenMode() != NULL) {
 }
 
 WorkspaceLayoutManager::~WorkspaceLayoutManager() {
@@ -84,7 +87,8 @@
 void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
   AdjustWindowBoundsWhenAdded(wm::GetWindowState(child));
   BaseLayoutManager::OnWindowAddedToLayout(child);
-  UpdateDesktopVisibility();
+  UpdateShelfVisibility();
+  UpdateFullscreenState();
   WindowPositioner::RearrangeVisibleWindowOnShow(child);
 }
 
@@ -96,7 +100,8 @@
 
 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
   BaseLayoutManager::OnWindowRemovedFromLayout(child);
-  UpdateDesktopVisibility();
+  UpdateShelfVisibility();
+  UpdateFullscreenState();
 }
 
 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
@@ -105,34 +110,32 @@
   if (child->TargetVisibility()) {
     WindowPositioner::RearrangeVisibleWindowOnShow(child);
   } else {
-    if (wm::GetWindowState(child)->IsFullscreen()) {
-      ash::Shell::GetInstance()->NotifyFullscreenStateChange(
-          false, child->GetRootWindow());
-    }
+    if (wm::GetWindowState(child)->IsFullscreen())
+      UpdateFullscreenState();
     WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
   }
-  UpdateDesktopVisibility();
+  UpdateShelfVisibility();
 }
 
 void WorkspaceLayoutManager::SetChildBounds(
     Window* child,
     const gfx::Rect& requested_bounds) {
-  if (!wm::GetWindowState(child)->tracked_by_workspace()) {
+  wm::WindowState* window_state = wm::GetWindowState(child);
+  if (window_state->is_dragged()) {
     SetChildBoundsDirect(child, requested_bounds);
-    return;
-  }
-  gfx::Rect child_bounds(requested_bounds);
-  // Some windows rely on this to set their initial bounds.
-  if (!SetMaximizedOrFullscreenBounds(wm::GetWindowState(child))) {
+  } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
+    // Some windows rely on this to set their initial bounds.
     // Non-maximized/full-screen windows have their size constrained to the
     // work-area.
+    gfx::Rect child_bounds(requested_bounds);
     child_bounds.set_width(std::min(work_area_in_parent_.width(),
                                     child_bounds.width()));
-    child_bounds.set_height(
-        std::min(work_area_in_parent_.height(), child_bounds.height()));
+    child_bounds.set_height(std::min(work_area_in_parent_.height(),
+                                     child_bounds.height()));
+    AdjustSnappedBounds(window_state, &child_bounds);
     SetChildBoundsDirect(child, child_bounds);
   }
-  UpdateDesktopVisibility();
+  UpdateShelfVisibility();
 }
 
 void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
@@ -154,11 +157,10 @@
   }
 }
 
-void WorkspaceLayoutManager::OnTrackedByWorkspaceChanged(
-    wm::WindowState* window_state,
-    bool old){
-  if (window_state->tracked_by_workspace())
-    SetMaximizedOrFullscreenBounds(window_state);
+void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) {
+  BaseLayoutManager::OnWindowStackingChanged(window);
+  UpdateShelfVisibility();
+  UpdateFullscreenState();
 }
 
 void WorkspaceLayoutManager::OnWindowShowTypeChanged(
@@ -189,9 +191,7 @@
   if (old_state != new_state &&
       (new_state == ui::SHOW_STATE_FULLSCREEN ||
        old_state == ui::SHOW_STATE_FULLSCREEN)) {
-    ash::Shell::GetInstance()->NotifyFullscreenStateChange(
-        new_state == ui::SHOW_STATE_FULLSCREEN,
-        window_state->window()->GetRootWindow());
+    UpdateFullscreenState();
   }
 
   UpdateBoundsFromShowState(window_state, old_state);
@@ -206,7 +206,7 @@
     wm::WindowState* state,
     ui::WindowShowState last_show_state) {
   BaseLayoutManager::ShowStateChanged(state, last_show_state);
-  UpdateDesktopVisibility();
+  UpdateShelfVisibility();
 }
 
 void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
@@ -219,7 +219,7 @@
 void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange(
     wm::WindowState* window_state,
     AdjustWindowReason reason) {
-  if (!window_state->tracked_by_workspace())
+  if (window_state->is_dragged())
     return;
 
   // Do not cross fade here: the window's layer hierarchy may be messed up for
@@ -249,8 +249,9 @@
           work_area_in_parent_, &bounds);
       break;
   }
+  AdjustSnappedBounds(window_state, &bounds);
   if (window_state->window()->bounds() != bounds)
-    window_state->window()->SetBounds(bounds);
+    SetChildBoundsAnimated(window_state->window(), bounds);
 }
 
 void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded(
@@ -263,7 +264,7 @@
   if (window_state->window()->bounds().IsEmpty())
     return;
 
-  if (!window_state->tracked_by_workspace())
+  if (window_state->is_dragged())
     return;
 
   if (SetMaximizedOrFullscreenBounds(window_state))
@@ -281,21 +282,33 @@
 
   ash::wm::AdjustBoundsToEnsureWindowVisibility(
       display_area, min_width, min_height, &bounds);
+  AdjustSnappedBounds(window_state, &bounds);
   if (window->bounds() != bounds)
     window->SetBounds(bounds);
 }
 
-void WorkspaceLayoutManager::UpdateDesktopVisibility() {
+void WorkspaceLayoutManager::UpdateShelfVisibility() {
   if (shelf_)
     shelf_->UpdateVisibilityState();
 }
 
+void WorkspaceLayoutManager::UpdateFullscreenState() {
+  bool is_fullscreen = GetRootWindowController(
+      window_->GetRootWindow())->GetWindowForFullscreenMode() != NULL;
+  if (is_fullscreen != is_fullscreen_) {
+    ash::Shell::GetInstance()->NotifyFullscreenStateChange(
+        is_fullscreen, window_->GetRootWindow());
+    is_fullscreen_ = is_fullscreen;
+  }
+}
+
 void WorkspaceLayoutManager::UpdateBoundsFromShowState(
     wm::WindowState* window_state,
     ui::WindowShowState last_show_state) {
   aura::Window* window = window_state->window();
   // See comment in SetMaximizedOrFullscreenBounds() as to why we use parent in
   // these calculation.
+  // TODO(varkha): Change the switch statement below to use wm::WindowShowType.
   switch (window_state->GetShowState()) {
     case ui::SHOW_STATE_DEFAULT:
     case ui::SHOW_STATE_NORMAL: {
@@ -317,13 +330,20 @@
           bounds_in_parent.SetRect(0, 0, 0, 0);
       }
       if (!bounds_in_parent.IsEmpty()) {
-        gfx::Rect new_bounds = BaseLayoutManager::BoundsWithScreenEdgeVisible(
-            window->parent()->parent(),
-            bounds_in_parent);
-        if (last_show_state == ui::SHOW_STATE_MINIMIZED)
-          SetChildBoundsDirect(window, new_bounds);
-        else
-          CrossFadeToBounds(window, new_bounds);
+        if ((last_show_state == ui::SHOW_STATE_DEFAULT ||
+             last_show_state == ui::SHOW_STATE_NORMAL) &&
+             window_state->IsSnapped()) {
+          AdjustSnappedBounds(window_state, &bounds_in_parent);
+          SetChildBoundsAnimated(window, bounds_in_parent);
+        } else {
+          gfx::Rect new_bounds = BaseLayoutManager::BoundsWithScreenEdgeVisible(
+              window->parent()->parent(),
+              bounds_in_parent);
+          if (last_show_state == ui::SHOW_STATE_MINIMIZED)
+            SetChildBoundsDirect(window, new_bounds);
+          else
+            CrossFadeToBounds(window, new_bounds);
+        }
       }
       window_state->ClearRestoreBounds();
       break;
@@ -363,8 +383,7 @@
 
 bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
     wm::WindowState* window_state) {
-  if (!window_state->tracked_by_workspace())
-    return false;
+  DCHECK(!window_state->is_dragged());
 
   // During animations there is a transform installed on the workspace
   // windows. For this reason this code uses the parent so that the transform is
@@ -385,5 +404,33 @@
   return false;
 }
 
+void WorkspaceLayoutManager::AdjustSnappedBounds(wm::WindowState* window_state,
+                                                 gfx::Rect* bounds) {
+  if (window_state->is_dragged() || !window_state->IsSnapped())
+    return;
+  gfx::Rect maximized_bounds = ScreenAsh::GetMaximizedWindowBoundsInParent(
+      window_state->window()->parent()->parent());
+  if (window_state->window_show_type() == wm::SHOW_TYPE_LEFT_SNAPPED)
+    bounds->set_x(maximized_bounds.x());
+  else if (window_state->window_show_type() == wm::SHOW_TYPE_RIGHT_SNAPPED)
+    bounds->set_x(maximized_bounds.right() - bounds->width());
+  bounds->set_y(maximized_bounds.y());
+  // TODO(varkha): Set width to 50% here for snapped windows.
+  bounds->set_height(maximized_bounds.height());
+}
+
+void WorkspaceLayoutManager::SetChildBoundsAnimated(Window* child,
+                                                    const gfx::Rect& bounds) {
+  const int kBoundsChangeSlideDurationMs = 120;
+
+  ui::Layer* layer = child->layer();
+  ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
+  slide_settings.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  slide_settings.SetTransitionDuration(
+      base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs));
+  SetChildBoundsDirect(child, bounds);
+}
+
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h
index 30a88f2..ab4876c 100644
--- a/ash/wm/workspace/workspace_layout_manager.h
+++ b/ash/wm/workspace/workspace_layout_manager.h
@@ -55,10 +55,7 @@
   virtual void OnWindowPropertyChanged(aura::Window* window,
                                        const void* key,
                                        intptr_t old) OVERRIDE;
-
-  // ash::WindowSettings::Observer overrides:
-  virtual void OnTrackedByWorkspaceChanged(wm::WindowState* window_state,
-                                           bool old) OVERRIDE;
+  virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
 
   // WindowStateObserver overrides:
   virtual void OnWindowShowTypeChanged(wm::WindowState* window_state,
@@ -76,7 +73,12 @@
 
   void AdjustWindowBoundsWhenAdded(wm::WindowState* window_state);
 
-  void UpdateDesktopVisibility();
+  // Updates the visibility state of the shelf.
+  void UpdateShelfVisibility();
+
+  // Updates the fullscreen state of the workspace and notifies Shell if it
+  // has changed.
+  void UpdateFullscreenState();
 
   // Updates the bounds of the window for a show state change from
   // |last_show_state|.
@@ -87,6 +89,13 @@
   // window are set and true is returned. Does nothing otherwise.
   bool SetMaximizedOrFullscreenBounds(wm::WindowState* window_state);
 
+  // Adjusts the |bounds| so that they are flush with the edge of the
+  // workspace if the window represented by |window_state| is side snapped.
+  void AdjustSnappedBounds(wm::WindowState* window_state, gfx::Rect* bounds);
+
+  // Animates the window bounds to |bounds|.
+  void SetChildBoundsAnimated(aura::Window* child, const gfx::Rect& bounds);
+
   internal::ShelfLayoutManager* shelf_;
   aura::Window* window_;
 
@@ -94,6 +103,9 @@
   // workspace switch.
   gfx::Rect work_area_in_parent_;
 
+  // True if this workspace is currently in fullscreen mode.
+  bool is_fullscreen_;
+
   DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager);
 };
 
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 4302f1d..d12fb15 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/shell_observer.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
@@ -47,6 +48,38 @@
   DISALLOW_COPY_AND_ASSIGN(MaximizeDelegateView);
 };
 
+class TestShellObserver : public ShellObserver {
+ public:
+  TestShellObserver() : call_count_(0),
+                        is_fullscreen_(false) {
+    Shell::GetInstance()->AddShellObserver(this);
+  }
+
+  virtual ~TestShellObserver() {
+    Shell::GetInstance()->RemoveShellObserver(this);
+  }
+
+  virtual void OnFullscreenStateChanged(bool is_fullscreen,
+                                        aura::Window* root_window) OVERRIDE {
+    call_count_++;
+    is_fullscreen_ = is_fullscreen;
+  }
+
+  int call_count() const {
+    return call_count_;
+  }
+
+  bool is_fullscreen() const {
+    return is_fullscreen_;
+  }
+
+ private:
+  int call_count_;
+  bool is_fullscreen_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestShellObserver);
+};
+
 }  // namespace
 
 typedef test::AshTestBase WorkspaceLayoutManagerTest;
@@ -407,4 +440,42 @@
       window->bounds().ToString());
 }
 
+TEST_F(WorkspaceLayoutManagerTest, NotifyFullscreenChanges) {
+  TestShellObserver observer;
+  scoped_ptr<aura::Window> window1(
+      CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
+  scoped_ptr<aura::Window> window2(
+      CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
+  wm::WindowState* window_state1 = wm::GetWindowState(window1.get());
+  wm::WindowState* window_state2 = wm::GetWindowState(window2.get());
+  window_state2->Activate();
+
+  window_state2->ToggleFullscreen();
+  EXPECT_EQ(1, observer.call_count());
+  EXPECT_TRUE(observer.is_fullscreen());
+
+  // When window1 moves to the front the fullscreen state should change.
+  window_state1->Activate();
+  EXPECT_EQ(2, observer.call_count());
+  EXPECT_FALSE(observer.is_fullscreen());
+
+  // It should change back if window2 becomes active again.
+  window_state2->Activate();
+  EXPECT_EQ(3, observer.call_count());
+  EXPECT_TRUE(observer.is_fullscreen());
+
+  window_state2->ToggleFullscreen();
+  EXPECT_EQ(4, observer.call_count());
+  EXPECT_FALSE(observer.is_fullscreen());
+
+  window_state2->ToggleFullscreen();
+  EXPECT_EQ(5, observer.call_count());
+  EXPECT_TRUE(observer.is_fullscreen());
+
+  // Closing the window should change the fullscreen state.
+  window2.reset();
+  EXPECT_EQ(6, observer.call_count());
+  EXPECT_FALSE(observer.is_fullscreen());
+}
+
 }  // namespace ash
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 3617da5..b8cd504 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -74,7 +74,7 @@
     // is set by tab dragging code.
     if (!window_state->IsNormalShowState() &&
         (window_component != HTCAPTION ||
-         window_state->tracked_by_workspace())) {
+         !window_state->is_dragged())) {
       return scoped_ptr<WindowResizer>();
     }
     window_resizer = internal::WorkspaceWindowResizer::Create(
@@ -436,6 +436,7 @@
   if (!did_move_or_resize_ || details_.window_component != HTCAPTION)
     return;
 
+  bool snapped = false;
   // When the window is not in the normal show state, we do not snap the window.
   // This happens when the user minimizes or maximizes the window by keyboard
   // shortcut while dragging it. If the window is the result of dragging a tab
@@ -458,8 +459,11 @@
     if (window_state()->CanResize() &&
         !dock_layout_->is_dragged_window_docked()) {
       snap_sizer_->SnapWindowToTargetBounds();
+      snapped = true;
     }
   }
+  if (window_state()->IsSnapped() && !snapped)
+    window_state()->Restore();
 }
 
 void WorkspaceWindowResizer::RevertDrag() {
@@ -749,9 +753,9 @@
     magnetism_window_ = NULL;
   }
 
-  // Avoid magnetically snapping to popups, menus, tooltips, controls and
-  // windows that are not tracked by workspace.
-  if (!window_state()->CanResize() || !window_state()->tracked_by_workspace())
+  // Avoid magnetically snapping windows that are not resizable.
+  // TODO(oshima): change this to window.type() == TYPE_NORMAL.
+  if (!window_state()->CanResize())
     return false;
 
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index c049103..599c4ea 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -596,6 +596,11 @@
 
 // Assertions around dragging to the left/right edge of the screen.
 TEST_F(WorkspaceWindowResizerTest, Edge) {
+  if (!SupportsHostWindowResize())
+    return;
+
+  // Resize host window to force insets update.
+  UpdateDisplay("800x700");
   // TODO(varkha): Insets are reset after every drag because of
   // http://crbug.com/292238.
   // Window is wide enough not to get docked right away.
@@ -636,11 +641,12 @@
   }
 
   // Test if the restore bounds is correct in multiple displays.
-  window_state->ClearRestoreBounds();
-
   if (!SupportsMultipleDisplays())
     return;
 
+  // Restore the window to clear snapped state.
+  window_state->Restore();
+
   UpdateDisplay("800x600,500x600");
   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
   EXPECT_EQ(root_windows[0], window_->GetRootWindow());
@@ -654,7 +660,7 @@
     scoped_ptr<WindowResizer> resizer(CreateResizerForTest(
         window_.get(), gfx::Point(), HTCAPTION));
     ASSERT_TRUE(resizer.get());
-    resizer->Drag(CalculateDragPoint(*resizer, 499, 00), 0);
+    resizer->Drag(CalculateDragPoint(*resizer, 499, 0), 0);
     int bottom =
         ScreenAsh::GetDisplayWorkAreaBoundsInParent(window_.get()).bottom();
     resizer->CompleteDrag(0);
@@ -783,8 +789,9 @@
     window_->SetBounds(gfx::Rect(100, 200, 300, 20));
     DCHECK_LT(window_->bounds().height(),
               WorkspaceWindowResizer::kMinOnscreenHeight);
+    // Drag down avoiding dragging along the edge as that would side-snap.
     scoped_ptr<WindowResizer> resizer(CreateResizerForTest(
-        window_.get(), gfx::Point(), HTCAPTION));
+        window_.get(), gfx::Point(10, 0), HTCAPTION));
     ASSERT_TRUE(resizer.get());
     resizer->Drag(CalculateDragPoint(*resizer, 0, 400), 0);
     int expected_y = kRootHeight - window_->bounds().height() - 10;
@@ -802,8 +809,9 @@
   {
     window_->SetBounds(gfx::Rect(100, 200, 300, 400));
     scoped_ptr<WindowResizer> resizer(CreateResizerForTest(
-        window_.get(), gfx::Point(), HTCAPTION));
+        window_.get(), gfx::Point(10, 0), HTCAPTION));
     ASSERT_TRUE(resizer.get());
+    // Drag down avoiding dragging along the edge as that would side-snap.
     resizer->Drag(CalculateDragPoint(*resizer, 0, 400), 0);
     int expected_y =
         kRootHeight - WorkspaceWindowResizer::kMinOnscreenHeight - 10;
diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc
index 8d37858..7bf6a28 100644
--- a/ash/wm/workspace_controller.cc
+++ b/ash/wm/workspace_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/wm/workspace_controller.h"
 
+#include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"
@@ -30,6 +31,15 @@
 // animation (when logging in).
 const int kInitialPauseTimeMS = 750;
 
+// Returns true if the |window| is docked and visible.
+bool IsDockedAndVisible(const aura::Window* window) {
+  return (window->parent()->id() == kShellWindowId_DockedContainer &&
+          window->IsVisible() &&
+          !wm::GetWindowState(window)->IsMinimized() &&
+          window->type() != aura::client::WINDOW_TYPE_POPUP &&
+          !window->transient_parent());
+}
+
 }  // namespace
 
 WorkspaceController::WorkspaceController(aura::Window* viewport)
@@ -57,31 +67,42 @@
 WorkspaceWindowState WorkspaceController::GetWindowState() const {
   if (!shelf_)
     return WORKSPACE_WINDOW_STATE_DEFAULT;
-
-  const gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
-  const aura::Window::Windows& windows(viewport_->children());
-  bool window_overlaps_launcher = false;
-  bool has_maximized_window = false;
-  for (aura::Window::Windows::const_iterator i = windows.begin();
-       i != windows.end(); ++i) {
-    wm::WindowState* window_state = wm::GetWindowState(*i);
-    if (window_state->ignored_by_shelf())
-      continue;
-    ui::Layer* layer = (*i)->layer();
-    if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f)
-      continue;
-    if (window_state->IsMaximized()) {
-      // An untracked window may still be fullscreen so we keep iterating when
-      // we hit a maximized window.
-      has_maximized_window = true;
-    } else if (window_state->IsFullscreen()) {
-      return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
-    }
-    if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds))
-      window_overlaps_launcher = true;
+  const aura::Window* topmost_fullscreen_window = GetRootWindowController(
+      viewport_->GetRootWindow())->GetWindowForFullscreenMode();
+  if (topmost_fullscreen_window &&
+      !wm::GetWindowState(topmost_fullscreen_window)->ignored_by_shelf()) {
+    return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
   }
-  if (has_maximized_window)
-    return WORKSPACE_WINDOW_STATE_MAXIMIZED;
+
+  // These are the container ids of containers which may contain windows that
+  // may overlap the launcher shelf and affect its transparency.
+  const int kWindowContainerIds[] = {
+      internal::kShellWindowId_DefaultContainer,
+      internal::kShellWindowId_DockedContainer,
+  };
+  const gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
+  bool window_overlaps_launcher = false;
+  for (size_t idx = 0; idx < arraysize(kWindowContainerIds); idx++) {
+    const aura::Window* container = Shell::GetContainer(
+        viewport_->GetRootWindow(), kWindowContainerIds[idx]);
+    const aura::Window::Windows& windows(container->children());
+    for (aura::Window::Windows::const_iterator i = windows.begin();
+         i != windows.end(); ++i) {
+      wm::WindowState* window_state = wm::GetWindowState(*i);
+      if (window_state->ignored_by_shelf())
+        continue;
+      ui::Layer* layer = (*i)->layer();
+      if (!layer->GetTargetVisibility())
+        continue;
+      if (window_state->IsMaximized())
+        return WORKSPACE_WINDOW_STATE_MAXIMIZED;
+      if (!window_overlaps_launcher &&
+          ((*i)->bounds().Intersects(shelf_bounds) ||
+           IsDockedAndVisible(*i))) {
+        window_overlaps_launcher = true;
+      }
+    }
+  }
 
   return window_overlaps_launcher ?
       WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF :
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 48e08fc..861578f 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -103,7 +103,6 @@
     aura::Window* window = CreateTestWindow();
     window->SetBounds(bounds);
     wm::WindowState* window_state = wm::GetWindowState(window);
-    window_state->SetTrackedByWorkspace(true);
     window_state->set_window_position_managed(true);
     window->Show();
     return window;
@@ -688,47 +687,6 @@
   EXPECT_EQ(w2->parent(), w1->parent());
 }
 
-// Verifies changing TrackedByWorkspace works.
-TEST_F(WorkspaceControllerTest, TrackedByWorkspace) {
-  // Create a fullscreen window.
-  scoped_ptr<Window> w1(CreateTestWindow());
-  w1->Show();
-  wm::ActivateWindow(w1.get());
-  w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
-  EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
-  EXPECT_TRUE(w1->IsVisible());
-
-  // Create a second fullscreen window and mark it not tracked by workspace
-  // manager.
-  scoped_ptr<Window> w2(CreateTestWindowUnparented());
-  w2->SetBounds(gfx::Rect(1, 6, 25, 30));
-  w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
-  ParentWindowInPrimaryRootWindow(w2.get());
-  w2->Show();
-  wm::GetWindowState(w2.get())->SetTrackedByWorkspace(false);
-  wm::ActivateWindow(w2.get());
-
-  // Activating |w2| should force it to have the same parent as |w1|.
-  EXPECT_EQ(w1->parent(), w2->parent());
-  EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
-  EXPECT_TRUE(w1->IsVisible());
-  EXPECT_TRUE(w2->IsVisible());
-
-  // Because |w2| isn't tracked we should be able to set the bounds of it.
-  gfx::Rect bounds(w2->bounds());
-  bounds.Offset(4, 5);
-  w2->SetBounds(bounds);
-  EXPECT_EQ(bounds.ToString(), w2->bounds().ToString());
-
-  // Transition it to tracked by worskpace. It should end up in the desktop
-  // workspace.
-  wm::GetWindowState(w2.get())->SetTrackedByWorkspace(true);
-  EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
-  EXPECT_TRUE(w1->IsVisible());
-  EXPECT_TRUE(w2->IsVisible());
-  EXPECT_EQ(w1->parent(), w2->parent());
-}
-
 // Test the placement of newly created windows.
 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
   if (!SupportsHostWindowResize())
@@ -1271,105 +1229,6 @@
 
 }  // namespace
 
-// Verifies setting tracked by workspace to false and then dragging a fullscreen
-// window doesn't result in changing the window hierarchy (which typically
-// indicates new workspaces have been created).
-TEST_F(WorkspaceControllerTest, DragFullscreenNonTrackedWindow) {
-  aura::test::EventGenerator generator(
-      Shell::GetPrimaryRootWindow(), gfx::Point());
-  generator.MoveMouseTo(5, 5);
-
-  aura::test::TestWindowDelegate delegate;
-  delegate.set_window_component(HTCAPTION);
-  scoped_ptr<Window> w1(
-      aura::test::CreateTestWindowWithDelegate(&delegate,
-                                               aura::client::WINDOW_TYPE_NORMAL,
-                                               gfx::Rect(5, 6, 7, 8),
-                                               NULL));
-  ParentWindowInPrimaryRootWindow(w1.get());
-  w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
-  w1->Show();
-  wm::ActivateWindow(w1.get());
-  DragMaximizedNonTrackedWindowObserver observer(w1.get());
-  w1->parent()->parent()->AddObserver(&observer);
-  const gfx::Rect max_bounds(w1->bounds());
-
-  generator.PressLeftButton();
-  generator.MoveMouseTo(100, 100);
-  // The bounds shouldn't change (drag should result in nothing happening
-  // now.
-  EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
-
-  generator.ReleaseLeftButton();
-  EXPECT_EQ(0, observer.change_count());
-
-  // Set tracked to false and repeat, now the window should move.
-  wm::GetWindowState(w1.get())->SetTrackedByWorkspace(false);
-  generator.MoveMouseTo(5, 5);
-  generator.PressLeftButton();
-  generator.MoveMouseBy(100, 100);
-  EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
-                      max_bounds.width(), max_bounds.height()).ToString(),
-            w1->bounds().ToString());
-
-  generator.ReleaseLeftButton();
-  wm::GetWindowState(w1.get())->SetTrackedByWorkspace(true);
-  // Marking the window tracked again should snap back to origin.
-  EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
-  EXPECT_EQ(0, observer.change_count());
-
-  w1->parent()->parent()->RemoveObserver(&observer);
-}
-
-// Verifies setting tracked by workspace to false and then dragging a maximized
-// window can change the bound.
-TEST_F(WorkspaceControllerTest, DragMaximizedNonTrackedWindow) {
-  aura::test::EventGenerator generator(
-      Shell::GetPrimaryRootWindow(), gfx::Point());
-  generator.MoveMouseTo(5, 5);
-
-  aura::test::TestWindowDelegate delegate;
-  delegate.set_window_component(HTCAPTION);
-  scoped_ptr<Window> w1(
-      aura::test::CreateTestWindowWithDelegate(&delegate,
-                                               aura::client::WINDOW_TYPE_NORMAL,
-                                               gfx::Rect(5, 6, 7, 8),
-                                               NULL));
-  ParentWindowInPrimaryRootWindow(w1.get());
-  w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
-  w1->Show();
-  wm::ActivateWindow(w1.get());
-  DragMaximizedNonTrackedWindowObserver observer(w1.get());
-  w1->parent()->parent()->AddObserver(&observer);
-  const gfx::Rect max_bounds(w1->bounds());
-
-  generator.PressLeftButton();
-  generator.MoveMouseTo(100, 100);
-  // The bounds shouldn't change (drag should result in nothing happening
-  // now.
-  EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
-
-  generator.ReleaseLeftButton();
-  EXPECT_EQ(0, observer.change_count());
-
-  // Set tracked to false and repeat, now the window should move.
-  wm::GetWindowState(w1.get())->SetTrackedByWorkspace(false);
-  generator.MoveMouseTo(5, 5);
-  generator.PressLeftButton();
-  generator.MoveMouseBy(100, 100);
-  EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
-                      max_bounds.width(), max_bounds.height()).ToString(),
-            w1->bounds().ToString());
-
-  generator.ReleaseLeftButton();
-  wm::GetWindowState(w1.get())->SetTrackedByWorkspace(true);
-  // Marking the window tracked again should snap back to origin.
-  EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
-  EXPECT_EQ(0, observer.change_count());
-
-  w1->parent()->parent()->RemoveObserver(&observer);
-}
-
 // Verifies that a new maximized window becomes visible after its activation
 // is requested, even though it does not become activated because a system
 // modal window is active.
@@ -1389,5 +1248,64 @@
   EXPECT_TRUE(maximized_window->IsVisible());
 }
 
+namespace {
+
+// Subclass of WorkspaceControllerTest that runs tests with docked windows
+// enabled and disabled.
+class WorkspaceControllerTestDragging
+    : public WorkspaceControllerTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  WorkspaceControllerTestDragging() {}
+  virtual ~WorkspaceControllerTestDragging() {}
+
+  // testing::Test:
+  virtual void SetUp() OVERRIDE {
+    WorkspaceControllerTest::SetUp();
+    if (!docked_windows_enabled()) {
+      CommandLine::ForCurrentProcess()->AppendSwitch(
+          ash::switches::kAshDisableDockedWindows);
+    }
+  }
+
+  bool docked_windows_enabled() const { return GetParam(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
+};
+
+}  // namespace
+
+// Verifies that when dragging a window over the shelf overlap is detected
+// during and after the drag.
+TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
+  aura::test::TestWindowDelegate delegate;
+  delegate.set_window_component(HTCAPTION);
+  scoped_ptr<Window> w1(
+      aura::test::CreateTestWindowWithDelegate(&delegate,
+                                               aura::client::WINDOW_TYPE_NORMAL,
+                                               gfx::Rect(5, 5, 100, 50),
+                                               NULL));
+  ParentWindowInPrimaryRootWindow(w1.get());
+
+  ShelfLayoutManager* shelf = shelf_layout_manager();
+  shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
+
+  // Drag near the shelf
+  aura::test::EventGenerator generator(
+      Shell::GetPrimaryRootWindow(), gfx::Point());
+  generator.MoveMouseTo(10, 10);
+  generator.PressLeftButton();
+  generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
+
+  // Shelf should detect overlap. Overlap state stays after mouse is released.
+  EXPECT_TRUE(GetWindowOverlapsShelf());
+  generator.ReleaseLeftButton();
+  EXPECT_TRUE(GetWindowOverlapsShelf());
+}
+
+INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
+                        ::testing::Bool());
+
 }  // namespace internal
 }  // namespace ash