Merge from Chromium at DEPS revision r210036

This commit was generated by merge_to_master.py.

Change-Id: Ib0e33a83ad5dfa541481e83d7acfc6970e68f471
diff --git a/ash/OWNERS b/ash/OWNERS
index ab22b20..55bce5e 100644
--- a/ash/OWNERS
+++ b/ash/OWNERS
@@ -4,6 +4,7 @@
 
 per-file ash_strings.grd=*
 per-file ash_chromeos_strings.grdp=*
+per-file ash_switches.*=*
 
 per-file ash_root_window_transformer.*=oshima@chromium.org
 per-file extended_desktop_unittest.*=oshima@chromium.org
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index f30ce5d..b96c201 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -39,7 +39,7 @@
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/web_notification/web_notification_tray.h"
-#include "ash/touch/touch_observer_hud.h"
+#include "ash/touch/touch_hud_debug.h"
 #include "ash/volume_control_delegate.h"
 #include "ash/wm/partial_screenshot_view.h"
 #include "ash/wm/power_button_controller.h"
@@ -561,8 +561,8 @@
     case TOUCH_HUD_CLEAR: {
       internal::RootWindowController* controller =
           internal::RootWindowController::ForActiveRootWindow();
-      if (controller->touch_observer_hud()) {
-        controller->touch_observer_hud()->Clear();
+      if (controller->touch_hud_debug()) {
+        controller->touch_hud_debug()->Clear();
         return true;
       }
       return false;
@@ -570,12 +570,17 @@
     case TOUCH_HUD_MODE_CHANGE: {
       internal::RootWindowController* controller =
           internal::RootWindowController::ForActiveRootWindow();
-      if (controller->touch_observer_hud()) {
-        controller->touch_observer_hud()->ChangeToNextMode();
+      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 DISABLE_GPU_WATCHDOG:
       content::GpuDataManager::GetInstance()->DisableGpuWatchdog();
       return true;
@@ -762,32 +767,32 @@
       if (ime_control_delegate_)
         return ime_control_delegate_->HandleSwitchIme(accelerator);
       break;
-    case SELECT_WIN_0:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(0);
+    case LAUNCH_APP_0:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0);
       return true;
-    case SELECT_WIN_1:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(1);
+    case LAUNCH_APP_1:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1);
       return true;
-    case SELECT_WIN_2:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(2);
+    case LAUNCH_APP_2:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2);
       return true;
-    case SELECT_WIN_3:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(3);
+    case LAUNCH_APP_3:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3);
       return true;
-    case SELECT_WIN_4:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(4);
+    case LAUNCH_APP_4:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4);
       return true;
-    case SELECT_WIN_5:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(5);
+    case LAUNCH_APP_5:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5);
       return true;
-    case SELECT_WIN_6:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(6);
+    case LAUNCH_APP_6:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6);
       return true;
-    case SELECT_WIN_7:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(7);
+    case LAUNCH_APP_7:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7);
       return true;
-    case SELECT_LAST_WIN:
-      Launcher::ForPrimaryDisplay()->SwitchToWindow(-1);
+    case LAUNCH_LAST_APP:
+      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
       return true;
     case WINDOW_SNAP_LEFT:
     case WINDOW_SNAP_RIGHT: {
@@ -831,10 +836,6 @@
       return true;
     }
     case TOGGLE_MAXIMIZED: {
-      if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
-        shell->delegate()->RecordUserMetricsAction(
-            UMA_ACCEL_MAXIMIZE_RESTORE_F4);
-      }
       shell->delegate()->ToggleMaximized();
       return true;
     }
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 874e807..5bfc38a 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -74,6 +74,8 @@
     TOUCH_HUD_MODE_CHANGE },
   { true, ui::VKEY_I, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN,
     TOUCH_HUD_CLEAR },
+  { true, ui::VKEY_9, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
+    TOUCH_HUD_PROJECTION_TOGGLE },
   // Accessibility: Spoken feedback shortcuts. The first one is to toggle
   // spoken feedback on or off. The others are only valid when
   // spoken feedback is enabled.
@@ -109,7 +111,7 @@
   // extended keyboard shortcuts.
   { false, ui::VKEY_LWIN, ui::EF_NONE, TOGGLE_APP_LIST },
   { false, ui::VKEY_LWIN, ui::EF_ALT_DOWN, TOGGLE_CAPS_LOCK },
-  { true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_NONE, TOGGLE_MAXIMIZED },
+  { true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_NONE, TOGGLE_FULLSCREEN },
   { true, ui::VKEY_MEDIA_LAUNCH_APP2, ui::EF_SHIFT_DOWN, TOGGLE_FULLSCREEN },
   { true, ui::VKEY_VOLUME_MUTE, ui::EF_NONE, VOLUME_MUTE },
   { true, ui::VKEY_VOLUME_DOWN, ui::EF_NONE, VOLUME_DOWN },
@@ -130,15 +132,15 @@
   { true, ui::VKEY_S, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN,
     SHOW_SYSTEM_TRAY_BUBBLE },
   { true, ui::VKEY_ESCAPE, ui::EF_SHIFT_DOWN, SHOW_TASK_MANAGER },
-  { true, ui::VKEY_1, ui::EF_ALT_DOWN, SELECT_WIN_0 },
-  { true, ui::VKEY_2, ui::EF_ALT_DOWN, SELECT_WIN_1 },
-  { true, ui::VKEY_3, ui::EF_ALT_DOWN, SELECT_WIN_2 },
-  { true, ui::VKEY_4, ui::EF_ALT_DOWN, SELECT_WIN_3 },
-  { true, ui::VKEY_5, ui::EF_ALT_DOWN, SELECT_WIN_4 },
-  { true, ui::VKEY_6, ui::EF_ALT_DOWN, SELECT_WIN_5 },
-  { true, ui::VKEY_7, ui::EF_ALT_DOWN, SELECT_WIN_6 },
-  { true, ui::VKEY_8, ui::EF_ALT_DOWN, SELECT_WIN_7 },
-  { true, ui::VKEY_9, ui::EF_ALT_DOWN, SELECT_LAST_WIN },
+  { true, ui::VKEY_1, ui::EF_ALT_DOWN, LAUNCH_APP_0 },
+  { true, ui::VKEY_2, ui::EF_ALT_DOWN, LAUNCH_APP_1 },
+  { true, ui::VKEY_3, ui::EF_ALT_DOWN, LAUNCH_APP_2 },
+  { true, ui::VKEY_4, ui::EF_ALT_DOWN, LAUNCH_APP_3 },
+  { true, ui::VKEY_5, ui::EF_ALT_DOWN, LAUNCH_APP_4 },
+  { true, ui::VKEY_6, ui::EF_ALT_DOWN, LAUNCH_APP_5 },
+  { true, ui::VKEY_7, ui::EF_ALT_DOWN, LAUNCH_APP_6 },
+  { true, ui::VKEY_8, ui::EF_ALT_DOWN, LAUNCH_APP_7 },
+  { true, ui::VKEY_9, ui::EF_ALT_DOWN, LAUNCH_LAST_APP },
 
   // Window management shortcuts.
   { true, ui::VKEY_OEM_4, ui::EF_ALT_DOWN, WINDOW_SNAP_LEFT },
diff --git a/ash/accelerators/accelerator_table.h b/ash/accelerators/accelerator_table.h
index 563a7c5..8c400ce 100644
--- a/ash/accelerators/accelerator_table.h
+++ b/ash/accelerators/accelerator_table.h
@@ -34,6 +34,15 @@
   FOCUS_PREVIOUS_PANE,
   KEYBOARD_BRIGHTNESS_DOWN,
   KEYBOARD_BRIGHTNESS_UP,
+  LAUNCH_APP_0,
+  LAUNCH_APP_1,
+  LAUNCH_APP_2,
+  LAUNCH_APP_3,
+  LAUNCH_APP_4,
+  LAUNCH_APP_5,
+  LAUNCH_APP_6,
+  LAUNCH_APP_7,
+  LAUNCH_LAST_APP,
   LOCK_PRESSED,
   LOCK_RELEASED,
   MAGNIFY_SCREEN_ZOOM_IN,
@@ -59,15 +68,6 @@
   SCALE_UI_DOWN,
   SCALE_UI_RESET,
   SCALE_UI_UP,
-  SELECT_LAST_WIN,
-  SELECT_WIN_0,
-  SELECT_WIN_1,
-  SELECT_WIN_2,
-  SELECT_WIN_3,
-  SELECT_WIN_4,
-  SELECT_WIN_5,
-  SELECT_WIN_6,
-  SELECT_WIN_7,
   SHOW_KEYBOARD_OVERLAY,
   SHOW_MESSAGE_CENTER_BUBBLE,
   SHOW_OAK,
@@ -89,6 +89,7 @@
   TOGGLE_WIFI,
   TOUCH_HUD_CLEAR,
   TOUCH_HUD_MODE_CHANGE,
+  TOUCH_HUD_PROJECTION_TOGGLE,
   VOLUME_DOWN,
   VOLUME_MUTE,
   VOLUME_UP,
diff --git a/ash/accelerators/exit_warning_handler.cc b/ash/accelerators/exit_warning_handler.cc
index 36b5b21..767b266 100644
--- a/ash/accelerators/exit_warning_handler.cc
+++ b/ash/accelerators/exit_warning_handler.cc
@@ -8,8 +8,8 @@
 #include "ash/shell_delegate.h"
 #include "ash/shell_window_ids.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "grit/ash_strings.h"
 #include "ui/aura/root_window.h"
 #include "ui/base/accessibility/accessible_view_state.h"
@@ -26,7 +26,7 @@
 namespace ash {
 namespace {
 
-const int64 kTimeOutMilliseconds = 1000;
+const int64 kTimeOutMilliseconds = 2000;
 const SkColor kForegroundColor = 0xFFFFFFFF;
 const SkColor kBackgroundColor = 0xE0808080;
 const int kHorizontalMarginAroundText = 100;
@@ -168,8 +168,7 @@
                    ps.width(), ps.height());
   views::Widget::InitParams params;
   params.type = views::Widget::InitParams::TYPE_POPUP;
-  params.transient = true;
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.accept_events = false;
   params.can_activate = false;
   params.keep_on_top = true;
diff --git a/ash/accelerators/exit_warning_handler.h b/ash/accelerators/exit_warning_handler.h
index 832a3ed..b303d84 100644
--- a/ash/accelerators/exit_warning_handler.h
+++ b/ash/accelerators/exit_warning_handler.h
@@ -6,7 +6,7 @@
 #define ASH_ACCELERATORS_EXIT_WARNING_HANDLER_H_
 
 #include "ash/ash_export.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/base/accelerators/accelerator.h"
 
 namespace views {
diff --git a/ash/accelerators/nested_dispatcher_controller_unittest.cc b/ash/accelerators/nested_dispatcher_controller_unittest.cc
index 8febefa..7c06542 100644
--- a/ash/accelerators/nested_dispatcher_controller_unittest.cc
+++ b/ash/accelerators/nested_dispatcher_controller_unittest.cc
@@ -35,7 +35,7 @@
 
   int num_key_events_dispatched() { return num_key_events_dispatched_; }
 
-#if defined(OS_WIN) || defined(USE_X11) || defined(USE_MESSAGEPUMP_LINUX)
+#if defined(OS_WIN) || defined(USE_X11) || defined(USE_OZONE)
   virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE {
     if (ui::EventTypeFromNative(event) == ui::ET_KEY_RELEASED)
       num_key_events_dispatched_++;
diff --git a/ash/ash.gyp b/ash/ash.gyp
index abbda38..3c7531c 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -92,6 +92,8 @@
         'display/display_error_dialog.h',
         'display/display_info.h',
         'display/display_info.cc',
+        'display/display_layout.h',
+        'display/display_layout.cc',
         'display/display_manager.cc',
         'display/display_manager.h',
         'display/display_pref_util.h',
@@ -127,6 +129,8 @@
         'keyboard_overlay/keyboard_overlay_delegate.h',
         'keyboard_overlay/keyboard_overlay_view.cc',
         'keyboard_overlay/keyboard_overlay_view.h',
+        'launcher/alternate_app_list_button.cc',
+        'launcher/alternate_app_list_button.h',
         'launcher/app_list_button.cc',
         'launcher/app_list_button.h',
         'launcher/launcher.cc',
@@ -231,6 +235,8 @@
         'system/chromeos/network/tray_sms.h',
         'system/chromeos/network/tray_vpn.cc',
         'system/chromeos/network/tray_vpn.h',
+        'system/chromeos/power/power_status.cc',
+        'system/chromeos/power/power_status.h',
         'system/chromeos/power/power_status_view.cc',
         'system/chromeos/power/power_status_view.h',
         'system/chromeos/power/tray_power.cc',
@@ -343,6 +349,10 @@
         'system/user/user_observer.h',
         'system/web_notification/web_notification_tray.cc',
         'system/web_notification/web_notification_tray.h',
+        'touch/touch_hud_debug.cc',
+        'touch/touch_hud_debug.h',
+        'touch/touch_hud_projection.cc',
+        'touch/touch_hud_projection.h',
         'touch/touch_observer_hud.cc',
         'touch/touch_observer_hud.h',
         'touch/touch_uma.cc',
@@ -412,6 +422,8 @@
         'wm/panels/panel_frame_view.h',
         'wm/panels/panel_layout_manager.cc',
         'wm/panels/panel_layout_manager.h',
+        'wm/panels/panel_window_event_handler.cc',
+        'wm/panels/panel_window_event_handler.h',
         'wm/panels/panel_window_resizer.cc',
         'wm/panels/panel_window_resizer.h',
         'wm/partial_screenshot_view.cc',
@@ -539,6 +551,8 @@
         ['chromeos==1', {
           'dependencies': [
             '../chromeos/chromeos.gyp:chromeos',
+            # Ash #includes power_supply_properties.pb.h directly.
+            '../chromeos/chromeos.gyp:power_manager_proto',
           ],
         }, { # else: chromeos!=1
           'sources/': [
@@ -625,6 +639,7 @@
         '../ui/compositor/compositor.gyp:compositor',
         '../ui/keyboard/keyboard.gyp:keyboard',
         '../ui/message_center/message_center.gyp:message_center',
+        '../ui/message_center/message_center.gyp:message_center_test_support',
         '../ui/ui.gyp:ui',
         '../ui/ui.gyp:ui_resources',
         '../ui/ui.gyp:ui_test_support',
@@ -694,6 +709,10 @@
         'shell/window_watcher.cc',
         'shell/window_watcher_unittest.cc',
         'system/chromeos/network/network_state_notifier_unittest.cc',
+        'system/chromeos/power/power_status_unittest.cc',
+        'system/chromeos/power/tray_power_unittest.cc',
+        'system/chromeos/screen_security/screen_tray_item_unittest.cc',
+        'system/chromeos/tray_display_unittest.cc',
         'system/tray/system_tray_unittest.cc',
         'system/user/tray_user_unittest.cc',
         'system/web_notification/web_notification_tray_unittest.cc',
@@ -780,6 +799,10 @@
           'sources/': [
             ['exclude', 'display/display_error_dialog_unittest.cc'],
           ],
+        }, {  # chromeos==1
+          'dependencies': [
+            '../chromeos/chromeos.gyp:power_manager_proto',
+          ],
         }],
       ],
     },
diff --git a/ash/ash_chromeos_strings.grdp b/ash/ash_chromeos_strings.grdp
index 1e99643..76b906b 100644
--- a/ash/ash_chromeos_strings.grdp
+++ b/ash/ash_chromeos_strings.grdp
@@ -3,6 +3,14 @@
      Everything in this file is wrapped in <if expr="pp_ifdef('chromeos')">. -->
 <grit-part>
 
+  <!-- Status tray charging strings. -->
+  <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected.">
+    Low-power charger connected
+  </message>
+  <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE" desc="The message body of a notification indicating that a low-current USB charger has been connected.">
+    Your Chromebook may not charge while it is turned on. Consider using the official charger.
+  </message>
+  
   <!-- Status Tray Network strings -->
   <message name="IDS_ASH_STATUS_TRAY_NETWORK" desc="The label used in the network dialog header.">
     Network
@@ -250,10 +258,10 @@
     Stop
   </message>
   <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED" desc="label for screen sharing notification">
-    You are being helped
+    Sharing control of your screen via Hangouts.
   </message>
   <message name="IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED_NAME" desc="label for screen sharing notification with name">
-    You are being helped by <ph name="HELPER_NAME">$1<ex>Walder Frey</ex></ph>
+    Sharing control of your screen with <ph name="HELPER_NAME">$1<ex>Walder Frey</ex></ph> via Hangouts.
   </message>
 
 </grit-part>
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 53b2547..a49e9bc 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -289,6 +289,9 @@
       <message name="IDS_ASH_STATUS_TRAY_UPDATE" desc="The label used in the tray popup to notify that the user should restart to get system updates.">
         Restart to update
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_RESTART_AND_POWERWASH_UPDATE" desc="The label used in the tray popup to notify that the user should restart and powerwash to get system updates.">
+        Restart and Powerwash to update
+      </message>
 
       <message name="IDS_ASH_STATUS_TRAY_BRIGHTNESS" desc="The accessible text for the brightness slider.">
         Brightness
@@ -307,22 +310,37 @@
       </message>
 
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING" desc="The label used in the tray to show that the current status is mirroring.">
-	Mirroring to <ph name="DISPLAY_NAME">$1</ph>
+        Mirroring to <ph name="DISPLAY_NAME">$1</ph>
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED" desc="The label used in the tray to show that the current status is extended.">
-	Extending screen to <ph name="DISPLAY_NAME">$1</ph>
+        Extending screen to <ph name="DISPLAY_NAME">$1</ph>
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL" desc="The label used in the tray to show that the current status is mirroring and the device doesn't have the internal display.">
-	Mirroring
+        Mirroring
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL" desc="The label used in the tray to show that the current status is extended and the device doesn't have the internal display.">
-	Extending screen
+        Extending screen
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED" desc="The label used in the tray to show that the current status is docked mode.">
-	Dock mode
+        Dock mode
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED" desc="The label used in the tray to notify that the display resolution settings has changed.">
+        <ph name="DISPLAY_NAME">$1</ph> has been resized to <ph name="RESOLUTION">$2</ph>
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED" desc="The label used in the tray to notify that the display rotation settings has changed.">
+        <ph name="DISPLAY_NAME">$1</ph> has been rotated
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY" desc="The label used in the tray to show a single display's configuration">
+        <ph name="DISPLAY_NAME">$1</ph>: <ph name="ANNOTATION">$2</ph>
       </message>
       <message name="IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME" desc="Label shown in tray for a display whose name is unknown.">
-	Unknown Display
+        Unknown Display
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION" desc="Label used to annotate the display's status like resolution or overscan flag.">
+        <ph name="RESOLUTION">$1</ph>, <ph name="OVERSCAN">$2</ph>
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN" desc="Label used to describe that the system notice that this display device may have overscan area.">
+        overscan
       </message>
       <message name="IDS_ASH_STATUS_TRAY_DRIVE_SYNCING" desc="The label in the tray to indicate onoing file sync operations.">
         Syncing <ph name="count">$1<ex>3</ex></ph> file(s)
@@ -392,13 +410,13 @@
         Spoken feedback
       </message>
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE" desc="The label used in the accessibility menu of the system tray to toggle on/off high contrast feature.">
-        High contrast
+        High contrast mode
       </message>
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER" desc="The label used in the accessibility menu of the system tray to toggle on/off magnifier feature.">
         Screen magnifier
       </message>
-      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR" desc="The label used in the accessibility menu of the system tray to toggle on/off large cursor feature.">
-        Large cursor
+      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR" desc="The label used in the accessibility menu of the system tray to toggle on/off large mouse cursor feature.">
+        Large mouse cursor
       </message>
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LEARN_MORE" desc="The label used in the accessibility menu of the system tray
      to open a webpage (article on help center) containing explanation about accessibility feature.">
@@ -498,13 +516,13 @@
         Minimize
       </message>
       <message name="IDS_ASH_DISPLAY_FAILURE_ON_MIRRORING" desc="An error message to show that the system failed to enter the mirroring mode.">
-	Could not mirror displays since no supported resolutions found. Entered extended desktop instead.
+        Could not mirror displays since no supported resolutions found. Entered extended desktop instead.
       </message>
       <message name="IDS_ASH_DISPLAY_FAILURE_ON_NON_MIRRORING" desc="An error message to show that the system failed to enter the extended desktop mode or unknown status. Please translate the parentized text.">
-	Dear Monitor, it's not working out between us. (That monitor is not supported)
+        Dear Monitor, it's not working out between us. (That monitor is not supported)
       </message>
       <message name="IDS_ASH_INTERNAL_DISPLAY_NAME" desc="The name of the internal display which is shown in the display settings.">
-	Internal Display
+        Internal Display
       </message>
       <message name="IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT" desc="The text of the notification when a peripheral device is in low battery condition.">
         Battery low (<ph name="percentage">$1<ex>56</ex></ph>%)
@@ -530,7 +548,7 @@
 
       <!-- ChromeOS-specific strings -->
       <if expr="pp_ifdef('chromeos')">
-	<part file="ash_chromeos_strings.grdp" />
+        <part file="ash_chromeos_strings.grdp" />
       </if>
 
     </messages>
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index cc64455..2e67ee5 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -29,6 +29,17 @@
 // Enable keyboard shortcuts useful for debugging.
 const char kAshDebugShortcuts[] = "ash-debug-shortcuts";
 
+// Default wallpaper to use in guest mode (as paths to trusted,
+// non-user-writable JPEG files).
+const char kAshDefaultGuestWallpaperLarge[] =
+    "ash-default-guest-wallpaper-large";
+const char kAshDefaultGuestWallpaperSmall[] =
+    "ash-default-guest-wallpaper-small";
+
+// Default wallpaper to use (as paths to trusted, non-user-writable JPEG files).
+const char kAshDefaultWallpaperLarge[] = "ash-default-wallpaper-large";
+const char kAshDefaultWallpaperSmall[] = "ash-default-wallpaper-small";
+
 // Disable auto window maximization logic.
 const char kAshDisableAutoMaximizing[] = "ash-disable-auto-maximizing";
 
@@ -63,6 +74,10 @@
 #if defined(OS_CHROMEOS)
 // Disable compositor based mirroring.
 const char kAshDisableSoftwareMirroring[] = "ash-disable-software-mirroring";
+
+// Disable the notification when a low-power USB charger is connected.
+const char kAshDisableUsbChargerNotification[] =
+    "ash-disable-usb-charger-notification";
 #endif
 
 // Extend the status tray volume item to allow the user to choose an audio
@@ -178,5 +193,12 @@
         HasSwitch(ash::switches::kAshUseAlternateShelfLayout);
 }
 
+#if defined(OS_CHROMEOS)
+bool UseUsbChargerNotification() {
+  return !CommandLine::ForCurrentProcess()->
+        HasSwitch(ash::switches::kAshDisableUsbChargerNotification);
+}
+#endif
+
 }  // namespace switches
 }  // namespace ash
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 6414bd9..23eacfc 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -21,6 +21,10 @@
 ASH_EXPORT extern const char kAshConstrainPointerToRoot[];
 ASH_EXPORT extern const char kAshCopyHostBackgroundAtBoot[];
 ASH_EXPORT extern const char kAshDebugShortcuts[];
+ASH_EXPORT extern const char kAshDefaultGuestWallpaperLarge[];
+ASH_EXPORT extern const char kAshDefaultGuestWallpaperSmall[];
+ASH_EXPORT extern const char kAshDefaultWallpaperLarge[];
+ASH_EXPORT extern const char kAshDefaultWallpaperSmall[];
 ASH_EXPORT extern const char kAshDisableAutoMaximizing[];
 ASH_EXPORT extern const char kAshDisableAutoWindowPlacement[];
 ASH_EXPORT extern const char kAshDisableDisplayChangeLimiter[];
@@ -33,6 +37,7 @@
 ASH_EXPORT extern const char kAshDisableDragAndDropAppListToLauncher[];
 #if defined(OS_CHROMEOS)
 ASH_EXPORT extern const char kAshDisableSoftwareMirroring[];
+ASH_EXPORT extern const char kAshDisableUsbChargerNotification[];
 #endif
 ASH_EXPORT extern const char kAshEnableAudioDeviceMenu[];
 ASH_EXPORT extern const char kAshEnableAdvancedGestures[];
@@ -69,6 +74,12 @@
 // Returns true if the alternate shelf layout should be used.
 ASH_EXPORT bool UseAlternateShelfLayout();
 
+#if defined(OS_CHROMEOS)
+// Returns true if a notification should appear when a low-power USB charger
+// is connected.
+ASH_EXPORT bool UseUsbChargerNotification();
+#endif
+
 }  // namespace switches
 }  // namespace ash
 
diff --git a/ash/desktop_background/desktop_background_controller.cc b/ash/desktop_background/desktop_background_controller.cc
index 83ffe46..25b63c3 100644
--- a/ash/desktop_background/desktop_background_controller.cc
+++ b/ash/desktop_background/desktop_background_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/desktop_background/desktop_background_controller.h"
 
+#include "ash/ash_switches.h"
 #include "ash/desktop_background/desktop_background_controller_observer.h"
 #include "ash/desktop_background/desktop_background_view.h"
 #include "ash/desktop_background/desktop_background_widget_controller.h"
@@ -13,8 +14,11 @@
 #include "ash/shell.h"
 #include "ash/shell_factory.h"
 #include "ash/shell_window_ids.h"
+#include "ash/wm/property_util.h"
 #include "ash/wm/root_window_layout_manager.h"
 #include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
 #include "base/logging.h"
 #include "base/synchronization/cancellation_flag.h"
 #include "base/threading/worker_pool.h"
@@ -23,13 +27,12 @@
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/rect.h"
-#include "ui/gfx/image/image.h"
 #include "ui/views/widget/widget.h"
 
 using ash::internal::DesktopBackgroundWidgetController;
-using ash::internal::kAnimatingDesktopController;
-using ash::internal::kDesktopController;
 using content::BrowserThread;
 
 namespace ash {
@@ -61,24 +64,6 @@
 
 }  // namespace
 
-#if defined(GOOGLE_CHROME_BUILD)
-const WallpaperInfo kDefaultLargeWallpaper =
-    { IDR_AURA_WALLPAPERS_2_LANDSCAPE8_LARGE, WALLPAPER_LAYOUT_CENTER_CROPPED };
-const WallpaperInfo kDefaultSmallWallpaper =
-    { IDR_AURA_WALLPAPERS_2_LANDSCAPE8_SMALL, WALLPAPER_LAYOUT_CENTER };
-const WallpaperInfo kGuestLargeWallpaper =
-    { IDR_AURA_WALLPAPERS_2_LANDSCAPE7_LARGE, WALLPAPER_LAYOUT_CENTER_CROPPED };
-const WallpaperInfo kGuestSmallWallpaper =
-    { IDR_AURA_WALLPAPERS_2_LANDSCAPE7_SMALL, WALLPAPER_LAYOUT_CENTER };
-#else
-const WallpaperInfo kDefaultLargeWallpaper =
-    { IDR_AURA_WALLPAPERS_5_GRADIENT5_LARGE, WALLPAPER_LAYOUT_TILE };
-const WallpaperInfo kDefaultSmallWallpaper =
-    { IDR_AURA_WALLPAPERS_5_GRADIENT5_SMALL, WALLPAPER_LAYOUT_TILE };
-const WallpaperInfo kGuestLargeWallpaper = kDefaultLargeWallpaper;
-const WallpaperInfo kGuestSmallWallpaper = kDefaultSmallWallpaper;
-#endif
-
 const int kSmallWallpaperMaxWidth = 1366;
 const int kSmallWallpaperMaxHeight = 800;
 const int kLargeWallpaperMaxWidth = 2560;
@@ -92,23 +77,30 @@
     : public base::RefCountedThreadSafe<
           DesktopBackgroundController::WallpaperLoader> {
  public:
-  explicit WallpaperLoader(const WallpaperInfo& info)
-      : info_(info) {
+  // If set, |file_path| must be a trusted (i.e. read-only,
+  // non-user-controlled) file containing a JPEG image.
+  WallpaperLoader(const base::FilePath& file_path,
+                  WallpaperLayout file_layout,
+                  int resource_id,
+                  WallpaperLayout resource_layout)
+      : file_path_(file_path),
+        file_layout_(file_layout),
+        resource_id_(resource_id),
+        resource_layout_(resource_layout) {
   }
 
-  static void LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> wl) {
+  static void LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> loader) {
     DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
-    wl->LoadingWallpaper();
+    loader->LoadWallpaper();
   }
 
+  const base::FilePath& file_path() const { return file_path_; }
+  int resource_id() const { return resource_id_; }
+
   void Cancel() {
     cancel_flag_.Set();
   }
 
-  int idr() const {
-    return info_.idr;
-  }
-
   WallpaperResizer* ReleaseWallpaperResizer() {
     return wallpaper_resizer_.release();
   }
@@ -117,27 +109,74 @@
   friend class base::RefCountedThreadSafe<
       DesktopBackgroundController::WallpaperLoader>;
 
-  void LoadingWallpaper() {
+  // Loads a JPEG image from |path|, a trusted file -- note that the image
+  // is not loaded in a sandboxed process. Returns an empty pointer on
+  // error.
+  static scoped_ptr<SkBitmap> LoadSkBitmapFromJPEGFile(
+      const base::FilePath& path) {
+    std::string data;
+    if (!file_util::ReadFileToString(path, &data)) {
+      LOG(ERROR) << "Unable to read data from " << path.value();
+      return scoped_ptr<SkBitmap>();
+    }
+
+    scoped_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(
+        reinterpret_cast<const unsigned char*>(data.data()), data.size()));
+    if (!bitmap)
+      LOG(ERROR) << "Unable to decode JPEG data from " << path.value();
+    return bitmap.Pass();
+  }
+
+  void LoadWallpaper() {
     if (cancel_flag_.IsSet())
       return;
-    wallpaper_resizer_.reset(new WallpaperResizer(info_, GetRootWindowsSize()));
+
+    if (!file_path_.empty())
+      file_bitmap_ = LoadSkBitmapFromJPEGFile(file_path_);
+
+    if (cancel_flag_.IsSet())
+      return;
+
+    if (file_bitmap_) {
+      gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(*file_bitmap_);
+      wallpaper_resizer_.reset(new WallpaperResizer(
+          image, GetRootWindowsSize(), file_layout_));
+    } else {
+      wallpaper_resizer_.reset(new WallpaperResizer(
+          resource_id_, GetRootWindowsSize(), resource_layout_));
+    }
   }
 
   ~WallpaperLoader() {}
 
   base::CancellationFlag cancel_flag_;
 
+  // Bitmap loaded from |file_path_|.
+  scoped_ptr<SkBitmap> file_bitmap_;
+
   scoped_ptr<WallpaperResizer> wallpaper_resizer_;
 
-  const WallpaperInfo info_;
+  // Path to a trusted JPEG file.
+  base::FilePath file_path_;
+
+  // Layout to be used when displaying the image from |file_path_|.
+  WallpaperLayout file_layout_;
+
+  // ID of an image resource to use if |file_path_| is empty or unloadable.
+  int resource_id_;
+
+  // Layout to be used when displaying |resource_id_|.
+  WallpaperLayout resource_layout_;
 
   DISALLOW_COPY_AND_ASSIGN(WallpaperLoader);
 };
 
 DesktopBackgroundController::DesktopBackgroundController()
-    : locked_(false),
+    : command_line_for_testing_(NULL),
+      locked_(false),
       desktop_background_mode_(BACKGROUND_NONE),
       background_color_(kTransparentColor),
+      current_default_wallpaper_resource_id_(-1),
       weak_ptr_factory_(this) {
 }
 
@@ -163,25 +202,10 @@
 
 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
   if (current_wallpaper_)
-    return current_wallpaper_->wallpaper_info().layout;
+    return current_wallpaper_->layout();
   return WALLPAPER_LAYOUT_CENTER_CROPPED;
 }
 
-gfx::ImageSkia DesktopBackgroundController::GetCurrentWallpaperImage() {
-  if (desktop_background_mode_ != BACKGROUND_IMAGE)
-    return gfx::ImageSkia();
-  return GetWallpaper();
-}
-
-int DesktopBackgroundController::GetWallpaperIDR() const {
-  if (wallpaper_loader_.get())
-    return wallpaper_loader_->idr();
-  else if (current_wallpaper_)
-    return current_wallpaper_->wallpaper_info().idr;
-  else
-    return -1;
-}
-
 void DesktopBackgroundController::OnRootWindowAdded(
     aura::RootWindow* root_window) {
   // The background hasn't been set yet.
@@ -199,6 +223,8 @@
     if (width < root_window_size.width() ||
         height < root_window_size.height()) {
       current_wallpaper_.reset(NULL);
+      current_default_wallpaper_path_ = base::FilePath();
+      current_default_wallpaper_resource_id_ = -1;
       ash::Shell::GetInstance()->user_wallpaper_delegate()->
           UpdateWallpaper();
     }
@@ -207,34 +233,71 @@
   InstallDesktopController(root_window);
 }
 
-void DesktopBackgroundController::SetDefaultWallpaper(
-    const WallpaperInfo& info) {
-  DCHECK_NE(GetWallpaperIDR(), info.idr);
+bool DesktopBackgroundController::SetDefaultWallpaper(bool is_guest) {
+  const bool use_large =
+      GetAppropriateResolution() == WALLPAPER_RESOLUTION_LARGE;
+
+  base::FilePath file_path;
+  WallpaperLayout file_layout = use_large ? WALLPAPER_LAYOUT_CENTER_CROPPED :
+      WALLPAPER_LAYOUT_CENTER;
+  int resource_id = -1;
+  WallpaperLayout resource_layout = WALLPAPER_LAYOUT_TILE;
+
+#if defined(GOOGLE_CHROME_BUILD)
+  if (use_large) {
+    resource_id = is_guest ? IDR_AURA_WALLPAPERS_2_LANDSCAPE7_LARGE :
+        IDR_AURA_WALLPAPERS_2_LANDSCAPE8_LARGE;
+    resource_layout = WALLPAPER_LAYOUT_CENTER_CROPPED;
+  } else {
+    resource_id = is_guest ? IDR_AURA_WALLPAPERS_2_LANDSCAPE7_SMALL :
+        IDR_AURA_WALLPAPERS_2_LANDSCAPE8_SMALL;
+    resource_layout = WALLPAPER_LAYOUT_CENTER;
+  }
+#else
+  resource_id = use_large ? IDR_AURA_WALLPAPERS_5_GRADIENT5_LARGE :
+      IDR_AURA_WALLPAPERS_5_GRADIENT5_SMALL;
+  resource_layout = WALLPAPER_LAYOUT_TILE;
+#endif
+
+  const char* switch_name = is_guest ?
+      (use_large ? switches::kAshDefaultGuestWallpaperLarge :
+       switches::kAshDefaultGuestWallpaperSmall) :
+      (use_large ? switches::kAshDefaultWallpaperLarge :
+       switches::kAshDefaultWallpaperSmall);
+  CommandLine* command_line = command_line_for_testing_ ?
+      command_line_for_testing_ : CommandLine::ForCurrentProcess();
+  file_path = command_line->GetSwitchValuePath(switch_name);
+
+  if (DefaultWallpaperIsAlreadyLoadingOrLoaded(file_path, resource_id))
+    return false;
 
   CancelPendingWallpaperOperation();
-  wallpaper_loader_ = new WallpaperLoader(info);
+  wallpaper_loader_ = new WallpaperLoader(
+      file_path, file_layout, resource_id, resource_layout);
   base::WorkerPool::PostTaskAndReply(
       FROM_HERE,
       base::Bind(&WallpaperLoader::LoadOnWorkerPoolThread, wallpaper_loader_),
-      base::Bind(&DesktopBackgroundController::OnWallpaperLoadCompleted,
+      base::Bind(&DesktopBackgroundController::OnDefaultWallpaperLoadCompleted,
                  weak_ptr_factory_.GetWeakPtr(),
                  wallpaper_loader_),
       true /* task_is_slow */);
+  return true;
 }
 
 void DesktopBackgroundController::SetCustomWallpaper(
-    const gfx::ImageSkia& wallpaper,
+    const gfx::ImageSkia& image,
     WallpaperLayout layout) {
   CancelPendingWallpaperOperation();
-  if (current_wallpaper_.get() &&
-      current_wallpaper_->wallpaper_image().BackedBySameObjectAs(wallpaper)) {
+  if (CustomWallpaperIsAlreadyLoaded(image))
     return;
-  }
 
-  WallpaperInfo info = { -1, layout };
-  current_wallpaper_.reset(new WallpaperResizer(info, GetRootWindowsSize(),
-                                                wallpaper));
+  current_wallpaper_.reset(new WallpaperResizer(
+      image, GetRootWindowsSize(), layout));
   current_wallpaper_->StartResize();
+
+  current_default_wallpaper_path_ = base::FilePath();
+  current_default_wallpaper_resource_id_ = -1;
+
   FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
                     OnWallpaperDataChanged());
   SetDesktopBackgroundImageMode();
@@ -264,7 +327,6 @@
 
 WallpaperResolution DesktopBackgroundController::GetAppropriateResolution() {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  WallpaperResolution resolution = WALLPAPER_RESOLUTION_SMALL;
   Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
   for (Shell::RootWindowList::iterator iter = root_windows.begin();
        iter != root_windows.end(); ++iter) {
@@ -274,11 +336,10 @@
     // scenario. Revisit and fix if necessary.
     gfx::Size host_window_size = (*iter)->GetHostSize();
     if (host_window_size.width() > kSmallWallpaperMaxWidth ||
-        host_window_size.height() > kSmallWallpaperMaxHeight) {
-      resolution = WALLPAPER_RESOLUTION_LARGE;
-    }
+        host_window_size.height() > kSmallWallpaperMaxHeight)
+      return WALLPAPER_RESOLUTION_LARGE;
   }
-  return resolution;
+  return WALLPAPER_RESOLUTION_SMALL;
 }
 
 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
@@ -297,11 +358,20 @@
                                    GetBackgroundContainerId(false));
 }
 
-void DesktopBackgroundController::OnWindowDestroying(aura::Window* window) {
-  window->SetProperty(kDesktopController,
-      static_cast<internal::DesktopBackgroundWidgetController*>(NULL));
-  window->SetProperty(kAnimatingDesktopController,
-      static_cast<internal::AnimatingDesktopController*>(NULL));
+bool DesktopBackgroundController::DefaultWallpaperIsAlreadyLoadingOrLoaded(
+    const base::FilePath& image_file, int image_resource_id) const {
+  return (wallpaper_loader_ &&
+          wallpaper_loader_->file_path() == image_file &&
+          wallpaper_loader_->resource_id() == image_resource_id) ||
+         (current_wallpaper_.get() &&
+          current_default_wallpaper_path_ == image_file &&
+          current_default_wallpaper_resource_id_ == image_resource_id);
+}
+
+bool DesktopBackgroundController::CustomWallpaperIsAlreadyLoaded(
+    const gfx::ImageSkia& image) const {
+  return current_wallpaper_.get() &&
+      current_wallpaper_->wallpaper_image().BackedBySameObjectAs(image);
 }
 
 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
@@ -309,24 +379,21 @@
   InstallDesktopControllerForAllWindows();
 }
 
-void DesktopBackgroundController::OnWallpaperLoadCompleted(
-    scoped_refptr<WallpaperLoader> wl) {
-  current_wallpaper_.reset(wl->ReleaseWallpaperResizer());
+void DesktopBackgroundController::OnDefaultWallpaperLoadCompleted(
+    scoped_refptr<WallpaperLoader> loader) {
+  current_wallpaper_.reset(loader->ReleaseWallpaperResizer());
+  current_wallpaper_->StartResize();
+  current_default_wallpaper_path_ = loader->file_path();
+  current_default_wallpaper_resource_id_ = loader->resource_id();
   FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
                     OnWallpaperDataChanged());
 
   SetDesktopBackgroundImageMode();
 
-  DCHECK(wl.get() == wallpaper_loader_.get());
+  DCHECK(loader.get() == wallpaper_loader_.get());
   wallpaper_loader_ = NULL;
 }
 
-void DesktopBackgroundController::NotifyAnimationFinished() {
-  Shell* shell = Shell::GetInstance();
-  shell->GetPrimaryRootWindowController()->HandleDesktopBackgroundVisible();
-  shell->user_wallpaper_delegate()->OnWallpaperAnimationFinished();
-}
-
 ui::Layer* DesktopBackgroundController::SetColorLayerForContainer(
     SkColor color,
     aura::RootWindow* root_window,
@@ -336,12 +403,6 @@
 
   Shell::GetContainer(root_window,container_id)->
       layer()->Add(background_layer);
-
-  base::MessageLoop::current()->PostTask(
-      FROM_HERE,
-      base::Bind(&DesktopBackgroundController::NotifyAnimationFinished,
-                 weak_ptr_factory_.GetWeakPtr()));
-
   return background_layer;
 }
 
@@ -368,17 +429,10 @@
       NOTREACHED();
       return;
   }
-  // Ensure we're only observing the root window once. Don't rely on a window
-  // property check as those can be cleared by tests resetting the background.
-  if (!root_window->HasObserver(this))
-    root_window->AddObserver(this);
+  GetRootWindowController(root_window)->SetAnimatingWallpaperController(
+      new internal::AnimatingDesktopController(component));
 
-  internal::AnimatingDesktopController* animating_controller =
-      root_window->GetProperty(kAnimatingDesktopController);
-  if (animating_controller)
-    animating_controller->StopAnimating();
-  root_window->SetProperty(kAnimatingDesktopController,
-                           new internal::AnimatingDesktopController(component));
+  component->StartAnimating(GetRootWindowController(root_window));
 }
 
 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
@@ -392,33 +446,36 @@
 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container,
                                                             int dst_container) {
   bool moved = false;
-  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
-  for (Shell::RootWindowList::iterator iter = root_windows.begin();
-    iter != root_windows.end(); ++iter) {
-    aura::RootWindow* root_window = *iter;
+  Shell::RootWindowControllerList controllers =
+      Shell::GetAllRootWindowControllers();
+  for (Shell::RootWindowControllerList::iterator iter = controllers.begin();
+    iter != controllers.end(); ++iter) {
+    internal::RootWindowController* root_window_controller = *iter;
     // In the steady state (no animation playing) the background widget
-    // controller exists in the kDesktopController property.
-    DesktopBackgroundWidgetController* desktop_controller = root_window->
-        GetProperty(kDesktopController);
+    // controller exists in the RootWindowController.
+    DesktopBackgroundWidgetController* desktop_controller =
+        root_window_controller->wallpaper_controller();
     if (desktop_controller) {
-      moved |= desktop_controller->Reparent(root_window,
-                                            src_container,
-                                            dst_container);
+      moved |= desktop_controller->Reparent(
+          root_window_controller->root_window(),
+          src_container,
+          dst_container);
     }
     // During desktop show animations the controller lives in
-    // kAnimatingDesktopController.
+    // AnimatingDesktopController owned by RootWindowController.
     // NOTE: If a wallpaper load happens during a desktop show animation there
     // can temporarily be two desktop background widgets.  We must reparent
     // both of them - one above and one here.
     DesktopBackgroundWidgetController* animating_controller =
-        root_window->GetProperty(kAnimatingDesktopController) ?
-        root_window->GetProperty(kAnimatingDesktopController)->
+        root_window_controller->animating_wallpaper_controller() ?
+        root_window_controller->animating_wallpaper_controller()->
             GetController(false) :
         NULL;
     if (animating_controller) {
-      moved |= animating_controller->Reparent(root_window,
-                                              src_container,
-                                              dst_container);
+      moved |= animating_controller->Reparent(
+          root_window_controller->root_window(),
+          src_container,
+          dst_container);
     }
   }
   return moved;
diff --git a/ash/desktop_background/desktop_background_controller.h b/ash/desktop_background/desktop_background_controller.h
index b877898..afdc2dc 100644
--- a/ash/desktop_background/desktop_background_controller.h
+++ b/ash/desktop_background/desktop_background_controller.h
@@ -7,16 +7,18 @@
 
 #include "ash/ash_export.h"
 #include "base/basictypes.h"
+#include "base/files/file_path.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/image/image_skia.h"
 
 typedef unsigned int SkColor;
 
+class CommandLine;
+
 namespace aura {
 class RootWindow;
 }
@@ -27,9 +29,16 @@
 }  // namespace internal
 
 enum WallpaperLayout {
+  // Center the wallpaper on the desktop without scaling it. The wallpaper
+  // may be cropped.
   WALLPAPER_LAYOUT_CENTER,
+  // Scale the wallpaper (while preserving its aspect ratio) to cover the
+  // desktop; the wallpaper may be cropped.
   WALLPAPER_LAYOUT_CENTER_CROPPED,
+  // Scale the wallpaper (without preserving its aspect ratio) to match the
+  // desktop's size.
   WALLPAPER_LAYOUT_STRETCH,
+  // Tile the wallpaper over the background without scaling it.
   WALLPAPER_LAYOUT_TILE,
 };
 
@@ -40,17 +49,6 @@
 
 const SkColor kLoginWallpaperColor = 0xFEFEFE;
 
-// Encapsulates wallpaper infomation needed by desktop background controller.
-struct ASH_EXPORT WallpaperInfo {
-  int idr;
-  WallpaperLayout layout;
-};
-
-ASH_EXPORT extern const WallpaperInfo kDefaultLargeWallpaper;
-ASH_EXPORT extern const WallpaperInfo kDefaultSmallWallpaper;
-ASH_EXPORT extern const WallpaperInfo kGuestLargeWallpaper;
-ASH_EXPORT extern const WallpaperInfo kGuestSmallWallpaper;
-
 // The width and height of small/large resolution wallpaper. When screen size is
 // smaller than |kSmallWallpaperMaxWidth| and |kSmallWallpaperMaxHeight|, the
 // small resolution wallpaper should be used. Otherwise, uses the large
@@ -69,7 +67,7 @@
 
 // Loads selected desktop wallpaper from file system asynchronously and updates
 // background layer if loaded successfully.
-class ASH_EXPORT DesktopBackgroundController : public aura::WindowObserver {
+class ASH_EXPORT DesktopBackgroundController {
  public:
   enum BackgroundMode {
     BACKGROUND_NONE,
@@ -80,38 +78,36 @@
   DesktopBackgroundController();
   virtual ~DesktopBackgroundController();
 
-  // Gets the desktop background mode.
   BackgroundMode desktop_background_mode() const {
     return desktop_background_mode_;
   }
 
+  void set_command_line_for_testing(CommandLine* command_line) {
+    command_line_for_testing_ = command_line;
+  }
+
   // Add/Remove observers.
   void AddObserver(DesktopBackgroundControllerObserver* observer);
   void RemoveObserver(DesktopBackgroundControllerObserver* observer);
 
+  // Provides current image on the background, or empty gfx::ImageSkia if there
+  // is no image, e.g. background is solid color.
   gfx::ImageSkia GetWallpaper() const;
 
   WallpaperLayout GetWallpaperLayout() const;
 
-  // Provides current image on the background, or empty gfx::ImageSkia if there
-  // is no image, e.g. background is solid color.
-  gfx::ImageSkia GetCurrentWallpaperImage();
-
-  // Gets the IDR of current wallpaper. Returns -1 if current wallpaper is not
-  // a builtin wallpaper.
-  int GetWallpaperIDR() const;
-
   // Initialize root window's background.
   void OnRootWindowAdded(aura::RootWindow* root_window);
 
-  // Loads builtin wallpaper asynchronously and sets to current wallpaper after
-  // loaded.
-  void SetDefaultWallpaper(const WallpaperInfo& info);
+  // Loads builtin wallpaper asynchronously and sets to current wallpaper
+  // after loaded. Returns true if the controller started loading the
+  // wallpaper and false otherwise (i.e. the appropriate wallpaper was
+  // already loading or loaded).
+  bool SetDefaultWallpaper(bool is_guest);
 
   // Sets the user selected custom wallpaper. Called when user selected a file
   // from file system or changed the layout of wallpaper.
-  void SetCustomWallpaper(const gfx::ImageSkia& wallpaper,
-                          WallpaperLayout layout);
+  void SetCustomWallpaper(const gfx::ImageSkia& image, WallpaperLayout layout);
 
   // Cancels the current wallpaper loading operation.
   void CancelPendingWallpaperOperation();
@@ -139,22 +135,28 @@
   // Returns true if the desktop moved.
   bool MoveDesktopToUnlockedContainer();
 
-  // WindowObserver implementation.
-  virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
-
  private:
   friend class internal::DesktopBackgroundControllerTest;
 
   // An operation to asynchronously loads wallpaper.
   class WallpaperLoader;
 
+  // Returns true if the specified default wallpaper is already being
+  // loaded by |wallpaper_loader_| or stored in |current_wallpaper_|.
+  bool DefaultWallpaperIsAlreadyLoadingOrLoaded(
+      const base::FilePath& image_file, int image_resource_id) const;
+
+  // Returns true if the specified custom wallpaper is already stored
+  // in |current_wallpaper_|.
+  bool CustomWallpaperIsAlreadyLoaded(const gfx::ImageSkia& image) const;
+
   // Creates view for all root windows, or notifies them to repaint if they
   // already exist.
   void SetDesktopBackgroundImageMode();
 
   // Creates a new background widget and sets the background mode to image mode.
-  // Called after wallpaper loaded successfully.
-  void OnWallpaperLoadCompleted(scoped_refptr<WallpaperLoader> wl);
+  // Called after a default wallpaper has been loaded successfully.
+  void OnDefaultWallpaperLoadCompleted(scoped_refptr<WallpaperLoader> loader);
 
   // Adds layer with solid |color| to container |container_id| in |root_window|.
   ui::Layer* SetColorLayerForContainer(SkColor color,
@@ -179,6 +181,9 @@
   // Send notification that background animation finished.
   void NotifyAnimationFinished();
 
+  // If non-NULL, used in place of the real command line.
+  CommandLine* command_line_for_testing_;
+
   // Can change at runtime.
   bool locked_;
 
@@ -191,6 +196,12 @@
   // The current wallpaper.
   scoped_ptr<WallpaperResizer> current_wallpaper_;
 
+  // If a default wallpaper is stored in |current_wallpaper_|, the path and
+  // resource ID that were passed to WallpaperLoader when loading it.
+  // Otherwise, empty and -1, respectively.
+  base::FilePath current_default_wallpaper_path_;
+  int current_default_wallpaper_resource_id_;
+
   scoped_refptr<WallpaperLoader> wallpaper_loader_;
 
   base::WeakPtrFactory<DesktopBackgroundController> weak_ptr_factory_;
diff --git a/ash/desktop_background/desktop_background_controller_unittest.cc b/ash/desktop_background/desktop_background_controller_unittest.cc
index d513a9c..4eb9b57 100644
--- a/ash/desktop_background/desktop_background_controller_unittest.cc
+++ b/ash/desktop_background/desktop_background_controller_unittest.cc
@@ -4,17 +4,38 @@
 
 #include "ash/desktop_background/desktop_background_controller.h"
 
+#include <cmath>
+#include <cstdlib>
+
+#include "ash/ash_switches.h"
+#include "ash/desktop_background/desktop_background_controller_observer.h"
 #include "ash/desktop_background/desktop_background_widget_controller.h"
+#include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/test/display_manager_test_api.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "content/public/test/test_browser_thread.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/root_window.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/layer_animator_test_controller.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
 
 using aura::RootWindow;
 using aura::Window;
 
+namespace ash {
+namespace internal {
+
 namespace {
 
 // Containers IDs used for tests.
@@ -30,6 +51,31 @@
   return static_cast<int>(container->children().size());
 }
 
+class TestObserver : public DesktopBackgroundControllerObserver {
+ public:
+  explicit TestObserver(DesktopBackgroundController* controller)
+      : controller_(controller) {
+    DCHECK(controller_);
+    controller_->AddObserver(this);
+  }
+
+  virtual ~TestObserver() {
+    controller_->RemoveObserver(this);
+  }
+
+  void WaitForWallpaperDataChanged() {
+    base::MessageLoop::current()->Run();
+  }
+
+  // DesktopBackgroundControllerObserver overrides:
+  virtual void OnWallpaperDataChanged() OVERRIDE {
+    base::MessageLoop::current()->Quit();
+  }
+
+ private:
+  DesktopBackgroundController* controller_;
+};
+
 // Steps a widget's layer animation until it is completed. Animations must be
 // enabled.
 void RunAnimationForWidget(views::Widget* widget) {
@@ -51,25 +97,168 @@
 
 }  // namespace
 
-namespace ash {
-namespace internal {
-
 class DesktopBackgroundControllerTest : public test::AshTestBase {
  public:
-  DesktopBackgroundControllerTest() {}
+  DesktopBackgroundControllerTest()
+      : ui_thread_(content::BrowserThread::UI, base::MessageLoop::current()),
+        command_line_(CommandLine::NO_PROGRAM),
+        controller_(NULL) {
+  }
   virtual ~DesktopBackgroundControllerTest() {}
 
   virtual void SetUp() OVERRIDE {
     test::AshTestBase::SetUp();
     // Ash shell initialization creates wallpaper. Reset it so we can manually
     // control wallpaper creation and animation in our tests.
-    RootWindow* root = Shell::GetPrimaryRootWindow();
-    root->SetProperty(kDesktopController,
-        static_cast<DesktopBackgroundWidgetController*>(NULL));
-    root->SetProperty(kAnimatingDesktopController,
-        static_cast<AnimatingDesktopController*>(NULL));
+    RootWindowController* root_window_controller =
+        Shell::GetPrimaryRootWindowController();
+    root_window_controller->SetWallpaperController(NULL);
+    root_window_controller->SetAnimatingWallpaperController(NULL);
+    controller_ = Shell::GetInstance()->desktop_background_controller();
   }
 
+ protected:
+  // Colors used for different default wallpapers by
+  // WriteWallpapersAndSetFlags().
+  static const SkColor kLargeWallpaperColor = SK_ColorRED;
+  static const SkColor kSmallWallpaperColor = SK_ColorGREEN;
+  static const SkColor kLargeGuestWallpaperColor = SK_ColorBLUE;
+  static const SkColor kSmallGuestWallpaperColor = SK_ColorYELLOW;
+
+  // Dimension used for width and height of default wallpaper images. A
+  // small value is used to minimize the amount of time spent compressing
+  // and writing images.
+  static const int kWallpaperSize = 2;
+
+  // Runs kAnimatingDesktopController's animation to completion.
+  // TODO(bshe): Don't require tests to run animations; it's slow.
+  void RunDesktopControllerAnimation() {
+    DesktopBackgroundWidgetController* controller =
+        Shell::GetPrimaryRootWindowController()->
+        animating_wallpaper_controller()->GetController(false);
+    ASSERT_NO_FATAL_FAILURE(RunAnimationForWidget(controller->widget()));
+  }
+
+  // Returns true if the color at the center of |image| is close to
+  // |expected_color|. (The center is used so small wallpaper images can be
+  // used.)
+  bool ImageIsNearColor(gfx::ImageSkia image, SkColor expected_color) {
+    if (image.size().IsEmpty()) {
+      LOG(ERROR) << "Image is empty";
+      return false;
+    }
+
+    const SkBitmap* bitmap = image.bitmap();
+    if (!bitmap) {
+      LOG(ERROR) << "Unable to get bitmap from image";
+      return false;
+    }
+
+    bitmap->lockPixels();
+    gfx::Point center = gfx::Rect(image.size()).CenterPoint();
+    SkColor image_color = bitmap->getColor(center.x(), center.y());
+    bitmap->unlockPixels();
+
+    const int kDiff = 3;
+    if (std::abs(static_cast<int>(SkColorGetA(image_color)) -
+                 static_cast<int>(SkColorGetA(expected_color))) > kDiff ||
+        std::abs(static_cast<int>(SkColorGetR(image_color)) -
+                 static_cast<int>(SkColorGetR(expected_color))) > kDiff ||
+        std::abs(static_cast<int>(SkColorGetG(image_color)) -
+                 static_cast<int>(SkColorGetG(expected_color))) > kDiff ||
+        std::abs(static_cast<int>(SkColorGetB(image_color)) -
+                 static_cast<int>(SkColorGetB(expected_color))) > kDiff) {
+      LOG(ERROR) << "Expected color near 0x" << std::hex << expected_color
+                 << " but got 0x" << image_color;
+      return false;
+    }
+
+    return true;
+  }
+
+  // Writes a JPEG image of the specified size and color to |path|. Returns
+  // true on success.
+  bool WriteJPEGFile(const base::FilePath& path,
+                     int width,
+                     int height,
+                     SkColor color) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0);
+    bitmap.allocPixels();
+    bitmap.eraseColor(color);
+
+    const int kQuality = 80;
+    std::vector<unsigned char> output;
+    if (!gfx::JPEGCodec::Encode(
+            static_cast<const unsigned char*>(bitmap.getPixels()),
+            gfx::JPEGCodec::FORMAT_SkBitmap, width, height, bitmap.rowBytes(),
+            kQuality, &output)) {
+      LOG(ERROR) << "Unable to encode " << width << "x" << height << " bitmap";
+      return false;
+    }
+
+    size_t bytes_written = file_util::WriteFile(
+        path, reinterpret_cast<const char*>(&output[0]), output.size());
+    if (bytes_written != output.size()) {
+      LOG(ERROR) << "Wrote " << bytes_written << " byte(s) instead of "
+                 << output.size() << " to " << path.value();
+      return false;
+    }
+
+    return true;
+  }
+
+  // Initializes |wallpaper_dir_|, writes JPEG wallpaper images to it, and
+  // passes |controller_| a command line instructing it to use the images.
+  // Only needs to be called (once) by tests that want to test loading of
+  // default wallpapers.
+  void WriteWallpapersAndSetFlags() {
+    wallpaper_dir_.reset(new base::ScopedTempDir);
+    ASSERT_TRUE(wallpaper_dir_->CreateUniqueTempDir());
+
+    const base::FilePath kLargePath =
+        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("large.jpg"));
+    ASSERT_TRUE(WriteJPEGFile(kLargePath, kWallpaperSize, kWallpaperSize,
+                              kLargeWallpaperColor));
+    command_line_.AppendSwitchPath(
+        switches::kAshDefaultWallpaperLarge, kLargePath);
+
+    const base::FilePath kSmallPath =
+        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("small.jpg"));
+    ASSERT_TRUE(WriteJPEGFile(kSmallPath, kWallpaperSize, kWallpaperSize,
+                              kSmallWallpaperColor));
+    command_line_.AppendSwitchPath(
+        switches::kAshDefaultWallpaperSmall, kSmallPath);
+
+    const base::FilePath kLargeGuestPath =
+        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("guest_large.jpg"));
+    ASSERT_TRUE(WriteJPEGFile(kLargeGuestPath, kWallpaperSize, kWallpaperSize,
+                              kLargeGuestWallpaperColor));
+    command_line_.AppendSwitchPath(
+        switches::kAshDefaultGuestWallpaperLarge, kLargeGuestPath);
+
+    const base::FilePath kSmallGuestPath =
+        wallpaper_dir_->path().Append(FILE_PATH_LITERAL("guest_small.jpg"));
+    ASSERT_TRUE(WriteJPEGFile(kSmallGuestPath, kWallpaperSize, kWallpaperSize,
+                              kSmallGuestWallpaperColor));
+    command_line_.AppendSwitchPath(
+        switches::kAshDefaultGuestWallpaperSmall, kSmallGuestPath);
+
+    controller_->set_command_line_for_testing(&command_line_);
+  }
+
+  content::TestBrowserThread ui_thread_;
+
+  // Custom command line passed to DesktopBackgroundController by
+  // WriteWallpapersAndSetFlags().
+  CommandLine command_line_;
+
+  // Directory created by WriteWallpapersAndSetFlags() to store default
+  // wallpaper images.
+  scoped_ptr<base::ScopedTempDir> wallpaper_dir_;
+
+  DesktopBackgroundController* controller_;  // Not owned.
+
  private:
   DISALLOW_COPY_AND_ASSIGN(DesktopBackgroundControllerTest);
 };
@@ -114,24 +303,22 @@
 
   // The new wallpaper is ready to start animating. kAnimatingDesktopController
   // holds the widget controller instance. kDesktopController will get it later.
-  RootWindow* root = Shell::GetPrimaryRootWindow();
-  EXPECT_TRUE(
-      root->GetProperty(kAnimatingDesktopController)->GetController(false));
+  RootWindowController* root_window_controller =
+      Shell::GetPrimaryRootWindowController();
+  EXPECT_TRUE(root_window_controller->animating_wallpaper_controller()->
+              GetController(false));
 
   // kDesktopController will receive the widget controller when the animation
   // is done.
-  EXPECT_FALSE(root->GetProperty(kDesktopController));
+  EXPECT_FALSE(root_window_controller->wallpaper_controller());
 
   // Force the widget's layer animation to play to completion.
-  ASSERT_NO_FATAL_FAILURE(
-      RunAnimationForWidget(
-          root->GetProperty(kAnimatingDesktopController)->GetController(false)->
-              widget()));
+  RunDesktopControllerAnimation();
 
   // Ownership has moved from kAnimatingDesktopController to kDesktopController.
-  EXPECT_FALSE(
-      root->GetProperty(kAnimatingDesktopController)->GetController(false));
-  EXPECT_TRUE(root->GetProperty(kDesktopController));
+  EXPECT_FALSE(root_window_controller->animating_wallpaper_controller()->
+               GetController(false));
+  EXPECT_TRUE(root_window_controller->wallpaper_controller());
 }
 
 // Test for crbug.com/149043 "Unlock screen, no launcher appears". Ensure we
@@ -147,11 +334,7 @@
   controller->CreateEmptyWallpaper();
 
   // Run wallpaper show animation to completion.
-  RootWindow* root = Shell::GetPrimaryRootWindow();
-  ASSERT_NO_FATAL_FAILURE(
-      RunAnimationForWidget(
-          root->GetProperty(kAnimatingDesktopController)->GetController(false)->
-              widget()));
+  RunDesktopControllerAnimation();
 
   // User locks the screen, which moves the background forward.
   controller->MoveDesktopToLockedContainer();
@@ -162,9 +345,11 @@
 
   // In this state we have two desktop background views stored in different
   // properties. Both are in the lock screen background container.
-  EXPECT_TRUE(
-      root->GetProperty(kAnimatingDesktopController)->GetController(false));
-  EXPECT_TRUE(root->GetProperty(kDesktopController));
+  RootWindowController* root_window_controller =
+      Shell::GetPrimaryRootWindowController();
+  EXPECT_TRUE(root_window_controller->animating_wallpaper_controller()->
+              GetController(false));
+  EXPECT_TRUE(root_window_controller->wallpaper_controller());
   EXPECT_EQ(0, ChildCountForContainer(kDesktopBackgroundId));
   EXPECT_EQ(2, ChildCountForContainer(kLockScreenBackgroundId));
 
@@ -177,10 +362,7 @@
   EXPECT_EQ(0, ChildCountForContainer(kLockScreenBackgroundId));
 
   // Finish the new desktop background animation.
-  ASSERT_NO_FATAL_FAILURE(
-      RunAnimationForWidget(
-          root->GetProperty(kAnimatingDesktopController)->GetController(false)->
-              widget()));
+  RunDesktopControllerAnimation();
 
   // Now there is one desktop background, in the back.
   EXPECT_EQ(1, ChildCountForContainer(kDesktopBackgroundId));
@@ -200,41 +382,149 @@
   controller->CreateEmptyWallpaper();
 
   // Run wallpaper show animation to completion.
-  RootWindow* root = Shell::GetPrimaryRootWindow();
-  ASSERT_NO_FATAL_FAILURE(
-      RunAnimationForWidget(
-          root->GetProperty(kAnimatingDesktopController)->GetController(false)->
-              widget()));
+  RunDesktopControllerAnimation();
 
   // Change to a new wallpaper.
   controller->CreateEmptyWallpaper();
 
-  DesktopBackgroundWidgetController* animatingController =
-      root->GetProperty(kAnimatingDesktopController)->GetController(false);
-  EXPECT_TRUE(animatingController);
-  EXPECT_TRUE(root->GetProperty(kDesktopController));
+  RootWindowController* root_window_controller =
+      Shell::GetPrimaryRootWindowController();
+  DesktopBackgroundWidgetController* animating_controller =
+      root_window_controller->animating_wallpaper_controller()->
+      GetController(false);
+  EXPECT_TRUE(animating_controller);
+  EXPECT_TRUE(root_window_controller->wallpaper_controller());
 
   // Change to another wallpaper before animation finished.
   controller->CreateEmptyWallpaper();
 
   // The animating controller should immediately move to desktop controller.
-  EXPECT_EQ(animatingController, root->GetProperty(kDesktopController));
+  EXPECT_EQ(animating_controller,
+            root_window_controller->wallpaper_controller());
 
   // Cache the new animating controller.
-  animatingController =
-      root->GetProperty(kAnimatingDesktopController)->GetController(false);
+  animating_controller = root_window_controller->
+      animating_wallpaper_controller()->GetController(false);
 
   // Run wallpaper show animation to completion.
   ASSERT_NO_FATAL_FAILURE(
       RunAnimationForWidget(
-          root->GetProperty(kAnimatingDesktopController)->GetController(false)->
-              widget()));
+          root_window_controller->animating_wallpaper_controller()->
+              GetController(false)->widget()));
 
-  EXPECT_TRUE(root->GetProperty(kDesktopController));
-  EXPECT_FALSE(
-      root->GetProperty(kAnimatingDesktopController)->GetController(false));
+  EXPECT_TRUE(root_window_controller->wallpaper_controller());
+  EXPECT_FALSE(root_window_controller->animating_wallpaper_controller()->
+               GetController(false));
   // The desktop controller should be the last created animating controller.
-  EXPECT_EQ(animatingController, root->GetProperty(kDesktopController));
+  EXPECT_EQ(animating_controller,
+            root_window_controller->wallpaper_controller());
+}
+
+TEST_F(DesktopBackgroundControllerTest, GetAppropriateResolution) {
+  // TODO(derat|oshima|bshe): Configuring desktops seems busted on Win8,
+  // even when just a single display is being used -- the small wallpaper
+  // is used instead of the large one. Track down the cause of the problem
+  // and only use a SupportsMultipleDisplays() clause for the dual-display
+  // code below.
+  if (!SupportsMultipleDisplays())
+    return;
+
+  test::DisplayManagerTestApi display_manager_test_api(
+      Shell::GetInstance()->display_manager());
+
+  // Small wallpaper images should be used for configurations less than or
+  // equal to kSmallWallpaperMaxWidth by kSmallWallpaperMaxHeight, even if
+  // multiple displays are connected.
+  display_manager_test_api.UpdateDisplay("800x600");
+  EXPECT_EQ(WALLPAPER_RESOLUTION_SMALL,
+            controller_->GetAppropriateResolution());
+  display_manager_test_api.UpdateDisplay("800x600,800x600");
+  EXPECT_EQ(WALLPAPER_RESOLUTION_SMALL,
+            controller_->GetAppropriateResolution());
+  display_manager_test_api.UpdateDisplay("1366x800");
+  EXPECT_EQ(WALLPAPER_RESOLUTION_SMALL,
+            controller_->GetAppropriateResolution());
+
+  // At larger sizes, large wallpapers should be used.
+  display_manager_test_api.UpdateDisplay("1367x800");
+  EXPECT_EQ(WALLPAPER_RESOLUTION_LARGE,
+            controller_->GetAppropriateResolution());
+  display_manager_test_api.UpdateDisplay("1367x801");
+  EXPECT_EQ(WALLPAPER_RESOLUTION_LARGE,
+            controller_->GetAppropriateResolution());
+  display_manager_test_api.UpdateDisplay("2560x1700");
+  EXPECT_EQ(WALLPAPER_RESOLUTION_LARGE,
+            controller_->GetAppropriateResolution());
+}
+
+// Test that DesktopBackgroundController loads the appropriate wallpaper
+// images as specified via command-line flags in various situations.
+// Splitting these into separate tests avoids needing to run animations.
+// TODO(derat): Combine these into a single test -- see
+// RunDesktopControllerAnimation()'s TODO.
+TEST_F(DesktopBackgroundControllerTest, SmallDefaultWallpaper) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  WriteWallpapersAndSetFlags();
+  TestObserver observer(controller_);
+
+  // At 800x600, the small wallpaper should be loaded.
+  test::DisplayManagerTestApi display_manager_test_api(
+      Shell::GetInstance()->display_manager());
+  display_manager_test_api.UpdateDisplay("800x600");
+  ASSERT_TRUE(controller_->SetDefaultWallpaper(false));
+  observer.WaitForWallpaperDataChanged();
+  EXPECT_TRUE(ImageIsNearColor(controller_->GetWallpaper(),
+                               kSmallWallpaperColor));
+
+  // Requesting the same wallpaper again should be a no-op.
+  ASSERT_FALSE(controller_->SetDefaultWallpaper(false));
+}
+
+TEST_F(DesktopBackgroundControllerTest, LargeDefaultWallpaper) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  WriteWallpapersAndSetFlags();
+  TestObserver observer(controller_);
+  test::DisplayManagerTestApi display_manager_test_api(
+      Shell::GetInstance()->display_manager());
+  display_manager_test_api.UpdateDisplay("1600x1200");
+  ASSERT_TRUE(controller_->SetDefaultWallpaper(false));
+  observer.WaitForWallpaperDataChanged();
+  EXPECT_TRUE(ImageIsNearColor(controller_->GetWallpaper(),
+                               kLargeWallpaperColor));
+}
+
+TEST_F(DesktopBackgroundControllerTest, SmallGuestWallpaper) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  WriteWallpapersAndSetFlags();
+  TestObserver observer(controller_);
+  test::DisplayManagerTestApi display_manager_test_api(
+      Shell::GetInstance()->display_manager());
+  display_manager_test_api.UpdateDisplay("800x600");
+  ASSERT_TRUE(controller_->SetDefaultWallpaper(true));
+  observer.WaitForWallpaperDataChanged();
+  EXPECT_TRUE(ImageIsNearColor(controller_->GetWallpaper(),
+                               kSmallGuestWallpaperColor));
+}
+
+TEST_F(DesktopBackgroundControllerTest, LargeGuestWallpaper) {
+  if (!SupportsMultipleDisplays())
+    return;
+
+  WriteWallpapersAndSetFlags();
+  TestObserver observer(controller_);
+  test::DisplayManagerTestApi display_manager_test_api(
+      Shell::GetInstance()->display_manager());
+  display_manager_test_api.UpdateDisplay("1600x1200");
+  ASSERT_TRUE(controller_->SetDefaultWallpaper(true));
+  observer.WaitForWallpaperDataChanged();
+  EXPECT_TRUE(ImageIsNearColor(controller_->GetWallpaper(),
+                               kLargeGuestWallpaperColor));
 }
 
 }  // namespace internal
diff --git a/ash/desktop_background/desktop_background_view.cc b/ash/desktop_background/desktop_background_view.cc
index e27a183..daaa6b4 100644
--- a/ash/desktop_background/desktop_background_view.cc
+++ b/ash/desktop_background/desktop_background_view.cc
@@ -21,80 +21,14 @@
 #include "ui/aura/root_window.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animation_observer.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/image.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_observer.h"
 
 namespace ash {
 namespace internal {
 namespace {
 
-class ShowWallpaperAnimationObserver : public ui::ImplicitAnimationObserver,
-                                       public views::WidgetObserver {
- public:
-  ShowWallpaperAnimationObserver(aura::RootWindow* root_window,
-                                 views::Widget* desktop_widget,
-                                 bool is_initial_animation)
-      : root_window_(root_window),
-        desktop_widget_(desktop_widget),
-        is_initial_animation_(is_initial_animation) {
-    DCHECK(desktop_widget_);
-    desktop_widget_->AddObserver(this);
-  }
-
-  virtual ~ShowWallpaperAnimationObserver() {
-    StopObservingImplicitAnimations();
-    if (desktop_widget_)
-      desktop_widget_->RemoveObserver(this);
-  }
-
- private:
-  // Overridden from ui::ImplicitAnimationObserver:
-  virtual void OnImplicitAnimationsScheduled() OVERRIDE {
-    if (is_initial_animation_) {
-      GetRootWindowController(root_window_)->
-          HandleInitialDesktopBackgroundAnimationStarted();
-    }
-  }
-
-  virtual void OnImplicitAnimationsCompleted() OVERRIDE {
-    GetRootWindowController(root_window_)->HandleDesktopBackgroundVisible();
-    ash::Shell::GetInstance()->user_wallpaper_delegate()->
-        OnWallpaperAnimationFinished();
-    // Only removes old component when wallpaper animation finished. If we
-    // remove the old one before the new wallpaper is done fading in there will
-    // be a white flash during the animation.
-    if (root_window_->GetProperty(kAnimatingDesktopController)) {
-      DesktopBackgroundWidgetController* controller =
-          root_window_->GetProperty(kAnimatingDesktopController)->
-              GetController(true);
-      // |desktop_widget_| should be the same animating widget we try to move
-      // to |kDesktopController|. Otherwise, we may close |desktop_widget_|
-      // before move it to |kDesktopController|.
-      DCHECK_EQ(controller->widget(), desktop_widget_);
-      // Release the old controller and close its background widget.
-      root_window_->SetProperty(kDesktopController, controller);
-    }
-    delete this;
-  }
-
-  // Overridden from views::WidgetObserver.
-  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
-    delete this;
-  }
-
-  aura::RootWindow* root_window_;
-  views::Widget* desktop_widget_;
-
-  // Is this object observing the initial brightness/grayscale animation?
-  const bool is_initial_animation_;
-
-  DISALLOW_COPY_AND_ASSIGN(ShowWallpaperAnimationObserver);
-};
-
 // For our scaling ratios we need to round positive numbers.
 int RoundPositive(double x) {
   return static_cast<int>(floor(x + 0.5));
@@ -188,7 +122,7 @@
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   if (controller->GetWallpaper().isNull())
-    params.transparent = true;
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.parent = root_window->GetChildById(container_id);
   desktop_widget->Init(params);
   desktop_widget->SetContentsView(new DesktopBackgroundView());
@@ -196,13 +130,16 @@
   views::corewm::SetWindowVisibilityAnimationType(
       desktop_widget->GetNativeView(), animation_type);
 
+  RootWindowController* root_window_controller =
+      GetRootWindowController(root_window);
+
   // Enable wallpaper transition for the following cases:
   // 1. Initial(OOBE) wallpaper animation.
   // 2. Wallpaper fades in from a non empty background.
   // 3. From an empty background, chrome transit to a logged in user session.
   // 4. From an empty background, guest user logged in.
   if (wallpaper_delegate->ShouldShowInitialAnimation() ||
-      root_window->GetProperty(kAnimatingDesktopController) ||
+      root_window_controller->animating_wallpaper_controller() ||
       Shell::GetInstance()->session_state_delegate()->NumberOfLoggedInUsers()) {
     views::corewm::SetWindowVisibilityAnimationTransition(
         desktop_widget->GetNativeView(), views::corewm::ANIMATE_SHOW);
@@ -213,14 +150,6 @@
   }
 
   desktop_widget->SetBounds(params.parent->bounds());
-  ui::ScopedLayerAnimationSettings settings(
-      desktop_widget->GetNativeView()->layer()->GetAnimator());
-  settings.SetPreemptionStrategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
-  settings.AddObserver(new ShowWallpaperAnimationObserver(
-      root_window, desktop_widget,
-      wallpaper_delegate->ShouldShowInitialAnimation()));
-  desktop_widget->Show();
-  desktop_widget->GetNativeView()->SetName("DesktopBackgroundView");
   return desktop_widget;
 }
 
diff --git a/ash/desktop_background/desktop_background_widget_controller.cc b/ash/desktop_background/desktop_background_widget_controller.cc
index 98d1ba4..cbd7727 100644
--- a/ash/desktop_background/desktop_background_widget_controller.cc
+++ b/ash/desktop_background/desktop_background_widget_controller.cc
@@ -5,23 +5,67 @@
 #include "ash/desktop_background/desktop_background_widget_controller.h"
 
 #include "ash/ash_export.h"
+#include "ash/desktop_background/user_wallpaper_delegate.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ui/aura/root_window.h"
-#include "ui/aura/window_property.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/views/widget/widget.h"
-
-// Exported for tests.
-DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(
-    ASH_EXPORT, ash::internal::DesktopBackgroundWidgetController*);
-DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(
-    ASH_EXPORT, ash::internal::AnimatingDesktopController*);
+#include "ui/views/widget/widget_observer.h"
 
 namespace ash {
 namespace internal {
+namespace {
 
-DEFINE_OWNED_WINDOW_PROPERTY_KEY(DesktopBackgroundWidgetController,
-                                 kDesktopController, NULL);
-DEFINE_OWNED_WINDOW_PROPERTY_KEY(AnimatingDesktopController,
-                                 kAnimatingDesktopController, NULL);
+class ShowWallpaperAnimationObserver : public ui::ImplicitAnimationObserver,
+                                       public views::WidgetObserver {
+ public:
+  ShowWallpaperAnimationObserver(RootWindowController* root_window_controller,
+                                 views::Widget* desktop_widget,
+                                 bool is_initial_animation)
+      : root_window_controller_(root_window_controller),
+        desktop_widget_(desktop_widget),
+        is_initial_animation_(is_initial_animation) {
+    DCHECK(desktop_widget_);
+    desktop_widget_->AddObserver(this);
+  }
+
+  virtual ~ShowWallpaperAnimationObserver() {
+    StopObservingImplicitAnimations();
+    if (desktop_widget_)
+      desktop_widget_->RemoveObserver(this);
+  }
+
+ private:
+  // Overridden from ui::ImplicitAnimationObserver:
+  virtual void OnImplicitAnimationsScheduled() OVERRIDE {
+    if (is_initial_animation_) {
+      root_window_controller_->
+          HandleInitialDesktopBackgroundAnimationStarted();
+    }
+  }
+
+  virtual void OnImplicitAnimationsCompleted() OVERRIDE {
+    root_window_controller_->OnWallpaperAnimationFinished(desktop_widget_);
+    delete this;
+  }
+
+  // Overridden from views::WidgetObserver.
+  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
+    delete this;
+  }
+
+  RootWindowController* root_window_controller_;
+  views::Widget* desktop_widget_;
+
+  // Is this object observing the initial brightness/grayscale animation?
+  const bool is_initial_animation_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShowWallpaperAnimationObserver);
+};
+
+}  // namespace
 
 DesktopBackgroundWidgetController::DesktopBackgroundWidgetController(
     views::Widget* widget) : widget_(widget) {
@@ -63,8 +107,7 @@
     views::Widget::ReparentNativeView(widget_->GetNativeView(),
         root_window->GetChildById(dest_container));
     return true;
-  }
-  if (layer_) {
+  } else if (layer_) {
     ui::Layer* layer = layer_.get();
     root_window->GetChildById(src_container)->layer()->Remove(layer);
     root_window->GetChildById(dest_container)->layer()->Add(layer);
@@ -74,6 +117,22 @@
   return false;
 }
 
+void DesktopBackgroundWidgetController::StartAnimating(
+    RootWindowController* root_window_controller) {
+  if (widget_) {
+    ui::ScopedLayerAnimationSettings settings(
+        widget_->GetNativeView()->layer()->GetAnimator());
+    settings.SetPreemptionStrategy(ui::LayerAnimator::ENQUEUE_NEW_ANIMATION);
+    settings.AddObserver(new ShowWallpaperAnimationObserver(
+        root_window_controller, widget_,
+        Shell::GetInstance()->user_wallpaper_delegate()->
+            ShouldShowInitialAnimation()));
+    widget_->Show();
+    widget_->GetNativeView()->SetName("DesktopBackgroundView");
+  } else if (layer_)
+    root_window_controller->OnWallpaperAnimationFinished(NULL);
+}
+
 AnimatingDesktopController::AnimatingDesktopController(
     DesktopBackgroundWidgetController* component) {
   controller_.reset(component);
diff --git a/ash/desktop_background/desktop_background_widget_controller.h b/ash/desktop_background/desktop_background_widget_controller.h
index bf94619..8aa3449 100644
--- a/ash/desktop_background/desktop_background_widget_controller.h
+++ b/ash/desktop_background/desktop_background_widget_controller.h
@@ -14,14 +14,13 @@
 
 namespace ash {
 namespace internal {
+class RootWindowController;
 
 // This class hides difference between two possible background implementations:
 // effective Layer-based for solid color, and Widget-based for images.
-// DesktopBackgroundWidgetController is installed as an owned property on the
-// RootWindow. To avoid a white flash during wallpaper changes the old
-// DesktopBackgroundWidgetController is moved to a secondary property
-// (kComponentWrapper). When the animation completes the old
-// DesktopBackgroundWidgetController is destroyed. Exported for tests.
+// DesktopBackgroundWidgetController is owned by RootWindowController.
+// When the animation completes the old DesktopBackgroundWidgetController is
+// destroyed. Exported for tests.
 class ASH_EXPORT DesktopBackgroundWidgetController
     : public views::WidgetObserver {
  public:
@@ -44,6 +43,11 @@
                 int src_container,
                 int dest_container);
 
+  // Starts wallpaper fade in animation. |root_window_controller| is
+  // the root window where the animation will happen. (This is
+  // necessary this as |layer_| doesn't have access to the root window).
+  void StartAnimating(RootWindowController* root_window_controller);
+
   views::Widget* widget() { return widget_; }
   ui::Layer* layer() { return layer_.get(); }
 
@@ -54,11 +58,10 @@
   DISALLOW_COPY_AND_ASSIGN(DesktopBackgroundWidgetController);
 };
 
-// This class wraps a DesktopBackgroundWidgetController pointer. It is installed
-// as an owned property on the RootWindow. DesktopBackgroundWidgetController is
-// moved to this property before animation completes. After animation completes,
-// the kDesktopController property on RootWindow is set to the
-// DesktopBackgroundWidgetController in this class. Exported for tests.
+// This class wraps a DesktopBackgroundWidgetController pointer. It is owned
+// by RootWindowController. The instance of DesktopBackgroundWidgetController is
+// moved to this RootWindowController when the animation completes.
+// Exported for tests.
 class ASH_EXPORT AnimatingDesktopController {
  public:
   explicit AnimatingDesktopController(
@@ -90,17 +93,6 @@
   DISALLOW_COPY_AND_ASSIGN(AnimatingDesktopController);
 };
 
-// Window property key, that binds instance of DesktopBackgroundWidgetController
-// to root windows.  Owned property.
-ASH_EXPORT extern
-    const aura::WindowProperty<DesktopBackgroundWidgetController*>* const
-        kDesktopController;
-
-// Wrapper for the DesktopBackgroundWidgetController for a desktop background
-// that is animating in.  Owned property.
-ASH_EXPORT extern const aura::WindowProperty<AnimatingDesktopController*>* const
-    kAnimatingDesktopController;
-
 }  // namespace internal
 }  // namespace ash
 
diff --git a/ash/desktop_background/wallpaper_resizer.cc b/ash/desktop_background/wallpaper_resizer.cc
index c7e1dda..b75e262 100644
--- a/ash/desktop_background/wallpaper_resizer.cc
+++ b/ash/desktop_background/wallpaper_resizer.cc
@@ -18,91 +18,95 @@
 namespace ash {
 namespace {
 
-// Callback used to indicate that wallpaper has been resized.
-typedef base::Callback<void(const SkBitmap&)> ResizedCallback;
-
 // For our scaling ratios we need to round positive numbers.
 int RoundPositive(double x) {
   return static_cast<int>(floor(x + 0.5));
 }
 
-// Resizes |wallpaper| to |target_size| and calls the callback.
-void Resize(const SkBitmap& wallpaper,
-            WallpaperLayout layout,
+// Resizes |orig_bitmap| to |target_size| using |layout| and stores the
+// resulting bitmap at |resized_bitmap_out|.
+void Resize(SkBitmap orig_bitmap,
             const gfx::Size& target_size,
-            base::MessageLoop* origin_loop,
-            const ResizedCallback& callback) {
-  SkBitmap resized_wallpaper = wallpaper;
-  int width = target_size.width();
-  int height = target_size.height();
-  if (wallpaper.width() > width || wallpaper.height() > height) {
-    gfx::Rect wallpaper_rect(0, 0, wallpaper.width(), wallpaper.height());
-    gfx::Size cropped_size = gfx::Size(std::min(width, wallpaper.width()),
-                                       std::min(height, wallpaper.height()));
+            WallpaperLayout layout,
+            SkBitmap* resized_bitmap_out) {
+  DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+  SkBitmap new_bitmap = orig_bitmap;
+
+  const int orig_width = orig_bitmap.width();
+  const int orig_height = orig_bitmap.height();
+  const int new_width = target_size.width();
+  const int new_height = target_size.height();
+
+  if (orig_width > new_width || orig_height > new_height) {
+    gfx::Rect wallpaper_rect(0, 0, orig_width, orig_height);
+    gfx::Size cropped_size = gfx::Size(std::min(new_width, orig_width),
+                                       std::min(new_height, orig_height));
     switch (layout) {
       case WALLPAPER_LAYOUT_CENTER:
         wallpaper_rect.ClampToCenteredSize(cropped_size);
-        wallpaper.extractSubset(&resized_wallpaper,
-                                gfx::RectToSkIRect(wallpaper_rect));
+        orig_bitmap.extractSubset(&new_bitmap,
+                                  gfx::RectToSkIRect(wallpaper_rect));
         break;
       case WALLPAPER_LAYOUT_TILE:
         wallpaper_rect.set_size(cropped_size);
-        wallpaper.extractSubset(&resized_wallpaper,
-                                gfx::RectToSkIRect(wallpaper_rect));
+        orig_bitmap.extractSubset(&new_bitmap,
+                                  gfx::RectToSkIRect(wallpaper_rect));
         break;
       case WALLPAPER_LAYOUT_STRETCH:
-        resized_wallpaper = skia::ImageOperations::Resize(
-            wallpaper, skia::ImageOperations::RESIZE_LANCZOS3,
-            width, height);
+        new_bitmap = skia::ImageOperations::Resize(
+            orig_bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
+            new_width, new_height);
         break;
       case WALLPAPER_LAYOUT_CENTER_CROPPED:
-        if (wallpaper.width() > width && wallpaper.height() > height) {
+        if (orig_width > new_width && orig_height > new_height) {
           // The dimension with the smallest ratio must be cropped, the other
           // one is preserved. Both are set in gfx::Size cropped_size.
-          double horizontal_ratio = static_cast<double>(width) /
-              static_cast<double>(wallpaper.width());
-          double vertical_ratio = static_cast<double>(height) /
-              static_cast<double>(wallpaper.height());
+          double horizontal_ratio = static_cast<double>(new_width) /
+              static_cast<double>(orig_width);
+          double vertical_ratio = static_cast<double>(new_height) /
+              static_cast<double>(orig_height);
 
           if (vertical_ratio > horizontal_ratio) {
             cropped_size = gfx::Size(
-                RoundPositive(static_cast<double>(width) / vertical_ratio),
-                wallpaper.height());
+                RoundPositive(static_cast<double>(new_width) / vertical_ratio),
+                orig_height);
           } else {
-            cropped_size = gfx::Size(wallpaper.width(),
-                RoundPositive(static_cast<double>(height) / horizontal_ratio));
+            cropped_size = gfx::Size(orig_width, RoundPositive(
+                static_cast<double>(new_height) / horizontal_ratio));
           }
           wallpaper_rect.ClampToCenteredSize(cropped_size);
           SkBitmap sub_image;
-          wallpaper.extractSubset(&sub_image,
-                                  gfx::RectToSkIRect(wallpaper_rect));
-          resized_wallpaper = skia::ImageOperations::Resize(
+          orig_bitmap.extractSubset(&sub_image,
+                                    gfx::RectToSkIRect(wallpaper_rect));
+          new_bitmap = skia::ImageOperations::Resize(
               sub_image, skia::ImageOperations::RESIZE_LANCZOS3,
-              width, height);
+              new_width, new_height);
         }
     }
   }
-  resized_wallpaper.setImmutable();
-  origin_loop->PostTask(FROM_HERE, base::Bind(callback, resized_wallpaper));
+
+  *resized_bitmap_out = new_bitmap;
+  resized_bitmap_out->setImmutable();
 }
 
 }  // namespace
 
-WallpaperResizer::WallpaperResizer(const WallpaperInfo& info,
-                                   const gfx::Size& target_size)
-    : wallpaper_info_(info),
+WallpaperResizer::WallpaperResizer(int image_resource_id,
+                                   const gfx::Size& target_size,
+                                   WallpaperLayout layout)
+    : wallpaper_image_(*(ui::ResourceBundle::GetSharedInstance().
+          GetImageNamed(image_resource_id).ToImageSkia())),
       target_size_(target_size),
-      wallpaper_image_(*(ui::ResourceBundle::GetSharedInstance().
-          GetImageNamed(info.idr).ToImageSkia())),
+      layout_(layout),
       weak_ptr_factory_(this) {
 }
 
-WallpaperResizer::WallpaperResizer(const WallpaperInfo& info,
+WallpaperResizer::WallpaperResizer(const gfx::ImageSkia& image,
                                    const gfx::Size& target_size,
-                                   const gfx::ImageSkia& image)
-    : wallpaper_info_(info),
+                                   WallpaperLayout layout)
+    : wallpaper_image_(image),
       target_size_(target_size),
-      wallpaper_image_(image),
+      layout_(layout),
       weak_ptr_factory_(this) {
 }
 
@@ -110,18 +114,17 @@
 }
 
 void WallpaperResizer::StartResize() {
-  if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  SkBitmap* resized_bitmap = new SkBitmap;
+  if (!content::BrowserThread::PostBlockingPoolTaskAndReply(
           FROM_HERE,
-          base::Bind(&Resize,
-                     *wallpaper_image_.bitmap(),
-                     wallpaper_info_.layout,
-                     target_size_,
-                     base::MessageLoop::current(),
-                     base::Bind(&WallpaperResizer::OnResizeFinished,
-                                weak_ptr_factory_.GetWeakPtr())),
-          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)) {
-    LOG(WARNING) << "PostSequencedWorkerTask failed. " <<
-                    "Wallpaper may not be resized.";
+          base::Bind(&Resize, *wallpaper_image_.bitmap(), target_size_,
+                     layout_, resized_bitmap),
+          base::Bind(&WallpaperResizer::OnResizeFinished,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     base::Owned(resized_bitmap)))) {
+    LOG(WARNING) << "PostSequencedWorkerTask failed. "
+                 << "Wallpaper may not be resized.";
   }
 }
 
@@ -133,8 +136,9 @@
   observers_.RemoveObserver(observer);
 }
 
-void WallpaperResizer::OnResizeFinished(const SkBitmap& resized_wallpaper) {
-  wallpaper_image_ = gfx::ImageSkia::CreateFrom1xBitmap(resized_wallpaper);
+void WallpaperResizer::OnResizeFinished(SkBitmap* resized_bitmap) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  wallpaper_image_ = gfx::ImageSkia::CreateFrom1xBitmap(*resized_bitmap);
   FOR_EACH_OBSERVER(WallpaperResizerObserver, observers_,
                     OnWallpaperResized());
 }
diff --git a/ash/desktop_background/wallpaper_resizer.h b/ash/desktop_background/wallpaper_resizer.h
index 9b47b7c..e39dcd5 100644
--- a/ash/desktop_background/wallpaper_resizer.h
+++ b/ash/desktop_background/wallpaper_resizer.h
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/size.h"
 
@@ -20,18 +21,21 @@
 // Stores the current wallpaper data and resize it to |target_size| if needed.
 class ASH_EXPORT WallpaperResizer {
  public:
-  WallpaperResizer(const WallpaperInfo& info, const gfx::Size& target_size);
+  WallpaperResizer(int image_resource_id,
+                   const gfx::Size& target_size,
+                   WallpaperLayout layout);
 
-  WallpaperResizer(const WallpaperInfo& info, const gfx::Size& target_size,
-                   const gfx::ImageSkia& image);
+  WallpaperResizer(const gfx::ImageSkia& image,
+                   const gfx::Size& target_size,
+                   WallpaperLayout layout);
 
-  virtual ~WallpaperResizer();
-
-  const WallpaperInfo& wallpaper_info() const { return wallpaper_info_; }
+  ~WallpaperResizer();
 
   const gfx::ImageSkia& wallpaper_image() const { return wallpaper_image_; }
+  const WallpaperLayout layout() const { return layout_; }
 
-  // Starts resize task on UI thread. It posts task to worker pool.
+  // Called on the UI thread to run Resize() on the worker pool and post an
+  // OnResizeFinished() task back to the UI thread on completion.
   void StartResize();
 
   // Add/Remove observers.
@@ -39,16 +43,20 @@
   void RemoveObserver(WallpaperResizerObserver* observer);
 
  private:
-  // Replaces the orginal uncompressed wallpaper to |resized_wallpaper|.
-  void OnResizeFinished(const SkBitmap& resized_wallpaper);
+  // Copies |resized_bitmap| to |wallpaper_image_| and notifies observers
+  // after Resize() has finished running.
+  void OnResizeFinished(SkBitmap* resized_bitmap);
 
   ObserverList<WallpaperResizerObserver> observers_;
 
-  const WallpaperInfo wallpaper_info_;
+  // Image that should currently be used for wallpaper. It initially
+  // contains the original image and is updated to contain the resized
+  // image by OnResizeFinished().
+  gfx::ImageSkia wallpaper_image_;
 
   gfx::Size target_size_;
 
-  gfx::ImageSkia wallpaper_image_;
+  WallpaperLayout layout_;
 
   base::WeakPtrFactory<WallpaperResizer> weak_ptr_factory_;
 
diff --git a/ash/desktop_background/wallpaper_resizer_unittest.cc b/ash/desktop_background/wallpaper_resizer_unittest.cc
index 0093ade..5761b67 100644
--- a/ash/desktop_background/wallpaper_resizer_unittest.cc
+++ b/ash/desktop_background/wallpaper_resizer_unittest.cc
@@ -5,7 +5,9 @@
 #include "ash/desktop_background/wallpaper_resizer.h"
 
 #include "ash/desktop_background/wallpaper_resizer_observer.h"
-#include "ash/test/ash_test_base.h"
+#include "base/message_loop/message_loop.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/image/image_skia_rep.h"
 
 using aura::RootWindow;
@@ -55,17 +57,19 @@
 namespace ash {
 namespace internal {
 
-class WallpaperResizerTest : public test::AshTestBase,
+class WallpaperResizerTest : public testing::Test,
                              public WallpaperResizerObserver {
  public:
-  WallpaperResizerTest() {}
+  WallpaperResizerTest()
+      : ui_thread_(content::BrowserThread::UI, &message_loop_) {
+  }
   virtual ~WallpaperResizerTest() {}
 
-  gfx::ImageSkia Resize(const WallpaperInfo& info,
+  gfx::ImageSkia Resize(const gfx::ImageSkia& image,
                         const gfx::Size& target_size,
-                        const gfx::ImageSkia& image) {
+                        WallpaperLayout layout) {
     scoped_ptr<WallpaperResizer> resizer;
-    resizer.reset(new WallpaperResizer(info, target_size, image));
+    resizer.reset(new WallpaperResizer(image, target_size, layout));
     resizer->AddObserver(this);
     resizer->StartResize();
     WaitForResize();
@@ -74,14 +78,17 @@
   }
 
   void WaitForResize() {
-    base::MessageLoop::current()->Run();
+    message_loop_.Run();
   }
 
   virtual void OnWallpaperResized() OVERRIDE {
-    base::MessageLoop::current()->Quit();
+    message_loop_.Quit();
   }
 
  private:
+  base::MessageLoop message_loop_;
+  content::TestBrowserThread ui_thread_;
+
   DISALLOW_COPY_AND_ASSIGN(WallpaperResizerTest);
 };
 
@@ -97,19 +104,18 @@
 
   for (int i = 0; i < length; i++) {
     WallpaperLayout layout = layouts[i];
-    WallpaperInfo info = { 0, layout };
     gfx::ImageSkia small_image(gfx::ImageSkiaRep(gfx::Size(10, 20),
                                                  ui::SCALE_FACTOR_100P));
 
-    gfx::ImageSkia resized_small = Resize(info, gfx::Size(800, 600),
-                                          small_image);
+    gfx::ImageSkia resized_small = Resize(small_image, gfx::Size(800, 600),
+                                          layout);
     EXPECT_EQ(10, resized_small.width());
     EXPECT_EQ(20, resized_small.height());
 
     gfx::ImageSkia large_image(gfx::ImageSkiaRep(gfx::Size(1000, 1000),
                                                  ui::SCALE_FACTOR_100P));
-    gfx::ImageSkia resized_large = Resize(info, gfx::Size(800, 600),
-                                          large_image);
+    gfx::ImageSkia resized_large = Resize(large_image, gfx::Size(800, 600),
+                                          layout);
     EXPECT_EQ(800, resized_large.width());
     EXPECT_EQ(600, resized_large.height());
   }
@@ -122,18 +128,14 @@
       gfx::Size(kTestImageWidth, kTestImageHeight));
 
   gfx::Size target_size = gfx::Size(kTargetWidth, kTargetHeight);
-  WallpaperInfo info_center = { 0, WALLPAPER_LAYOUT_CENTER };
-  gfx::ImageSkia center = Resize(info_center, target_size, image);
+  gfx::ImageSkia center = Resize(image, target_size, WALLPAPER_LAYOUT_CENTER);
 
-  WallpaperInfo info_center_cropped = { 0, WALLPAPER_LAYOUT_CENTER_CROPPED };
-  gfx::ImageSkia center_cropped = Resize(info_center_cropped, target_size,
-                                         image);
+  gfx::ImageSkia center_cropped = Resize(image, target_size,
+                                         WALLPAPER_LAYOUT_CENTER_CROPPED);
 
-  WallpaperInfo info_stretch = { 0, WALLPAPER_LAYOUT_STRETCH };
-  gfx::ImageSkia stretch = Resize(info_stretch, target_size, image);
+  gfx::ImageSkia stretch = Resize(image, target_size, WALLPAPER_LAYOUT_STRETCH);
 
-  WallpaperInfo info_tile = { 0, WALLPAPER_LAYOUT_TILE };
-  gfx::ImageSkia tile = Resize(info_tile, target_size, image);
+  gfx::ImageSkia tile = Resize(image, target_size, WALLPAPER_LAYOUT_TILE);
 
   EXPECT_TRUE(IsColor(center, kExpectedCenter));
   EXPECT_TRUE(IsColor(center_cropped, kExpectedCenterCropped));
diff --git a/ash/display/display_controller.cc b/ash/display/display_controller.cc
index bc1aeff..e8186db 100644
--- a/ash/display/display_controller.cc
+++ b/ash/display/display_controller.cc
@@ -10,7 +10,6 @@
 
 #include "ash/ash_switches.h"
 #include "ash/display/display_manager.h"
-#include "ash/display/display_pref_util.h"
 #include "ash/display/root_window_transformers.h"
 #include "ash/host/root_window_host_factory.h"
 #include "ash/root_window_controller.h"
@@ -20,18 +19,13 @@
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
-#include "base/json/json_value_converter.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
-#include "base/values.h"
 #include "third_party/skia/include/utils/SkMatrix44.h"
 #include "ui/aura/client/activation_client.h"
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/screen_position_client.h"
-#include "ui/aura/env.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/root_window_transformer.h"
 #include "ui/aura/window.h"
@@ -44,7 +38,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "base/chromeos/chromeos_version.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #if defined(USE_X11)
 #include "ash/display/output_configurator_animation.h"
 #include "chromeos/display/output_configurator.h"
@@ -70,14 +64,6 @@
 // ash::Shell:: is deleted.
 int num_displays_for_shutdown = -1;
 
-// The maximum value for 'offset' in DisplayLayout in case of outliers.  Need
-// to change this value in case to support even larger displays.
-const int kMaxValidOffset = 10000;
-
-// The number of pixels to overlap between the primary and secondary displays,
-// in case that the offset value is too large.
-const int kMinimumOverlapForInvalidOffset = 100;
-
 // Specifies how long the display change should have been disabled
 // after each display change operations.
 // |kCycleDisplayThrottleTimeoutMs| is set to be longer to avoid
@@ -88,41 +74,6 @@
 const int64 kCycleDisplayThrottleTimeoutMs = 4000;
 const int64 kSwapDisplayThrottleTimeoutMs = 500;
 
-// Persistent key names
-const char kPositionKey[] = "position";
-const char kOffsetKey[] = "offset";
-const char kMirroredKey[] = "mirrored";
-const char kPrimaryIdKey[] = "primary-id";
-
-typedef std::map<DisplayLayout::Position, std::string> PositionToStringMap;
-
-const PositionToStringMap* GetPositionToStringMap() {
-  static const PositionToStringMap* map = CreateToStringMap(
-      DisplayLayout::TOP, "top",
-      DisplayLayout::BOTTOM, "bottom",
-      DisplayLayout::RIGHT, "right",
-      DisplayLayout::LEFT, "left");
-  return map;
-}
-
-bool GetPositionFromString(const base::StringPiece& position,
-                           DisplayLayout::Position* field) {
-  if (ReverseFind(GetPositionToStringMap(), position, field))
-    return true;
-  LOG(ERROR) << "Invalid position value:" << position;
-  return false;
-}
-
-std::string GetStringFromPosition(DisplayLayout::Position position) {
-  const PositionToStringMap* map = GetPositionToStringMap();
-  PositionToStringMap::const_iterator iter = map->find(position);
-  return iter != map->end() ? iter->second : std::string("unknown");
-}
-
-bool GetDisplayIdFromString(const base::StringPiece& position, int64* field) {
-  return base::StringToInt64(position, field);
-}
-
 internal::DisplayManager* GetDisplayManager() {
   return Shell::GetInstance()->display_manager();
 }
@@ -243,98 +194,6 @@
 }  // namespace internal
 
 ////////////////////////////////////////////////////////////////////////////////
-// DisplayLayout
-
-// static
-DisplayLayout DisplayLayout::FromInts(int position, int offsets) {
-  return DisplayLayout(static_cast<Position>(position), offsets);
-}
-
-DisplayLayout::DisplayLayout()
-    : position(RIGHT),
-      offset(0),
-      mirrored(false),
-      primary_id(gfx::Display::kInvalidDisplayID) {
-}
-
-DisplayLayout::DisplayLayout(DisplayLayout::Position position, int offset)
-    : position(position),
-      offset(offset),
-      mirrored(false),
-      primary_id(gfx::Display::kInvalidDisplayID) {
-  DCHECK_LE(TOP, position);
-  DCHECK_GE(LEFT, position);
-
-  // Set the default value to |position| in case position is invalid.  DCHECKs
-  // above doesn't stop in Release builds.
-  if (TOP > position || LEFT < position)
-    this->position = RIGHT;
-
-  DCHECK_GE(kMaxValidOffset, abs(offset));
-}
-
-DisplayLayout DisplayLayout::Invert() const {
-  Position inverted_position = RIGHT;
-  switch (position) {
-    case TOP:
-      inverted_position = BOTTOM;
-      break;
-    case BOTTOM:
-      inverted_position = TOP;
-      break;
-    case RIGHT:
-      inverted_position = LEFT;
-      break;
-    case LEFT:
-      inverted_position = RIGHT;
-      break;
-  }
-  DisplayLayout ret = DisplayLayout(inverted_position, -offset);
-  ret.primary_id = primary_id;
-  return ret;
-}
-
-// static
-bool DisplayLayout::ConvertFromValue(const base::Value& value,
-                                     DisplayLayout* layout) {
-  base::JSONValueConverter<DisplayLayout> converter;
-  return converter.Convert(value, layout);
-}
-
-// static
-bool DisplayLayout::ConvertToValue(const DisplayLayout& layout,
-                                   base::Value* value) {
-  base::DictionaryValue* dict_value = NULL;
-  if (!value->GetAsDictionary(&dict_value) || dict_value == NULL)
-    return false;
-
-  const std::string position_str = GetStringFromPosition(layout.position);
-  dict_value->SetString(kPositionKey, position_str);
-  dict_value->SetInteger(kOffsetKey, layout.offset);
-  dict_value->SetBoolean(kMirroredKey, layout.mirrored);
-  dict_value->SetString(kPrimaryIdKey, base::Int64ToString(layout.primary_id));
-  return true;
-}
-
-std::string DisplayLayout::ToString() const {
-  const std::string position_str = GetStringFromPosition(position);
-  return base::StringPrintf(
-      "%s, %d%s",
-      position_str.c_str(), offset, mirrored ? ", mirrored" : "");
-}
-
-// static
-void DisplayLayout::RegisterJSONConverter(
-    base::JSONValueConverter<DisplayLayout>* converter) {
-  converter->RegisterCustomField<Position>(
-      kPositionKey, &DisplayLayout::position, &GetPositionFromString);
-  converter->RegisterIntField(kOffsetKey, &DisplayLayout::offset);
-  converter->RegisterBoolField(kMirroredKey, &DisplayLayout::mirrored);
-  converter->RegisterCustomField<int64>(
-      kPrimaryIdKey, &DisplayLayout::primary_id, &GetDisplayIdFromString);
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // DisplayChangeLimiter
 
 DisplayController::DisplayChangeLimiter::DisplayChangeLimiter()
@@ -441,11 +300,11 @@
       GetDisplayManager()->GetPrimaryDisplayCandidate();
   primary_display_id = primary_candidate->id();
   AddRootWindowForDisplay(*primary_candidate);
-  UpdateDisplayBoundsForLayout();
 }
 
 void DisplayController::InitSecondaryDisplays() {
   internal::DisplayManager* display_manager = GetDisplayManager();
+  UpdateDisplayBoundsForLayout();
   for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
     const gfx::Display* display = display_manager->GetDisplayAt(i);
     if (primary_display_id != display->id()) {
@@ -454,13 +313,13 @@
     }
   }
   if (display_manager->GetNumDisplays() > 1) {
-    UpdateDisplayBoundsForLayout();
     DisplayIdPair pair = GetCurrentDisplayIdPair();
     DisplayLayout layout = GetCurrentDisplayLayout();
     SetPrimaryDisplayId(
         layout.primary_id == gfx::Display::kInvalidDisplayID ?
         pair.first : layout.primary_id);
   }
+  UpdateHostWindowNames();
 }
 
 void DisplayController::AddObserver(Observer* observer) {
@@ -517,10 +376,6 @@
   GetDisplayManager()->SetOverscanInsets(display_id, insets_in_dip);
 }
 
-void DisplayController::ClearCustomOverscanInsets(int64 display_id) {
-  GetDisplayManager()->ClearCustomOverscanInsets(display_id);
-}
-
 std::vector<internal::RootWindowController*>
 DisplayController::GetAllRootWindowControllers() {
   std::vector<internal::RootWindowController*> controllers;
@@ -547,20 +402,6 @@
   RegisterLayoutForDisplayIdPairInternal(id1, id2, layout, true);
 }
 
-void DisplayController::RegisterLayoutForDisplayId(
-    int64 id,
-    const DisplayLayout& layout) {
-  int64 first_id = gfx::Display::InternalDisplayId();
-  if (first_id == gfx::Display::kInvalidDisplayID)
-    first_id = GetDisplayManager()->first_display_id();
-  // Caveat: This doesn't work if the machine booted with
-  // no display.
-  // Ignore if the layout was registered for the internal or
-  // 1st display.
-  if (first_id != id)
-    RegisterLayoutForDisplayIdPairInternal(first_id, id, layout, false);
-}
-
 void DisplayController::SetLayoutForCurrentDisplays(
     const DisplayLayout& layout_relative_to_primary) {
   DCHECK_EQ(2U, GetDisplayManager()->GetNumDisplays());
@@ -578,24 +419,23 @@
     to_set.primary_id = primary.id();
     paired_layouts_[pair] = to_set;
     NotifyDisplayConfigurationChanging();
+    // TODO(oshima): Call UpdateDisplays instead.
     UpdateDisplayBoundsForLayout();
     NotifyDisplayConfigurationChanged();
   }
 }
 
-DisplayLayout DisplayController::GetCurrentDisplayLayout() const {
+DisplayLayout DisplayController::GetCurrentDisplayLayout() {
   DCHECK_EQ(2U, GetDisplayManager()->num_connected_displays());
   // Invert if the primary was swapped.
   if (GetDisplayManager()->num_connected_displays() > 1) {
     DisplayIdPair pair = GetCurrentDisplayIdPair();
-    DisplayLayout layout = GetRegisteredDisplayLayout(pair);
-    const gfx::Display& primary = GetPrimaryDisplay();
-    // Invert if the primary was swapped. If mirrored, first is always
-    // primary.
-    return pair.first == primary.id() ? layout : layout.Invert();
+    return ComputeDisplayLayoutForDisplayIdPair(pair);
   }
   // On release build, just fallback to default instead of blowing up.
-  return default_display_layout_;
+  DisplayLayout layout = default_display_layout_;
+  layout.primary_id = primary_display_id;
+  return layout;
 }
 
 DisplayIdPair DisplayController::GetCurrentDisplayIdPair() const {
@@ -617,10 +457,11 @@
 }
 
 DisplayLayout DisplayController::GetRegisteredDisplayLayout(
-    const DisplayIdPair& pair) const {
+    const DisplayIdPair& pair) {
   std::map<DisplayIdPair, DisplayLayout>::const_iterator iter =
       paired_layouts_.find(pair);
-  return iter != paired_layouts_.end() ? iter->second : default_display_layout_;
+  return
+      iter != paired_layouts_.end() ? iter->second : CreateDisplayLayout(pair);
 }
 
 void DisplayController::ToggleMirrorMode() {
@@ -820,22 +661,15 @@
 }
 
 void DisplayController::OnDisplayBoundsChanged(const gfx::Display& display) {
-  if (limiter_)
-    limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
   const internal::DisplayInfo& display_info =
       GetDisplayManager()->GetDisplayInfo(display.id());
   DCHECK(!display_info.bounds_in_pixel().IsEmpty());
-
-  UpdateDisplayBoundsForLayout();
   aura::RootWindow* root = root_windows_[display.id()];
-  SetDisplayPropertiesOnHostWindow(root, display);
   root->SetHostBounds(display_info.bounds_in_pixel());
+  SetDisplayPropertiesOnHostWindow(root, display);
 }
 
 void DisplayController::OnDisplayAdded(const gfx::Display& display) {
-  if (limiter_)
-    limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
-
   if (primary_root_window_for_replace_) {
     DCHECK(root_windows_.empty());
     primary_display_id = display.id();
@@ -843,7 +677,6 @@
     primary_root_window_for_replace_->SetProperty(
         internal::kDisplayIdKey, display.id());
     primary_root_window_for_replace_ = NULL;
-    UpdateDisplayBoundsForLayout();
     const internal::DisplayInfo& display_info =
         GetDisplayManager()->GetDisplayInfo(display.id());
     root_windows_[display.id()]->SetHostBounds(
@@ -853,15 +686,11 @@
       primary_display_id = display.id();
     DCHECK(!root_windows_.empty());
     aura::RootWindow* root = AddRootWindowForDisplay(display);
-    UpdateDisplayBoundsForLayout();
     Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root);
   }
 }
 
 void DisplayController::OnDisplayRemoved(const gfx::Display& display) {
-  if (limiter_)
-    limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
-
   aura::RootWindow* root_to_delete = root_windows_[display.id()];
   DCHECK(root_to_delete) << display.ToString();
 
@@ -941,49 +770,11 @@
       GetDisplayManager()->num_connected_displays() < 2) {
     return;
   }
-
   DCHECK_EQ(2, Shell::GetScreen()->GetNumDisplays());
-  const gfx::Rect& primary_bounds = GetPrimaryDisplay().bounds();
-
-  gfx::Display* secondary_display = GetSecondaryDisplay();
-  const gfx::Rect& secondary_bounds = secondary_display->bounds();
-  gfx::Point new_secondary_origin = primary_bounds.origin();
 
   const DisplayLayout layout = GetCurrentDisplayLayout();
-  DisplayLayout::Position position = layout.position;
-
-  // Ignore the offset in case the secondary display doesn't share edges with
-  // the primary display.
-  int offset = layout.offset;
-  if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) {
-    offset = std::min(
-        offset, primary_bounds.width() - kMinimumOverlapForInvalidOffset);
-    offset = std::max(
-        offset, -secondary_bounds.width() + kMinimumOverlapForInvalidOffset);
-  } else {
-    offset = std::min(
-        offset, primary_bounds.height() - kMinimumOverlapForInvalidOffset);
-    offset = std::max(
-        offset, -secondary_bounds.height() + kMinimumOverlapForInvalidOffset);
-  }
-  switch (position) {
-    case DisplayLayout::TOP:
-      new_secondary_origin.Offset(offset, -secondary_bounds.height());
-      break;
-    case DisplayLayout::RIGHT:
-      new_secondary_origin.Offset(primary_bounds.width(), offset);
-      break;
-    case DisplayLayout::BOTTOM:
-      new_secondary_origin.Offset(offset, primary_bounds.height());
-      break;
-    case DisplayLayout::LEFT:
-      new_secondary_origin.Offset(-secondary_bounds.width(), offset);
-      break;
-  }
-  gfx::Insets insets = secondary_display->GetWorkAreaInsets();
-  secondary_display->set_bounds(
-      gfx::Rect(new_secondary_origin, secondary_bounds.size()));
-  secondary_display->UpdateWorkAreaFromInsets(insets);
+  Shell::GetInstance()->display_manager()->UpdateDisplayBoundsForLayout(
+      layout, GetPrimaryDisplay(), GetSecondaryDisplay());
 }
 
 void DisplayController::NotifyDisplayConfigurationChanging() {
@@ -996,13 +787,17 @@
 void DisplayController::NotifyDisplayConfigurationChanged() {
   if (in_bootstrap())
     return;
+
+  if (limiter_)
+    limiter_->SetThrottleTimeout(kAfterDisplayChangeThrottleTimeoutMs);
+
   focus_activation_store_->Restore();
 
   internal::DisplayManager* display_manager = GetDisplayManager();
   if (display_manager->num_connected_displays() > 1) {
     DisplayIdPair pair = GetCurrentDisplayIdPair();
     if (paired_layouts_.find(pair) == paired_layouts_.end())
-      paired_layouts_[pair] = default_display_layout_;
+      CreateDisplayLayout(pair);
     paired_layouts_[pair].mirrored = display_manager->IsMirrored();
     if (Shell::GetScreen()->GetNumDisplays() > 1 ) {
       int64 primary_id = paired_layouts_[pair].primary_id;
@@ -1017,6 +812,7 @@
     }
   }
   FOR_EACH_OBSERVER(Observer, observers_, OnDisplayConfigurationChanged());
+  UpdateHostWindowNames();
 }
 
 void DisplayController::RegisterLayoutForDisplayIdPairInternal(
@@ -1036,4 +832,40 @@
 #endif
 }
 
+DisplayLayout DisplayController::ComputeDisplayLayoutForDisplayIdPair(
+    const DisplayIdPair& pair) {
+  DisplayLayout layout = GetRegisteredDisplayLayout(pair);
+  int64 primary_id = layout.primary_id;
+  // TODO(oshima): replace this with DCHECK.
+  if (primary_id == gfx::Display::kInvalidDisplayID)
+    primary_id = GetPrimaryDisplay().id();
+  // Invert if the primary was swapped. If mirrored, first is always
+  // primary.
+  return pair.first == primary_id ? layout : layout.Invert();
+}
+
+void DisplayController::UpdateHostWindowNames() {
+#if defined(USE_X11)
+  // crbug.com/120229 - set the window title for the primary dislpay
+  // to "aura_root_0" so gtalk can find the primary root window to broadcast.
+  // TODO(jhorwich) Remove this once Chrome supports window-based broadcasting.
+  aura::RootWindow* primary = Shell::GetPrimaryRootWindow();
+  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+  for (size_t i = 0; i < root_windows.size(); ++i) {
+    std::string name =
+        root_windows[i] == primary ? "aura_root_0" : "aura_root_x";
+    gfx::AcceleratedWidget xwindow = root_windows[i]->GetAcceleratedWidget();
+    XStoreName(ui::GetXDisplay(), xwindow, name.c_str());
+  }
+#endif
+}
+
+DisplayLayout DisplayController::CreateDisplayLayout(
+    const DisplayIdPair& pair) {
+  DisplayLayout layout = default_display_layout_;
+  layout.primary_id = pair.first;
+  paired_layouts_[pair] = layout;
+  return layout;
+}
+
 }  // namespace ash
diff --git a/ash/display/display_controller.h b/ash/display/display_controller.h
index ca441cb..299be14 100644
--- a/ash/display/display_controller.h
+++ b/ash/display/display_controller.h
@@ -9,14 +9,14 @@
 #include <vector>
 
 #include "ash/ash_export.h"
+#include "ash/display/display_layout.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/gfx/display_observer.h"
-#include "ui/gfx/display.h"
 
 namespace aura {
 class Display;
@@ -28,61 +28,23 @@
 template <typename T> class JSONValueConverter;
 }
 
+namespace gfx {
+class Display;
+class Insets;
+class Point;
+}
+
 namespace ash {
 namespace internal {
 class DisplayManager;
 class FocusActivationStore;
+class MirrorWindowController;
 class RootWindowController;
 }
 
-typedef std::pair<int64, int64> DisplayIdPair;
-
-struct ASH_EXPORT DisplayLayout {
-  // Layout options where the secondary display should be positioned.
-  enum Position {
-    TOP,
-    RIGHT,
-    BOTTOM,
-    LEFT
-  };
-  // Factory method to create DisplayLayout from ints. The |mirrored| is
-  // set to false and |primary_id| is set to gfx::Display::kInvalidDisplayId.
-  // Used for persistence and webui.
-  static DisplayLayout FromInts(int position, int offsets);
-
-  DisplayLayout();
-  DisplayLayout(Position position, int offset);
-
-  // Returns an inverted display layout.
-  DisplayLayout Invert() const WARN_UNUSED_RESULT;
-
-  // Converter functions to/from base::Value.
-  static bool ConvertFromValue(const base::Value& value, DisplayLayout* layout);
-  static bool ConvertToValue(const DisplayLayout& layout, base::Value* value);
-
-  // This method is used by base::JSONValueConverter, you don't need to call
-  // this directly. Instead consider using converter functions above.
-  static void RegisterJSONConverter(
-      base::JSONValueConverter<DisplayLayout>* converter);
-
-  Position position;
-
-  // The offset of the position of the secondary display.  The offset is
-  // based on the top/left edge of the primary display.
-  int offset;
-
-  // True if displays are mirrored.
-  bool mirrored;
-
-  // The id of the display used as a primary display.
-  int64 primary_id;
-
-  // Returns string representation of the layout for debugging/testing.
-  std::string ToString() const;
-};
-
 // DisplayController owns and maintains RootWindows for each attached
 // display, keeping them in sync with display configuration changes.
+// TODO(oshima): Factor out the layout registration class.
 class ASH_EXPORT DisplayController : public gfx::DisplayObserver {
  public:
   class ASH_EXPORT Observer {
@@ -164,7 +126,6 @@
   // display_manager.h for the details.
   gfx::Insets GetOverscanInsets(int64 display_id) const;
   void SetOverscanInsets(int64 display_id, const gfx::Insets& insets_in_dip);
-  void ClearCustomOverscanInsets(int64 display_id);
 
   const DisplayLayout& default_display_layout() const {
     return default_display_layout_;
@@ -175,22 +136,20 @@
   void RegisterLayoutForDisplayIdPair(int64 id1,
                                       int64 id2,
                                       const DisplayLayout& layout);
-  // OBSOLETE
-  // TODO(oshima): Remove this in m28.
-  void RegisterLayoutForDisplayId(int64 id, const DisplayLayout& layout);
-
   // Sets the layout for the current display pair. The |layout| specifies
   // the locaion of the secondary display relative to the primary.
   void SetLayoutForCurrentDisplays(const DisplayLayout& layout);
 
   // Returns the display layout used for current displays.
-  DisplayLayout GetCurrentDisplayLayout() const;
+  DisplayLayout GetCurrentDisplayLayout();
 
   // Returns the current display pair.
   DisplayIdPair GetCurrentDisplayIdPair() const;
 
   // Returns the display layout registered for the given display id |pair|.
-  DisplayLayout GetRegisteredDisplayLayout(const DisplayIdPair& pair) const;
+  // If no layout is registered, it creatas new layout using
+  // |default_display_layout_|.
+  DisplayLayout GetRegisteredDisplayLayout(const DisplayIdPair& pair);
 
   // Checks if the mouse pointer is on one of displays, and moves to
   // the center of the nearest display if it's outside of all displays.
@@ -210,6 +169,7 @@
 
  private:
   friend class internal::DisplayManager;
+  friend class internal::MirrorWindowController;
 
   // Creates a root window for |display| and stores it in the |root_windows_|
   // map.
@@ -231,6 +191,17 @@
 
   void OnFadeOutForSwapDisplayFinished();
 
+  // Returns the display layout for the display id pair
+  // with display swapping applied.  That is, this returns
+  // flipped layout if the displays are swapped.
+  DisplayLayout ComputeDisplayLayoutForDisplayIdPair(
+      const DisplayIdPair& display_pair);
+
+  void UpdateHostWindowNames();
+
+  // Creates new layout for display pair from |default_display_layout_|.
+  DisplayLayout CreateDisplayLayout(const DisplayIdPair& display_pair);
+
   bool in_bootstrap() const { return in_bootstrap_; }
 
   class DisplayChangeLimiter {
diff --git a/ash/display/display_controller_unittest.cc b/ash/display/display_controller_unittest.cc
index 7b41a51..d126c98 100644
--- a/ash/display/display_controller_unittest.cc
+++ b/ash/display/display_controller_unittest.cc
@@ -21,6 +21,12 @@
 #include "ui/gfx/screen.h"
 #include "ui/views/widget/widget.h"
 
+#if defined(USE_X11)
+#include "ui/base/x/x11_util.h"
+#include <X11/Xlib.h>
+#undef RootWindow
+#endif
+
 namespace ash {
 namespace test {
 namespace {
@@ -113,8 +119,11 @@
   virtual ~TestEventHandler() {}
 
   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
-    if (event->flags() & ui::EF_IS_SYNTHESIZED)
+    if (event->flags() & ui::EF_IS_SYNTHESIZED &&
+        event->type() != ui::ET_MOUSE_EXITED &&
+        event->type() != ui::ET_MOUSE_ENTERED) {
       return;
+    }
     aura::Window* target = static_cast<aura::Window*>(event->target());
     mouse_location_ = event->root_location();
     target_root_ = target->GetRootWindow();
@@ -184,6 +193,21 @@
   return Shell::GetInstance()->display_manager()->GetDisplayInfo(id).ui_scale();
 }
 
+#if defined(USE_X11)
+void GetPrimaryAndSeconary(aura::RootWindow** primary,
+                           aura::RootWindow** secondary) {
+  *primary = Shell::GetPrimaryRootWindow();
+  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+  *secondary = root_windows[0] == *primary ? root_windows[1] : root_windows[0];
+}
+
+std::string GetXWindowName(aura::RootWindow* window) {
+  char* name = NULL;
+  XFetchName(ui::GetXDisplay(), window->GetAcceleratedWidget(), &name);
+  return std::string(name);
+}
+#endif
+
 }  // namespace
 
 typedef test::AshTestBase DisplayControllerTest;
@@ -721,7 +745,7 @@
   generator.MoveMouseToInHost(20, 25);
   EXPECT_EQ("5,15", event_handler.GetLocationAndReset());
 
-  display_controller->ClearCustomOverscanInsets(display1.id());
+  display_controller->SetOverscanInsets(display1.id(), gfx::Insets());
   EXPECT_EQ("0,0 120x200", root_windows[0]->bounds().ToString());
   EXPECT_EQ("120,0 150x200",
             ScreenAsh::GetSecondaryDisplay().bounds().ToString());
@@ -729,6 +753,19 @@
   generator.MoveMouseToInHost(30, 20);
   EXPECT_EQ("30,20", event_handler.GetLocationAndReset());
 
+  // Make sure the root window transformer uses correct scale
+  // factor when swapping display. Test crbug.com/253690.
+  UpdateDisplay("400x300*2,600x400/o");
+  root_windows = Shell::GetAllRootWindows();
+  gfx::Point point;
+  Shell::GetAllRootWindows()[1]->GetRootTransform().TransformPoint(point);
+  EXPECT_EQ("15,10", point.ToString());
+
+  display_controller->SwapPrimaryDisplay();
+  point.SetPoint(0, 0);
+  Shell::GetAllRootWindows()[1]->GetRootTransform().TransformPoint(point);
+  EXPECT_EQ("15,10", point.ToString());
+
   Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
 }
 
@@ -783,6 +820,7 @@
   EXPECT_EQ(gfx::Display::ROTATE_90, GetStoredRotation(display1.id()));
   EXPECT_EQ(gfx::Display::ROTATE_270, GetStoredRotation(display2_id));
 
+#if !defined(OS_WIN)
   aura::test::EventGenerator generator2(root_windows[1]);
   generator2.MoveMouseToInHost(50, 40);
   EXPECT_EQ("179,25", event_handler.GetLocationAndReset());
@@ -799,6 +837,7 @@
 
   generator1.MoveMouseToInHost(50, 40);
   EXPECT_EQ("69,159", event_handler.GetLocationAndReset());
+#endif
 
   Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
 }
@@ -933,5 +972,29 @@
   Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
 }
 
+#if defined(USE_X11)
+TEST_F(DisplayControllerTest, XWidowNameForRootWindow) {
+  EXPECT_EQ("aura_root_0", GetXWindowName(Shell::GetPrimaryRootWindow()));
+
+  // Multiple display.
+  UpdateDisplay("200x200,300x300");
+  aura::RootWindow* primary, *secondary;
+  GetPrimaryAndSeconary(&primary, &secondary);
+  EXPECT_EQ("aura_root_0", GetXWindowName(primary));
+  EXPECT_EQ("aura_root_x", GetXWindowName(secondary));
+
+  // Swap primary.
+  primary = secondary = NULL;
+  Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
+  GetPrimaryAndSeconary(&primary, &secondary);
+  EXPECT_EQ("aura_root_0", GetXWindowName(primary));
+  EXPECT_EQ("aura_root_x", GetXWindowName(secondary));
+
+  // Switching back to single display.
+  UpdateDisplay("300x400");
+  EXPECT_EQ("aura_root_0", GetXWindowName(Shell::GetPrimaryRootWindow()));
+}
+#endif
+
 }  // namespace test
 }  // namespace ash
diff --git a/ash/display/display_info.cc b/ash/display/display_info.cc
index 446a0ca..a177000 100644
--- a/ash/display/display_info.cc
+++ b/ash/display/display_info.cc
@@ -40,10 +40,10 @@
   static int64 synthesized_display_id = 2200000000LL;
 
 #if defined(OS_WIN)
-  gfx::Rect bounds(aura::RootWindowHost::GetNativeScreenSize());
+  gfx::Rect bounds_in_pixel(aura::RootWindowHost::GetNativeScreenSize());
 #else
-  gfx::Rect bounds(kDefaultHostWindowX, kDefaultHostWindowY,
-                   kDefaultHostWindowWidth, kDefaultHostWindowHeight);
+  gfx::Rect bounds_in_pixel(kDefaultHostWindowX, kDefaultHostWindowY,
+                            kDefaultHostWindowWidth, kDefaultHostWindowHeight);
 #endif
   std::string main_spec = spec;
 
@@ -89,7 +89,7 @@
              &width, &height, &device_scale_factor) >= 2 ||
       sscanf(main_spec.c_str(), "%d+%d-%dx%d*%f", &x, &y, &width, &height,
              &device_scale_factor) >= 4) {
-    bounds.SetRect(x, y, width, height);
+    bounds_in_pixel.SetRect(x, y, width, height);
   }
   if (id == gfx::Display::kInvalidDisplayID)
     id = synthesized_display_id++;
@@ -98,7 +98,16 @@
   display_info.set_device_scale_factor(device_scale_factor);
   display_info.set_rotation(rotation);
   display_info.set_ui_scale(ui_scale);
-  display_info.SetBounds(bounds);
+  display_info.SetBounds(bounds_in_pixel);
+
+  // To test the overscan, it creates the default 5% overscan.
+  if (has_overscan) {
+    int width = bounds_in_pixel.width() / device_scale_factor / 40;
+    int height = bounds_in_pixel.height() / device_scale_factor / 40;
+    display_info.SetOverscanInsets(gfx::Insets(height, width, height, width));
+    display_info.UpdateDisplaySize();
+  }
+
   DVLOG(1) << "DisplayInfoFromSpec info=" << display_info.ToString()
            << ", spec=" << spec;
   return display_info;
@@ -110,7 +119,6 @@
       rotation_(gfx::Display::ROTATE_0),
       device_scale_factor_(1.0f),
       overscan_insets_in_dip_(0, 0, 0, 0),
-      has_custom_overscan_insets_(false),
       ui_scale_(1.0f),
       native_(false) {
 }
@@ -124,7 +132,6 @@
       rotation_(gfx::Display::ROTATE_0),
       device_scale_factor_(1.0f),
       overscan_insets_in_dip_(0, 0, 0, 0),
-      has_custom_overscan_insets_(false),
       ui_scale_(1.0f),
       native_(false) {
 }
@@ -142,6 +149,13 @@
   size_in_pixel_ = native_info.size_in_pixel_;
   device_scale_factor_ = native_info.device_scale_factor_;
 
+  // Copy overscan_insets_in_dip_ if it's not empty. This is for test
+  // cases which use "/o" annotation which sets the overscan inset
+  // to native, and that overscan has to be propagated. This does not
+  // happen on the real environment.
+  if (!native_info.overscan_insets_in_dip_.empty())
+    overscan_insets_in_dip_ = native_info.overscan_insets_in_dip_;
+
   // Rotation_ and ui_scale_ are given by preference, or unit
   // tests. Don't copy if this native_info came from
   // DisplayChangeObserverX11.
@@ -162,21 +176,10 @@
 
 void DisplayInfo::UpdateDisplaySize() {
   size_in_pixel_ = bounds_in_pixel_.size();
-  if (has_custom_overscan_insets_) {
+  if (!overscan_insets_in_dip_.empty()) {
     gfx::Insets insets_in_pixel =
         overscan_insets_in_dip_.Scale(device_scale_factor_);
     size_in_pixel_.Enlarge(-insets_in_pixel.width(), -insets_in_pixel.height());
-  } else if (has_overscan_) {
-    // Currently we assume 5% overscan and hope for the best if TV claims it
-    // overscan, but doesn't expose how much.
-    // TODO(oshima): The insets has to be applied after rotation.
-    // Fix this.
-    int width = bounds_in_pixel_.width() / 40;
-    int height = bounds_in_pixel_.height() / 40;
-    gfx::Insets insets_in_pixel(height, width, height, width);
-    overscan_insets_in_dip_ =
-        insets_in_pixel.Scale(1.0 / device_scale_factor_);
-    size_in_pixel_.Enlarge(-insets_in_pixel.width(), -insets_in_pixel.height());
   } else {
     overscan_insets_in_dip_.Set(0, 0, 0, 0);
   }
@@ -189,9 +192,7 @@
   size_in_pixel_ = gfx::ToFlooredSize(size_f);
 }
 
-void DisplayInfo::SetOverscanInsets(bool custom,
-                                    const gfx::Insets& insets_in_dip) {
-  has_custom_overscan_insets_ = custom;
+void DisplayInfo::SetOverscanInsets(const gfx::Insets& insets_in_dip) {
   overscan_insets_in_dip_ = insets_in_dip;
 }
 
diff --git a/ash/display/display_info.h b/ash/display/display_info.h
index e89c7c4..a679a73 100644
--- a/ash/display/display_info.h
+++ b/ash/display/display_info.h
@@ -66,7 +66,8 @@
   // The name of the display.
   const std::string& name() const { return name_; }
 
-  // True if the display has overscan.
+  // True if the display EDID has the overscan flag. This does not create the
+  // actual overscan automatically, but used in the message.
   bool has_overscan() const { return has_overscan_; }
 
   void set_rotation(gfx::Display::Rotation rotation) { rotation_ = rotation; }
@@ -94,8 +95,7 @@
   void set_ui_scale(float scale) { ui_scale_ = scale; }
 
   // Copy the display info except for fields that can be modified by a user
-  // (|has_custom_overscan_insets_| and |custom_overscan_insets_in_dip_|,
-  //  |rotation_| and |ui_scale_|). |rotation_| and |ui_scale_| are copied
+  // (|rotation_| and |ui_scale_|). |rotation_| and |ui_scale_| are copied
   // when the |another_info| isn't native one.
   void Copy(const DisplayInfo& another_info);
 
@@ -105,28 +105,12 @@
 
   // Update the |bounds_in_pixel| according to the current overscan
   // and rotation settings.
-  // 1) If this has custom overscan insets
-  //    (i.e. |has_custom_overscan_insets_| is true), it simply applies
-  //    the existing |overscan_insets_in_dip_|.
-  // 2) If this doesn't have custom overscan insets but the display claims
-  //    that it has overscan (|has_overscan_| is true), then updates
-  //    |overscan_insets_in_dip_| to default value (5% of the display size)
-  //    and apply the insets.
-  // 3) Otherwise, clear the overscan insets.
   void UpdateDisplaySize();
 
   // Sets/Clears the overscan insets.
-  void SetOverscanInsets(bool custom,
-                         const gfx::Insets& insets_in_dip);
+  void SetOverscanInsets(const gfx::Insets& insets_in_dip);
   gfx::Insets GetOverscanInsetsInPixel() const;
 
-  void clear_has_custom_overscan_insets() {
-    has_custom_overscan_insets_ = false;
-  }
-  bool has_custom_overscan_insets() const {
-    return has_custom_overscan_insets_;
-  }
-
   void set_native(bool native) { native_ = native; }
   bool native() const { return native_; }
 
@@ -134,12 +118,6 @@
   std::string ToString() const;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(DisplayManagerTest, AutomaticOverscanInsets);
-  // Set the overscan flag. Used for test.
-  void set_has_overscan_for_test(bool has_overscan) {
-    has_overscan_ = has_overscan;
-  }
-
   int64 id_;
   std::string name_;
   bool has_overscan_;
@@ -151,10 +129,6 @@
   gfx::Size size_in_pixel_;
   gfx::Insets overscan_insets_in_dip_;
 
-  // True if the |overscan_insets_in_dip| is specified by a user. This
-  // is used not to override the insets by native insets.
-  bool has_custom_overscan_insets_;
-
   // UI scale of the display.
   float ui_scale_;
 
diff --git a/ash/display/display_info_unittest.cc b/ash/display/display_info_unittest.cc
index 6806915..d425046 100644
--- a/ash/display/display_info_unittest.cc
+++ b/ash/display/display_info_unittest.cc
@@ -22,19 +22,19 @@
 
   info = DisplayInfo::CreateFromSpecWithID("10+20-300x400*2/o", 10);
   EXPECT_EQ("10,20 300x400", info.bounds_in_pixel().ToString());
-  EXPECT_EQ("286x380", info.size_in_pixel().ToString());
+  EXPECT_EQ("288x380", info.size_in_pixel().ToString());
   EXPECT_EQ(gfx::Display::ROTATE_0, info.rotation());
   EXPECT_EQ("5,3,5,3", info.overscan_insets_in_dip().ToString());
 
   info = DisplayInfo::CreateFromSpecWithID("10+20-300x400*2/ob", 10);
   EXPECT_EQ("10,20 300x400", info.bounds_in_pixel().ToString());
-  EXPECT_EQ("286x380", info.size_in_pixel().ToString());
+  EXPECT_EQ("288x380", info.size_in_pixel().ToString());
   EXPECT_EQ(gfx::Display::ROTATE_0, info.rotation());
   EXPECT_EQ("5,3,5,3", info.overscan_insets_in_dip().ToString());
 
   info = DisplayInfo::CreateFromSpecWithID("10+20-300x400*2/or", 10);
   EXPECT_EQ("10,20 300x400", info.bounds_in_pixel().ToString());
-  EXPECT_EQ("380x286", info.size_in_pixel().ToString());
+  EXPECT_EQ("380x288", info.size_in_pixel().ToString());
   EXPECT_EQ(gfx::Display::ROTATE_90, info.rotation());
   // TODO(oshima): This should be rotated too. Fix this.
   EXPECT_EQ("5,3,5,3", info.overscan_insets_in_dip().ToString());
diff --git a/ash/display/display_layout.cc b/ash/display/display_layout.cc
new file mode 100644
index 0000000..a733e76
--- /dev/null
+++ b/ash/display/display_layout.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 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/display/display_layout.h"
+
+#include "ash/display/display_pref_util.h"
+#include "base/json/json_value_converter.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "ui/gfx/display.h"
+
+namespace ash {
+namespace  {
+
+// The maximum value for 'offset' in DisplayLayout in case of outliers.  Need
+// to change this value in case to support even larger displays.
+const int kMaxValidOffset = 10000;
+
+// Persistent key names
+const char kPositionKey[] = "position";
+const char kOffsetKey[] = "offset";
+const char kMirroredKey[] = "mirrored";
+const char kPrimaryIdKey[] = "primary-id";
+
+typedef std::map<DisplayLayout::Position, std::string> PositionToStringMap;
+
+const PositionToStringMap* GetPositionToStringMap() {
+  static const PositionToStringMap* map = CreateToStringMap(
+      DisplayLayout::TOP, "top",
+      DisplayLayout::BOTTOM, "bottom",
+      DisplayLayout::RIGHT, "right",
+      DisplayLayout::LEFT, "left");
+  return map;
+}
+
+bool GetPositionFromString(const base::StringPiece& position,
+                           DisplayLayout::Position* field) {
+  if (ReverseFind(GetPositionToStringMap(), position, field))
+    return true;
+  LOG(ERROR) << "Invalid position value:" << position;
+  return false;
+}
+
+std::string GetStringFromPosition(DisplayLayout::Position position) {
+  const PositionToStringMap* map = GetPositionToStringMap();
+  PositionToStringMap::const_iterator iter = map->find(position);
+  return iter != map->end() ? iter->second : std::string("unknown");
+}
+
+bool GetDisplayIdFromString(const base::StringPiece& position, int64* field) {
+  return base::StringToInt64(position, field);
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// DisplayLayout
+
+// static
+DisplayLayout DisplayLayout::FromInts(int position, int offsets) {
+  return DisplayLayout(static_cast<Position>(position), offsets);
+}
+
+DisplayLayout::DisplayLayout()
+    : position(RIGHT),
+      offset(0),
+      mirrored(false),
+      primary_id(gfx::Display::kInvalidDisplayID) {
+}
+
+DisplayLayout::DisplayLayout(DisplayLayout::Position position, int offset)
+    : position(position),
+      offset(offset),
+      mirrored(false),
+      primary_id(gfx::Display::kInvalidDisplayID) {
+  DCHECK_LE(TOP, position);
+  DCHECK_GE(LEFT, position);
+
+  // Set the default value to |position| in case position is invalid.  DCHECKs
+  // above doesn't stop in Release builds.
+  if (TOP > position || LEFT < position)
+    this->position = RIGHT;
+
+  DCHECK_GE(kMaxValidOffset, abs(offset));
+}
+
+DisplayLayout DisplayLayout::Invert() const {
+  Position inverted_position = RIGHT;
+  switch (position) {
+    case TOP:
+      inverted_position = BOTTOM;
+      break;
+    case BOTTOM:
+      inverted_position = TOP;
+      break;
+    case RIGHT:
+      inverted_position = LEFT;
+      break;
+    case LEFT:
+      inverted_position = RIGHT;
+      break;
+  }
+  DisplayLayout ret = DisplayLayout(inverted_position, -offset);
+  ret.primary_id = primary_id;
+  return ret;
+}
+
+// static
+bool DisplayLayout::ConvertFromValue(const base::Value& value,
+                                     DisplayLayout* layout) {
+  base::JSONValueConverter<DisplayLayout> converter;
+  return converter.Convert(value, layout);
+}
+
+// static
+bool DisplayLayout::ConvertToValue(const DisplayLayout& layout,
+                                   base::Value* value) {
+  base::DictionaryValue* dict_value = NULL;
+  if (!value->GetAsDictionary(&dict_value) || dict_value == NULL)
+    return false;
+
+  const std::string position_str = GetStringFromPosition(layout.position);
+  dict_value->SetString(kPositionKey, position_str);
+  dict_value->SetInteger(kOffsetKey, layout.offset);
+  dict_value->SetBoolean(kMirroredKey, layout.mirrored);
+  dict_value->SetString(kPrimaryIdKey, base::Int64ToString(layout.primary_id));
+  return true;
+}
+
+std::string DisplayLayout::ToString() const {
+  const std::string position_str = GetStringFromPosition(position);
+  return base::StringPrintf(
+      "%s, %d%s",
+      position_str.c_str(), offset, mirrored ? ", mirrored" : "");
+}
+
+// static
+void DisplayLayout::RegisterJSONConverter(
+    base::JSONValueConverter<DisplayLayout>* converter) {
+  converter->RegisterCustomField<Position>(
+      kPositionKey, &DisplayLayout::position, &GetPositionFromString);
+  converter->RegisterIntField(kOffsetKey, &DisplayLayout::offset);
+  converter->RegisterBoolField(kMirroredKey, &DisplayLayout::mirrored);
+  converter->RegisterCustomField<int64>(
+      kPrimaryIdKey, &DisplayLayout::primary_id, &GetDisplayIdFromString);
+}
+
+}  // namespace ash
diff --git a/ash/display/display_layout.h b/ash/display/display_layout.h
new file mode 100644
index 0000000..dc32208
--- /dev/null
+++ b/ash/display/display_layout.h
@@ -0,0 +1,71 @@
+// Copyright (c) 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_DISPLAY_DISPLAY_LAYOUT_H_
+#define ASH_DISPLAY_DISPLAY_LAYOUT_H_
+
+#include <map>
+#include <string>
+
+#include "ash/ash_export.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+class Value;
+template <typename T> class JSONValueConverter;
+}
+
+namespace ash {
+
+typedef std::pair<int64, int64> DisplayIdPair;
+
+struct ASH_EXPORT DisplayLayout {
+  // Layout options where the secondary display should be positioned.
+  enum Position {
+    TOP,
+    RIGHT,
+    BOTTOM,
+    LEFT
+  };
+
+  // Factory method to create DisplayLayout from ints. The |mirrored| is
+  // set to false and |primary_id| is set to gfx::Display::kInvalidDisplayId.
+  // Used for persistence and webui.
+  static DisplayLayout FromInts(int position, int offsets);
+
+  DisplayLayout();
+  DisplayLayout(Position position, int offset);
+
+  // Returns an inverted display layout.
+  DisplayLayout Invert() const WARN_UNUSED_RESULT;
+
+  // Converter functions to/from base::Value.
+  static bool ConvertFromValue(const base::Value& value, DisplayLayout* layout);
+  static bool ConvertToValue(const DisplayLayout& layout, base::Value* value);
+
+  // This method is used by base::JSONValueConverter, you don't need to call
+  // this directly. Instead consider using converter functions above.
+  static void RegisterJSONConverter(
+      base::JSONValueConverter<DisplayLayout>* converter);
+
+  Position position;
+
+  // The offset of the position of the secondary display.  The offset is
+  // based on the top/left edge of the primary display.
+  int offset;
+
+  // True if displays are mirrored.
+  bool mirrored;
+
+  // The id of the display used as a primary display.
+  int64 primary_id;
+
+  // Returns string representation of the layout for debugging/testing.
+  std::string ToString() const;
+};
+
+}  // namespace ash
+
+#endif
diff --git a/ash/display/display_manager.cc b/ash/display/display_manager.cc
index eceb643..11fb650 100644
--- a/ash/display/display_manager.cc
+++ b/ash/display/display_manager.cc
@@ -58,6 +58,10 @@
 
 namespace {
 
+// The number of pixels to overlap between the primary and secondary displays,
+// in case that the offset value is too large.
+const int kMinimumOverlapForInvalidOffset = 100;
+
 // List of value UI Scale values. Scales for 2x are equivalent to 640,
 // 800, 1024, 1280, 1440, 1600 and 1920 pixel width respectively on
 // 2560 pixel width 2x density display. Please see crbug.com/233375
@@ -192,6 +196,54 @@
   return 1.0f;
 }
 
+void DisplayManager::UpdateDisplayBoundsForLayout(
+    const DisplayLayout& layout,
+    const gfx::Display& primary_display,
+    gfx::Display* secondary_display) {
+  DCHECK_EQ("0,0", primary_display.bounds().origin().ToString());
+
+  const gfx::Rect& primary_bounds = primary_display.bounds();
+  DisplayController::GetPrimaryDisplay().bounds();
+
+  const gfx::Rect& secondary_bounds = secondary_display->bounds();
+  gfx::Point new_secondary_origin = primary_bounds.origin();
+
+  DisplayLayout::Position position = layout.position;
+
+  // Ignore the offset in case the secondary display doesn't share edges with
+  // the primary display.
+  int offset = layout.offset;
+  if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) {
+    offset = std::min(
+        offset, primary_bounds.width() - kMinimumOverlapForInvalidOffset);
+    offset = std::max(
+        offset, -secondary_bounds.width() + kMinimumOverlapForInvalidOffset);
+  } else {
+    offset = std::min(
+        offset, primary_bounds.height() - kMinimumOverlapForInvalidOffset);
+    offset = std::max(
+        offset, -secondary_bounds.height() + kMinimumOverlapForInvalidOffset);
+  }
+  switch (position) {
+    case DisplayLayout::TOP:
+      new_secondary_origin.Offset(offset, -secondary_bounds.height());
+      break;
+    case DisplayLayout::RIGHT:
+      new_secondary_origin.Offset(primary_bounds.width(), offset);
+      break;
+    case DisplayLayout::BOTTOM:
+      new_secondary_origin.Offset(offset, primary_bounds.height());
+      break;
+    case DisplayLayout::LEFT:
+      new_secondary_origin.Offset(-secondary_bounds.width(), offset);
+      break;
+  }
+  gfx::Insets insets = secondary_display->GetWorkAreaInsets();
+  secondary_display->set_bounds(
+      gfx::Rect(new_secondary_origin, secondary_bounds.size()));
+  secondary_display->UpdateWorkAreaFromInsets(insets);
+}
+
 bool DisplayManager::IsActiveDisplay(const gfx::Display& display) const {
   for (DisplayList::const_iterator iter = displays_.begin();
        iter != displays_.end(); ++iter) {
@@ -236,20 +288,7 @@
 
 void DisplayManager::SetOverscanInsets(int64 display_id,
                                        const gfx::Insets& insets_in_dip) {
-  // TODO(oshima): insets has to be rotated according to the
-  // the current display rotation.
-  display_info_[display_id].SetOverscanInsets(true, insets_in_dip);
-  DisplayInfoList display_info_list;
-  for (DisplayList::const_iterator iter = displays_.begin();
-       iter != displays_.end(); ++iter) {
-    display_info_list.push_back(GetDisplayInfo(iter->id()));
-  }
-  AddMirrorDisplayInfoIfAny(&display_info_list);
-  UpdateDisplays(display_info_list);
-}
-
-void DisplayManager::ClearCustomOverscanInsets(int64 display_id) {
-  display_info_[display_id].clear_has_custom_overscan_insets();
+  display_info_[display_id].SetOverscanInsets(insets_in_dip);
   DisplayInfoList display_info_list;
   for (DisplayList::const_iterator iter = displays_.begin();
        iter != displays_.end(); ++iter) {
@@ -321,7 +360,7 @@
   if (0.5f <= ui_scale && ui_scale <= 2.0f)
     display_info_[display_id].set_ui_scale(ui_scale);
   if (overscan_insets)
-    display_info_[display_id].SetOverscanInsets(true, *overscan_insets);
+    display_info_[display_id].SetOverscanInsets(*overscan_insets);
 }
 
 bool DisplayManager::IsDisplayRotationEnabled() const {
@@ -419,7 +458,7 @@
   if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
     DCHECK_EQ(1u, updated_display_info_list.size()) <<
         "Multiple display test does not work on Win8 bots. Please "
-        "skip (don't disable) the test using |SupportMultipleDisplay()|";
+        "skip (don't disable) the test using SupportsMultipleDisplays()";
   }
 #endif
 
@@ -453,7 +492,7 @@
     if (new_info_iter != new_display_info_list.end() &&
         mirrored_display_id == new_info_iter->id()) {
       DisplayInfo info = *new_info_iter;
-      info.SetOverscanInsets(true, gfx::Insets());
+      info.SetOverscanInsets(gfx::Insets());
       InsertAndUpdateDisplayInfo(info);
 
       mirrored_display_ = CreateDisplayFromDisplayInfoById(new_info_iter->id());
@@ -550,6 +589,17 @@
   display_controller->NotifyDisplayConfigurationChanging();
   mouse_location_in_native = display_controller->GetNativeMouseCursorLocation();
 
+  size_t updated_index;
+  if (UpdateSecondaryDisplayBoundsForLayout(&new_displays, &updated_index) &&
+      std::find(added_display_indices.begin(),
+                added_display_indices.end(),
+                updated_index) == added_display_indices.end() &&
+      std::find(changed_display_indices.begin(),
+                changed_display_indices.end(),
+                updated_index) == changed_display_indices.end()) {
+    changed_display_indices.push_back(updated_index);
+  }
+
   displays_ = new_displays;
 
   base::AutoReset<bool> resetter(&change_display_upon_host_resize_, false);
@@ -685,6 +735,15 @@
   return base::StringPrintf("Display %d", static_cast<int>(id));
 }
 
+int64 DisplayManager::GetDisplayIdForUIScaling() const {
+  // UI Scaling is effective only on internal display.
+  int64 display_id = gfx::Display::InternalDisplayId();
+#if defined(OS_WIN)
+  display_id = first_display_id();
+#endif
+  return display_id;
+}
+
 void DisplayManager::SetMirrorMode(bool mirrored) {
   if (num_connected_displays() <= 1)
     return;
@@ -768,15 +827,6 @@
   mirrored_display_ = gfx::Display();
 }
 
-int64 DisplayManager::GetDisplayIdForUIScaling() const {
-  // UI Scaling is effective only on internal display.
-  int64 display_id = gfx::Display::InternalDisplayId();
-#if defined(OS_WIN)
-  display_id = first_display_id();
-#endif
-  return display_id;
-}
-
 void DisplayManager::Init() {
   // TODO(oshima): Move this logic to DisplayChangeObserver.
   const string size_str = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
@@ -856,12 +906,49 @@
 
   // Simply set the origin to (0,0).  The primary display's origin is
   // always (0,0) and the secondary display's bounds will be updated
-  // by |DisplayController::UpdateDisplayBoundsForLayout|.
+  // in |UpdateSecondaryDisplayBoundsForLayout| called in |UpdateDisplay|.
   new_display.SetScaleAndBounds(
       display_info.device_scale_factor(), gfx::Rect(bounds_in_pixel.size()));
   new_display.set_rotation(display_info.rotation());
   return new_display;
 }
 
+bool DisplayManager::UpdateSecondaryDisplayBoundsForLayout(
+    DisplayList* displays,
+    size_t* updated_index) const {
+  if (displays->size() != 2U)
+    return false;
+
+  DisplayController* controller = Shell::GetInstance()->display_controller();
+  int64 id_at_zero = displays->at(0).id();
+  DisplayIdPair pair =
+      (id_at_zero == first_display_id_ ||
+       id_at_zero == gfx::Display::InternalDisplayId()) ?
+      std::make_pair(id_at_zero, displays->at(1).id()) :
+      std::make_pair(displays->at(1).id(), id_at_zero) ;
+  DisplayLayout layout =
+      controller->ComputeDisplayLayoutForDisplayIdPair(pair);
+
+  // Ignore if a user has a old format (should be extremely rare)
+  // and this will be replaced with DCHECK.
+  if (layout.primary_id != gfx::Display::kInvalidDisplayID) {
+    size_t primary_index, secondary_index;
+    if (displays->at(0).id() == layout.primary_id) {
+      primary_index = 0;
+      secondary_index = 1;
+    } else {
+      primary_index = 1;
+      secondary_index = 0;
+    }
+    gfx::Rect bounds =
+        GetDisplayForId(displays->at(secondary_index).id()).bounds();
+    UpdateDisplayBoundsForLayout(
+        layout, displays->at(primary_index), &displays->at(secondary_index));
+    *updated_index = secondary_index;
+    return bounds != displays->at(secondary_index).bounds();
+  }
+  return false;
+}
+
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/display/display_manager.h b/ash/display/display_manager.h
index f1e52f9..3d75616 100644
--- a/ash/display/display_manager.h
+++ b/ash/display/display_manager.h
@@ -10,6 +10,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/display/display_info.h"
+#include "ash/display/display_layout.h"
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
 #include "ui/aura/root_window_observer.h"
@@ -54,6 +55,12 @@
   // Returns next valid UI scale.
   static float GetNextUIScale(const DisplayInfo& info, bool up);
 
+  // Updates the bounds of |secondary_display| according to |layout|.
+  static void UpdateDisplayBoundsForLayout(
+      const DisplayLayout& layout,
+      const gfx::Display& primary_display,
+      gfx::Display* secondary_display);
+
   // When set to true, the MonitorManager calls OnDisplayBoundsChanged
   // even if the display's bounds didn't change. Used to swap primary
   // display.
@@ -89,9 +96,6 @@
   // display's bounds change.
   void SetOverscanInsets(int64 display_id, const gfx::Insets& insets_in_dip);
 
-  // Clears the overscan insets
-  void ClearCustomOverscanInsets(int64 display_id);
-
   // Sets the display's rotation.
   void SetDisplayRotation(int64 display_id, gfx::Display::Rotation rotation);
 
@@ -187,7 +191,7 @@
   void SetSoftwareMirroring(bool enabled);
 #endif
 
- private:
+private:
   FRIEND_TEST_ALL_PREFIXES(ExtendedDesktopTest, ConvertPoint);
   FRIEND_TEST_ALL_PREFIXES(DisplayManagerTest, TestNativeDisplaysChanged);
   FRIEND_TEST_ALL_PREFIXES(DisplayManagerTest,
@@ -226,6 +230,14 @@
   // Creates a display object from the DisplayInfo for |display_id|.
   gfx::Display CreateDisplayFromDisplayInfoById(int64 display_id);
 
+  // Updates the bounds of the secondary display in |display_list|
+  // using the layout registered for the display pair and set the
+  // index of display updated to |updated_index|. Returns true
+  // if the secondary display's bounds has been changed from current
+  // value, or false otherwise.
+  bool UpdateSecondaryDisplayBoundsForLayout(DisplayList* display_list,
+                                             size_t* updated_index) const;
+
   int64 first_display_id_;
 
   gfx::Display mirrored_display_;
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 062a0e7..f06aa78 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -11,6 +11,7 @@
 #include "ash/test/display_manager_test_api.h"
 #include "ash/test/mirror_window_test_api.h"
 #include "base/format_macros.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "ui/aura/env.h"
 #include "ui/aura/root_window.h"
@@ -27,6 +28,14 @@
 
 using base::StringPrintf;
 
+namespace {
+
+std::string ToDisplayName(int64 id) {
+  return "x-" + base::Int64ToString(id);
+}
+
+}  // namespace
+
 class DisplayManagerTest : public test::AshTestBase,
                            public gfx::DisplayObserver,
                            public aura::WindowObserver {
@@ -118,7 +127,7 @@
   DISALLOW_COPY_AND_ASSIGN(DisplayManagerTest);
 };
 
-TEST_F(DisplayManagerTest, NativeDisplayTest) {
+TEST_F(DisplayManagerTest, UpdateDisplayTest) {
   if (!SupportsMultipleDisplays())
     return;
 
@@ -209,6 +218,15 @@
   EXPECT_EQ("1000,1000 600x400",
             GetDisplayInfoAt(1).bounds_in_pixel().ToString());
   reset();
+
+  // Changing primary will update secondary as well.
+  UpdateDisplay("0+0-800x600,1000+1000-600x400");
+  EXPECT_EQ("2 0 0", GetCountSummary());
+  reset();
+  EXPECT_EQ("0,0 800x600",
+            display_manager()->GetDisplayAt(0)->bounds().ToString());
+  EXPECT_EQ("800,0 600x400",
+            display_manager()->GetDisplayAt(1)->bounds().ToString());
 }
 
 // Test in emulation mode (use_fullscreen_host_window=false)
@@ -365,6 +383,9 @@
 }
 
 TEST_F(DisplayManagerTest, TestDeviceScaleOnlyChange) {
+  if (!SupportsHostWindowResize())
+    return;
+
   UpdateDisplay("1000x600");
   EXPECT_EQ(1,
             Shell::GetPrimaryRootWindow()->compositor()->device_scale_factor());
@@ -378,13 +399,13 @@
 }
 
 DisplayInfo CreateDisplayInfo(int64 id, const gfx::Rect& bounds) {
-  DisplayInfo info(id, StringPrintf("x-%d", static_cast<int>(id)), false);
+  DisplayInfo info(id, ToDisplayName(id), false);
   info.SetBounds(bounds);
   return info;
 }
 
 TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
-  const int internal_display_id =
+  const int64 internal_display_id =
       test::DisplayManagerTestApi(display_manager()).
       SetFirstDisplayAsInternalDisplay();
   const int external_id = 10;
@@ -426,20 +447,24 @@
   EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
   EXPECT_EQ(external_id, Shell::GetScreen()->GetPrimaryDisplay().id());
 
+  EXPECT_EQ(internal_display_id, gfx::Display::InternalDisplayId());
+
   // Primary connected, with different bounds.
   display_info_list.clear();
   display_info_list.push_back(internal_display_info);
   display_info_list.push_back(external_display_info);
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_EQ(2U, display_manager()->GetNumDisplays());
-  // need to remember which is primary
+  EXPECT_EQ(internal_display_id, Shell::GetScreen()->GetPrimaryDisplay().id());
+
+  // This combinatino is new, so internal display becomes primary.
   EXPECT_EQ("0,0 500x500",
             FindDisplayForId(internal_display_id).bounds().ToString());
   EXPECT_EQ("1,1 100x100",
             FindDisplayInfoForId(10).bounds_in_pixel().ToString());
   EXPECT_EQ(2U, display_manager()->num_connected_displays());
   EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
-  EXPECT_EQ(StringPrintf("x-%d", internal_display_id),
+  EXPECT_EQ(ToDisplayName(internal_display_id),
             display_manager()->GetDisplayNameForId(internal_display_id));
 
   // Emulate suspend.
@@ -452,7 +477,7 @@
             FindDisplayInfoForId(10).bounds_in_pixel().ToString());
   EXPECT_EQ(2U, display_manager()->num_connected_displays());
   EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
-  EXPECT_EQ(StringPrintf("x-%d", internal_display_id),
+  EXPECT_EQ(ToDisplayName(internal_display_id),
             display_manager()->GetDisplayNameForId(internal_display_id));
 
   // External display has disconnected then resumed.
@@ -500,7 +525,7 @@
   EXPECT_TRUE(display_manager()->IsMirrored());
 
   // Test display name.
-  EXPECT_EQ(StringPrintf("x-%d", internal_display_id),
+  EXPECT_EQ(ToDisplayName(internal_display_id),
             display_manager()->GetDisplayNameForId(internal_display_id));
   EXPECT_EQ("x-10", display_manager()->GetDisplayNameForId(10));
   EXPECT_EQ("x-11", display_manager()->GetDisplayNameForId(11));
@@ -690,43 +715,6 @@
   EXPECT_EQ("0,0 100x100", FindDisplayForId(10).bounds().ToString());
 }
 
-TEST_F(DisplayManagerTest, AutomaticOverscanInsets) {
-  if (!SupportsMultipleDisplays())
-    return;
-
-  UpdateDisplay("200x200,400x400");
-
-  std::vector<DisplayInfo> display_info_list;
-  display_info_list.push_back(GetDisplayInfoAt(0));
-  display_info_list.push_back(GetDisplayInfoAt(1));
-  display_info_list[1].set_has_overscan_for_test(true);
-  int64 id = display_info_list[1].id();
-  // SetDefaultOverscanInsets(&display_info_list[1]);
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-  // It has overscan insets, although SetOverscanInsets() isn't called.
-  EXPECT_EQ("380x380",
-            GetDisplayInfoAt(1).size_in_pixel().ToString());
-
-  // If custom overscan insets is specified, the specified value is used.
-  display_manager()->SetOverscanInsets(id, gfx::Insets(5, 6, 7, 8));
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-  EXPECT_EQ("386x388",
-            GetDisplayInfoAt(1).size_in_pixel().ToString());
-
-  // Do not overscan even though it has 'has_overscan' flag, if the custom
-  // insets is empty.
-  display_manager()->SetOverscanInsets(id, gfx::Insets());
-  display_manager()->OnNativeDisplaysChanged(display_info_list);
-  EXPECT_EQ("400x400",
-            GetDisplayInfoAt(1).size_in_pixel().ToString());
-
-  // Clearing the custom overscan should set the bounds to
-  // original.
-  display_manager()->ClearCustomOverscanInsets(id);
-  EXPECT_EQ("380x380",
-            GetDisplayInfoAt(1).size_in_pixel().ToString());
-}
-
 TEST_F(DisplayManagerTest, Rotate) {
   if (!SupportsMultipleDisplays())
     return;
@@ -756,6 +744,20 @@
   EXPECT_EQ("300x400",
             GetDisplayInfoAt(1).size_in_pixel().ToString());
 
+  // Just Rotating display will change the bounds on both display.
+  UpdateDisplay("100x200/l,300x400");
+  EXPECT_EQ("2 0 0", GetCountSummary());
+  reset();
+
+  // Updating tothe same configuration should report no changes.
+  UpdateDisplay("100x200/l,300x400");
+  EXPECT_EQ("0 0 0", GetCountSummary());
+  reset();
+
+  UpdateDisplay("100x200/l,300x400");
+  EXPECT_EQ("0 0 0", GetCountSummary());
+  reset();
+
   UpdateDisplay("200x200");
   EXPECT_EQ("1 0 1", GetCountSummary());
   reset();
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 4f750fe..a2b9d09 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -11,6 +11,7 @@
 #undef RootWindow
 #endif
 
+#include "ash/display/display_controller.h"
 #include "ash/display/display_info.h"
 #include "ash/display/display_manager.h"
 #include "ash/display/root_window_transformers.h"
@@ -155,6 +156,9 @@
 }
 
 void MirrorWindowController::UpdateWindow(const DisplayInfo& display_info) {
+  if (Shell::GetInstance()->display_controller()->in_bootstrap())
+    return;
+
   static int mirror_root_window_count = 0;
   DisplayManager* display_manager = Shell::GetInstance()->display_manager();
 
diff --git a/ash/display/output_configurator_animation.cc b/ash/display/output_configurator_animation.cc
index 0b57aca..d58d9a5 100644
--- a/ash/display/output_configurator_animation.cc
+++ b/ash/display/output_configurator_animation.cc
@@ -9,7 +9,7 @@
 #include "ash/shell_window_ids.h"
 #include "base/bind.h"
 #include "base/stl_util.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer.h"
diff --git a/ash/display/output_configurator_animation.h b/ash/display/output_configurator_animation.h
index dc70264..3fefe92 100644
--- a/ash/display/output_configurator_animation.h
+++ b/ash/display/output_configurator_animation.h
@@ -9,7 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "base/callback.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "chromeos/display/output_configurator.h"
 
 namespace aura {
diff --git a/ash/display/root_window_transformers.cc b/ash/display/root_window_transformers.cc
index 6142839..a698fa4 100644
--- a/ash/display/root_window_transformers.cc
+++ b/ash/display/root_window_transformers.cc
@@ -44,6 +44,10 @@
   }
 }
 
+// TODO(oshima): Transformers should be able to adjust itself
+// when the device scale factor is changed, instead of
+// precalculating the transform using fixed value.
+
 gfx::Transform CreateRotationTransform(aura::RootWindow* root_window,
                                        const gfx::Display& display) {
   DisplayInfo info =
diff --git a/ash/display/shared_display_edge_indicator.cc b/ash/display/shared_display_edge_indicator.cc
index 5370c3b..cfcf4ba 100644
--- a/ash/display/shared_display_edge_indicator.cc
+++ b/ash/display/shared_display_edge_indicator.cc
@@ -49,7 +49,7 @@
                             views::View* contents_view) {
   views::Widget* widget = new views::Widget;
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.can_activate = false;
   params.keep_on_top = true;
   // We set the context to the primary root window; this is OK because the ash
diff --git a/ash/drag_drop/drag_image_view.cc b/ash/drag_drop/drag_image_view.cc
index 066b99c..242488b 100644
--- a/ash/drag_drop/drag_image_view.cc
+++ b/ash/drag_drop/drag_image_view.cc
@@ -26,7 +26,7 @@
   params.context = context;
   params.accept_events = false;
   params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.transparent = true;
+  params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW;
   drag_widget->Init(params);
   drag_widget->SetOpacity(0xFF);
   drag_widget->GetNativeWindow()->set_owned_by_parent(false);
diff --git a/ash/launcher/alternate_app_list_button.cc b/ash/launcher/alternate_app_list_button.cc
new file mode 100644
index 0000000..77f1184
--- /dev/null
+++ b/ash/launcher/alternate_app_list_button.cc
@@ -0,0 +1,137 @@
+// 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/alternate_app_list_button.h"
+
+#include "ash/ash_switches.h"
+#include "ash/launcher/launcher_button_host.h"
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_element.h"
+#include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia_operations.h"
+
+namespace ash {
+namespace internal {
+
+// static
+const int AlternateAppListButton::kImageBoundsSize = 7;
+
+
+AlternateAppListButton::AlternateAppListButton(views::ButtonListener* listener,
+                                               LauncherButtonHost* host,
+                                               ShelfWidget* shelf_widget)
+    : views::ImageButton(listener),
+      host_(host),
+      shelf_widget_(shelf_widget) {
+  SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
+  SetSize(gfx::Size(ShelfLayoutManager::kShelfSize,
+                    ShelfLayoutManager::kShelfSize));
+}
+
+AlternateAppListButton::~AlternateAppListButton() {
+}
+
+bool AlternateAppListButton::OnMousePressed(const ui::MouseEvent& event) {
+  ImageButton::OnMousePressed(event);
+  host_->PointerPressedOnButton(this, LauncherButtonHost::MOUSE, event);
+  return true;
+}
+
+void AlternateAppListButton::OnMouseReleased(const ui::MouseEvent& event) {
+  ImageButton::OnMouseReleased(event);
+  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, false);
+}
+
+void AlternateAppListButton::OnMouseCaptureLost() {
+  host_->PointerReleasedOnButton(this, LauncherButtonHost::MOUSE, true);
+  ImageButton::OnMouseCaptureLost();
+}
+
+bool AlternateAppListButton::OnMouseDragged(const ui::MouseEvent& event) {
+  ImageButton::OnMouseDragged(event);
+  host_->PointerDraggedOnButton(this, LauncherButtonHost::MOUSE, event);
+  return true;
+}
+
+void AlternateAppListButton::OnMouseMoved(const ui::MouseEvent& event) {
+  ImageButton::OnMouseMoved(event);
+  host_->MouseMovedOverButton(this);
+}
+
+void AlternateAppListButton::OnMouseEntered(const ui::MouseEvent& event) {
+  ImageButton::OnMouseEntered(event);
+  host_->MouseEnteredButton(this);
+}
+
+void AlternateAppListButton::OnMouseExited(const ui::MouseEvent& event) {
+  ImageButton::OnMouseExited(event);
+  host_->MouseExitedButton(this);
+}
+
+void AlternateAppListButton::OnPaint(gfx::Canvas* canvas) {
+  // Call the base class first to paint any background/borders.
+  View::OnPaint(canvas);
+
+  int background_image_id = 0;
+  if (Shell::GetInstance()->GetAppListTargetVisibility()) {
+    background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED;
+  } else {
+    if (shelf_widget_->GetDimsShelf())
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_ON_BLACK;
+    else
+      background_image_id = IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL;
+  }
+  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+  const gfx::ImageSkia* background_image =
+      rb.GetImageNamed(background_image_id).ToImageSkia();
+  const gfx::ImageSkia* forground_image =
+      rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE).ToImageSkia();
+
+  gfx::Rect contents_bounds = GetContentsBounds();
+  gfx::Rect background_bounds, forground_bounds;
+
+  background_bounds.set_x(contents_bounds.x() +
+      std::max(0,
+          (contents_bounds.width() - background_image->width()) / 2));
+  background_bounds.set_y(contents_bounds.y() +
+      std::max(kImageBoundsSize,
+          (contents_bounds.height() - background_image->height()) / 2));
+  background_bounds.set_size(background_image->size());
+
+  forground_bounds.set_size(forground_image->size());
+  forground_bounds.set_x(background_bounds.x() +
+      std::max(0,
+          (background_bounds.width() - forground_bounds.width()) / 2));
+  forground_bounds.set_y(background_bounds.y() +
+      std::max(0,
+          (background_bounds.height() - forground_bounds.height()) / 2));
+
+  canvas->DrawImageInt(*background_image,
+                       background_bounds.x(),
+                       background_bounds.y());
+  canvas->DrawImageInt(*forground_image,
+                       forground_bounds.x(),
+                       forground_bounds.y());
+
+  OnPaintFocusBorder(canvas);
+}
+
+void AlternateAppListButton::GetAccessibleState(
+    ui::AccessibleViewState* state) {
+  state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
+  state->name = host_->GetAccessibleName(this);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/launcher/alternate_app_list_button.h b/ash/launcher/alternate_app_list_button.h
new file mode 100644
index 0000000..f68207c
--- /dev/null
+++ b/ash/launcher/alternate_app_list_button.h
@@ -0,0 +1,58 @@
+// 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_ALTERNATE_APP_LIST_BUTTON_H_
+#define ASH_LAUNCHER_ALTERNATE_APP_LIST_BUTTON_H_
+
+#include "ui/views/controls/button/image_button.h"
+
+namespace ash {
+
+class ShelfWidget;
+
+namespace internal {
+
+class LauncherButtonHost;
+
+
+
+// Button used for the AppList icon on the launcher.
+// This class is an alternate implementation to
+// ash::internal::AppListButton for the purposes of testing an
+// alternate shelf layout (see ash_switches: UseAlternateShelfLayout).
+class AlternateAppListButton : public views::ImageButton {
+ public:
+  // Bounds size (inset) required for the app icon image (in pixels).
+  static const int kImageBoundsSize;
+
+  AlternateAppListButton(views::ButtonListener* listener,
+                         LauncherButtonHost* host,
+                         ShelfWidget* shelf_widget);
+  virtual ~AlternateAppListButton();
+
+ protected:
+  // View overrides:
+  virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseCaptureLost() OVERRIDE;
+  virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseMoved(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ private:
+  LauncherButtonHost* host_;
+  // Reference to the shelf widget containing this button, owned by the
+  // root window controller.
+  ShelfWidget* shelf_widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(AlternateAppListButton);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_LAUNCHER_ALTERNATE_APP_LIST_BUTTON_H_
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 8991c35..03e7ff8 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -146,11 +146,11 @@
   return launcher_view_->GetAppListButtonView();
 }
 
-void Launcher::SwitchToWindow(int window_index) {
+void Launcher::LaunchAppIndexAt(int item_index) {
   LauncherModel* launcher_model = launcher_view_->model();
   const LauncherItems& items = launcher_model->items();
   int item_count = launcher_model->item_count();
-  int indexes_left = window_index >= 0 ? window_index : item_count;
+  int indexes_left = item_index >= 0 ? item_index : item_count;
   int found_index = -1;
 
   // Iterating until we have hit the index we are interested in which
@@ -165,7 +165,7 @@
   // There are two ways how found_index can be valid: a.) the nth item was
   // found (which is true when indexes_left is -1) or b.) the last item was
   // requested (which is true when index was passed in as a negative number).
-  if (found_index >= 0 && (indexes_left == -1 || window_index < 0) &&
+  if (found_index >= 0 && (indexes_left == -1 || item_index < 0) &&
       (delegate_->IsPerAppLauncher() ||
        (items[found_index].status == ash::STATUS_RUNNING ||
         items[found_index].status == ash::STATUS_CLOSED))) {
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
index caeb63f..f9bdcc0 100644
--- a/ash/launcher/launcher.h
+++ b/ash/launcher/launcher.h
@@ -87,9 +87,9 @@
 
   views::View* GetAppListButtonView() const;
 
-  // Switches to a 0-indexed (in order of creation) window.
-  // A negative index switches to the last window in the list.
-  void SwitchToWindow(int window_index);
+  // Launch a 0-indexed launcher item in the Launcher.
+  // A negative index launches the last launcher item in the launcher.
+  void LaunchAppIndexAt(int item_index);
 
   // Only to be called for testing. Retrieves the LauncherView.
   // TODO(sky): remove this!
diff --git a/ash/launcher/launcher_model.cc b/ash/launcher/launcher_model.cc
index 68e57e0..8daf561 100644
--- a/ash/launcher/launcher_model.cc
+++ b/ash/launcher/launcher_model.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/ash_switches.h"
 #include "ash/launcher/launcher_model_observer.h"
 
 namespace ash {
@@ -22,7 +23,7 @@
     case TYPE_PLATFORM_APP:
       return 1;
     case TYPE_APP_LIST:
-      return 2;
+      return ash::switches::UseAlternateShelfLayout() ? 0 : 2;
     case TYPE_APP_PANEL:
       return 3;
   }
@@ -148,7 +149,8 @@
 
 int LauncherModel::ValidateInsertionIndex(LauncherItemType type,
                                           int index) const {
-  DCHECK(index >= 0 && index <= item_count());
+  DCHECK(index >= 0 && index <= item_count() +
+      (ash::switches::UseAlternateShelfLayout() ? 1 : 0));
 
   // Clamp |index| to the allowed range for the type as determined by |weight|.
   LauncherItem weight_dummy;
diff --git a/ash/launcher/launcher_tooltip_manager.cc b/ash/launcher/launcher_tooltip_manager.cc
index 1ed63d5..8fbcc6a 100644
--- a/ash/launcher/launcher_tooltip_manager.cc
+++ b/ash/launcher/launcher_tooltip_manager.cc
@@ -11,8 +11,8 @@
 #include "ash/wm/window_animations.h"
 #include "base/bind.h"
 #include "base/message_loop.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
 #include "ui/base/events/event.h"
diff --git a/ash/launcher/launcher_tooltip_manager_unittest.cc b/ash/launcher/launcher_tooltip_manager_unittest.cc
index 6a38d09..5df58a6 100644
--- a/ash/launcher/launcher_tooltip_manager_unittest.cc
+++ b/ash/launcher/launcher_tooltip_manager_unittest.cc
@@ -12,7 +12,7 @@
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_util.h"
 #include "base/strings/string16.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/root_window.h"
 #include "ui/base/events/event.h"
 #include "ui/base/events/event_constants.h"
@@ -90,7 +90,7 @@
     widget_.reset(new views::Widget);
     views::Widget::InitParams params(
         views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-    params.transparent = true;
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
     params.parent = Shell::GetContainer(
         Shell::GetPrimaryRootWindow(),
diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc
index 70292f2..ede55d4 100644
--- a/ash/launcher/launcher_view.cc
+++ b/ash/launcher/launcher_view.cc
@@ -9,6 +9,7 @@
 #include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
 #include "ash/drag_drop/drag_image_view.h"
+#include "ash/launcher/alternate_app_list_button.h"
 #include "ash/launcher/app_list_button.h"
 #include "ash/launcher/launcher_button.h"
 #include "ash/launcher/launcher_delegate.h"
@@ -441,7 +442,8 @@
   LayoutToIdealBounds();
   for (int i=0; i < view_model_->view_size(); ++i) {
     // TODO: remove when AppIcon is a Launcher Button.
-    if (TYPE_APP_LIST == model_->items()[i].type) {
+    if (TYPE_APP_LIST == model_->items()[i].type &&
+        !ash::switches::UseAlternateShelfLayout()) {
       ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
       static_cast<AppListButton*>(view_model_->view_at(i))->SetImageAlignment(
           shelf->SelectValueForShelfAlignment(
@@ -683,6 +685,7 @@
     return;
 
   int first_panel_index = model_->FirstPanelIndex();
+  // TODO(harrym): if alternate shelf layout stays, rename app_list_index.
   int app_list_index = first_panel_index - 1;
 
   // Initial x,y values account both leading_inset in primary
@@ -698,8 +701,12 @@
       leading_inset(),
       leading_inset(),
       0);
-  int w = shelf->PrimaryAxisValue(kLauncherPreferredSize, width());
-  int h = shelf->PrimaryAxisValue(height(), kLauncherPreferredSize);
+
+  int shelf_size = ash::switches::UseAlternateShelfLayout() ?
+      ShelfLayoutManager::kShelfSize : kLauncherPreferredSize;
+
+  int w = shelf->PrimaryAxisValue(shelf_size, width());
+  int h = shelf->PrimaryAxisValue(height(), shelf_size);
   for (int i = 0; i < view_model_->view_size(); ++i) {
     if (i < first_visible_index_) {
       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
@@ -901,21 +908,26 @@
     }
 
     case TYPE_APP_LIST: {
-      // TODO(dave): turn this into a LauncherButton too.
-      AppListButton* button = new AppListButton(this, this);
-      ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
-      button->SetImageAlignment(
-          shelf->SelectValueForShelfAlignment(
-              views::ImageButton::ALIGN_CENTER,
-              views::ImageButton::ALIGN_LEFT,
-              views::ImageButton::ALIGN_RIGHT,
-              views::ImageButton::ALIGN_CENTER),
-          shelf->SelectValueForShelfAlignment(
-              views::ImageButton::ALIGN_TOP,
-              views::ImageButton::ALIGN_MIDDLE,
-              views::ImageButton::ALIGN_MIDDLE,
-              views::ImageButton::ALIGN_BOTTOM));
-      view = button;
+      if (ash::switches::UseAlternateShelfLayout()) {
+        view = new AlternateAppListButton(this, this,
+            tooltip_->shelf_layout_manager()->shelf_widget());
+      } else {
+        // TODO(dave): turn this into a LauncherButton too.
+        AppListButton* button = new AppListButton(this, this);
+        ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
+        button->SetImageAlignment(
+            shelf->SelectValueForShelfAlignment(
+                views::ImageButton::ALIGN_CENTER,
+                views::ImageButton::ALIGN_LEFT,
+                views::ImageButton::ALIGN_RIGHT,
+                views::ImageButton::ALIGN_CENTER),
+            shelf->SelectValueForShelfAlignment(
+                views::ImageButton::ALIGN_TOP,
+                views::ImageButton::ALIGN_MIDDLE,
+                views::ImageButton::ALIGN_MIDDLE,
+                views::ImageButton::ALIGN_BOTTOM));
+        view = button;
+      }
       break;
     }
 
@@ -1023,17 +1035,36 @@
 
 bool LauncherView::SameDragType(LauncherItemType typea,
                                 LauncherItemType typeb) const {
-  switch (typea) {
-    case TYPE_TABBED:
-    case TYPE_PLATFORM_APP:
+  if (ash::switches::UseAlternateShelfLayout()) {
+    // TODO(harrym): Allow app list to be repositionable, if this goes live
+    // (no flag) the pref file has to be updated so the changes persist.
+    switch (typea) {
+     case TYPE_TABBED:
+     case TYPE_PLATFORM_APP:
       return (typeb == TYPE_TABBED || typeb == TYPE_PLATFORM_APP);
-    case TYPE_APP_SHORTCUT:
-    case TYPE_BROWSER_SHORTCUT:
-      return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
-    case TYPE_WINDOWED_APP:
-    case TYPE_APP_LIST:
-    case TYPE_APP_PANEL:
-      return typeb == typea;
+     case TYPE_APP_SHORTCUT:
+     case TYPE_APP_LIST:
+     case TYPE_BROWSER_SHORTCUT:
+      return (typeb == TYPE_APP_SHORTCUT ||
+              typeb == TYPE_APP_LIST ||
+              typeb == TYPE_BROWSER_SHORTCUT);
+     case TYPE_WINDOWED_APP:
+     case TYPE_APP_PANEL:
+       return typeb == typea;
+    }
+  } else {
+    switch (typea) {
+      case TYPE_TABBED:
+      case TYPE_PLATFORM_APP:
+        return (typeb == TYPE_TABBED || typeb == TYPE_PLATFORM_APP);
+      case TYPE_APP_SHORTCUT:
+      case TYPE_BROWSER_SHORTCUT:
+        return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
+      case TYPE_WINDOWED_APP:
+      case TYPE_APP_LIST:
+      case TYPE_APP_PANEL:
+        return typeb == typea;
+    }
   }
   NOTREACHED();
   return false;
@@ -1339,6 +1370,8 @@
 }
 
 void LauncherView::LauncherStatusChanged() {
+  if (ash::switches::UseAlternateShelfLayout())
+    return;
   AppListButton* app_list_button =
       static_cast<AppListButton*>(GetAppListButtonView());
   if (model_->status() == LauncherModel::STATUS_LOADING)
diff --git a/ash/launcher/tabbed_launcher_button.h b/ash/launcher/tabbed_launcher_button.h
index 6eb0b9e..0ac2c3b 100644
--- a/ash/launcher/tabbed_launcher_button.h
+++ b/ash/launcher/tabbed_launcher_button.h
@@ -7,7 +7,7 @@
 
 #include "ash/launcher/launcher_button.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/glow_hover_controller.h"
diff --git a/ash/magnifier/magnification_controller.cc b/ash/magnifier/magnification_controller.cc
index bb50285..c8becce 100644
--- a/ash/magnifier/magnification_controller.cc
+++ b/ash/magnifier/magnification_controller.cc
@@ -4,8 +4,6 @@
 
 #include "ash/magnifier/magnification_controller.h"
 
-#include "ash/display/display_controller.h"
-#include "ash/display/display_manager.h"
 #include "ash/display/root_window_transformers.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
diff --git a/ash/magnifier/magnification_controller_unittest.cc b/ash/magnifier/magnification_controller_unittest.cc
index 701c1f4..f369586 100644
--- a/ash/magnifier/magnification_controller_unittest.cc
+++ b/ash/magnifier/magnification_controller_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/display/display_controller.h"
 #include "ash/magnifier/magnification_controller.h"
 #include "ash/magnifier/magnifier_constants.h"
 #include "ash/shell.h"
diff --git a/ash/magnifier/partial_magnification_controller.cc b/ash/magnifier/partial_magnification_controller.cc
index 1f46d51..d913856 100644
--- a/ash/magnifier/partial_magnification_controller.cc
+++ b/ash/magnifier/partial_magnification_controller.cc
@@ -144,7 +144,7 @@
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.can_activate = false;
   params.accept_events = false;
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.parent = root_window;
   zoom_widget_->Init(params);
   zoom_widget_->SetBounds(gfx::Rect(mouse.x() - kMagnifierWidth / 2,
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index c858b32..b9e20ac 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -20,6 +20,7 @@
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BROWSER_SHORTCUT" file="common/chromium-32.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" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_HOT" file="common/launcher/launcher_appmenu_hover.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED" file="common/launcher/launcher_appmenu_pressed.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_INCOGNITO_BROWSER" file="common/launcher/launcher_incognito_browser.png" />
@@ -33,6 +34,9 @@
       <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_TASK_MANAGER" file="common/launcher/task_manager.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_MULTI_WINDOW_RESIZE_H" file="common/multi_window_resize_horizontal.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_MULTI_WINDOW_RESIZE_V" file="common/multi_window_resize_vertical.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_BACKGROUND_NORMAL" file="common/alt_launcher/status_icon_background_normal.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_BACKGROUND_ON_BLACK" file="common/alt_launcher/status_icon_background_onblack_normal.png" />
+      <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_BACKGROUND_PRESSED" file="common/alt_launcher/status_icon_background_pressed.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_RESIZE_SHADOW_BOTTOM" file="common/resize_shadow_bottom.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_RESIZE_SHADOW_BOTTOM_LEFT" file="common/resize_shadow_bottom_left.png" />
       <structure type="chrome_scaled_image" name="IDR_AURA_RESIZE_SHADOW_BOTTOM_RIGHT" file="common/resize_shadow_bottom_right.png" />
@@ -132,6 +136,7 @@
 
       <!-- ChromeOS specific icons -->
       <if expr="pp_ifdef('chromeos')">
+        <structure type="chrome_scaled_image" name="IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER" file="cros/notification/notification_low_power_charger.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_ENTERPRISE_DARK" file="cros/status/status_managed.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_MANAGED_USER" file="cros/status/status_managed_mode_user.png" />
         <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_NETWORK_1X" file="cros/network/statusbar_network_1x.png" />
diff --git a/ash/resources/default_100_percent/common/alt_launcher/launcher_background.png b/ash/resources/default_100_percent/common/alt_launcher/launcher_background.png
index 38951a4..45b06ed 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/launcher_background.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/launcher_background.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_active.png b/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_active.png
index 7812473..6a69d17 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_active.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_active.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_running.png b/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_running.png
index 2360bb7..9bcff86 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_running.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/launcher_underline_running.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_app_menu_icon.png b/ash/resources/default_100_percent/common/alt_launcher/status_app_menu_icon.png
index 40c5082..5e5f18b 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_app_menu_icon.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_app_menu_icon.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_normal.png b/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_normal.png
index 4c0299a..565c501 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_normal.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_onblack_normal.png b/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_onblack_normal.png
index f1c8389..41ba35c 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_onblack_normal.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_onblack_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_pressed.png b/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_pressed.png
index 1325562..d8cdd77 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_pressed.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_icon_background_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_notification_icon.png b/ash/resources/default_100_percent/common/alt_launcher/status_notification_icon.png
index e941a3f..388c1e0 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_notification_icon.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_notification_icon.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_center.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_center.png
index 2616bc3..7f895ba 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_center.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_center.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_left.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_left.png
index 0252ad7..8b00443 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_left.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_left.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_center.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_center.png
index ffc9223..0d26eb2 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_center.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_center.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_left.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_left.png
index d0affb9..3484127 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_left.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_left.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_right.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_right.png
index cd1d9ee..5787128 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_right.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_onblack_right.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_right.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_right.png
index 7608c85..850e80f 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_right.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_normal_right.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_center.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_center.png
index 2eb9556..3eb461d 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_center.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_center.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_left.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_left.png
index 686332c..969c86b 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_left.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_left.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_right.png b/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_right.png
index 36347a0..8ec7227 100644
--- a/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_right.png
+++ b/ash/resources/default_100_percent/common/alt_launcher/status_tray_pressed_right.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/chromium-32.png b/ash/resources/default_100_percent/common/chromium-32.png
index 4266218..e708447 100644
--- a/ash/resources/default_100_percent/common/chromium-32.png
+++ b/ash/resources/default_100_percent/common/chromium-32.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_appmenu.png b/ash/resources/default_100_percent/common/launcher/launcher_appmenu.png
index 13118c8..719d772 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_appmenu.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_appmenu.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_appmenu_hover.png b/ash/resources/default_100_percent/common/launcher/launcher_appmenu_hover.png
index bafb356..a41d0e4 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_appmenu_hover.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_appmenu_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_appmenu_pressed.png b/ash/resources/default_100_percent/common/launcher/launcher_appmenu_pressed.png
index c6f0ce0..98750f8 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_appmenu_pressed.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_appmenu_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_browser.png b/ash/resources/default_100_percent/common/launcher/launcher_browser.png
index 9a5ee09..669a3a9 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_browser.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_browser.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png b/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png
index ba32624..b974cb3 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_browser_panel.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png b/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png
index 870bd9c..690de02 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_incognito_browser.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_overflow.png b/ash/resources/default_100_percent/common/launcher/launcher_overflow.png
index d9c555d..05cc71d 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_overflow.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_overflow.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_underline_left_running.png b/ash/resources/default_100_percent/common/launcher/launcher_underline_left_running.png
index d70f0eb..6a4294f 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_underline_left_running.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_underline_left_running.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_underline_right_active.png b/ash/resources/default_100_percent/common/launcher/launcher_underline_right_active.png
index 01e392e..82572f2 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_underline_right_active.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_underline_right_active.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_underline_right_running.png b/ash/resources/default_100_percent/common/launcher/launcher_underline_right_running.png
index 7e96267..24de3c0 100644
--- a/ash/resources/default_100_percent/common/launcher/launcher_underline_right_running.png
+++ b/ash/resources/default_100_percent/common/launcher/launcher_underline_right_running.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/launcher/task_manager.png b/ash/resources/default_100_percent/common/launcher/task_manager.png
index 3b4e561..293bc76 100644
--- a/ash/resources/default_100_percent/common/launcher/task_manager.png
+++ b/ash/resources/default_100_percent/common/launcher/task_manager.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/multi_window_resize_horizontal.png b/ash/resources/default_100_percent/common/multi_window_resize_horizontal.png
index 0f9f63d..0f17aa9 100644
--- a/ash/resources/default_100_percent/common/multi_window_resize_horizontal.png
+++ b/ash/resources/default_100_percent/common/multi_window_resize_horizontal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/multi_window_resize_vertical.png b/ash/resources/default_100_percent/common/multi_window_resize_vertical.png
index c3e0750..7dceda4 100644
--- a/ash/resources/default_100_percent/common/multi_window_resize_vertical.png
+++ b/ash/resources/default_100_percent/common/multi_window_resize_vertical.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/notification/notification_button_active_hover.png b/ash/resources/default_100_percent/common/notification/notification_button_active_hover.png
index cd0c8dc..7a39af0 100644
--- a/ash/resources/default_100_percent/common/notification/notification_button_active_hover.png
+++ b/ash/resources/default_100_percent/common/notification/notification_button_active_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/notification/notification_button_active_normal.png b/ash/resources/default_100_percent/common/notification/notification_button_active_normal.png
index cd0c8dc..391b130 100644
--- a/ash/resources/default_100_percent/common/notification/notification_button_active_normal.png
+++ b/ash/resources/default_100_percent/common/notification/notification_button_active_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/notification/notification_button_active_pressed.png b/ash/resources/default_100_percent/common/notification/notification_button_active_pressed.png
index 6551999..309b888 100644
--- a/ash/resources/default_100_percent/common/notification/notification_button_active_pressed.png
+++ b/ash/resources/default_100_percent/common/notification/notification_button_active_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/notification/notification_button_inactive_hover.png b/ash/resources/default_100_percent/common/notification/notification_button_inactive_hover.png
index 99dd7ae..eb6788a 100644
--- a/ash/resources/default_100_percent/common/notification/notification_button_inactive_hover.png
+++ b/ash/resources/default_100_percent/common/notification/notification_button_inactive_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/notification/notification_button_inactive_normal.png b/ash/resources/default_100_percent/common/notification/notification_button_inactive_normal.png
index b81c965..659decf 100644
--- a/ash/resources/default_100_percent/common/notification/notification_button_inactive_normal.png
+++ b/ash/resources/default_100_percent/common/notification/notification_button_inactive_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/notification/notification_button_inactive_pressed.png b/ash/resources/default_100_percent/common/notification/notification_button_inactive_pressed.png
index fe56d69..44b62ee 100644
--- a/ash/resources/default_100_percent/common/notification/notification_button_inactive_pressed.png
+++ b/ash/resources/default_100_percent/common/notification/notification_button_inactive_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_fullscreen_hover.png b/ash/resources/default_100_percent/common/window_close_fullscreen_hover.png
index 3729d47..bd6cffa 100644
--- a/ash/resources/default_100_percent/common/window_close_fullscreen_hover.png
+++ b/ash/resources/default_100_percent/common/window_close_fullscreen_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_fullscreen_normal.png b/ash/resources/default_100_percent/common/window_close_fullscreen_normal.png
index 44a19c1..d9b19f1 100644
--- a/ash/resources/default_100_percent/common/window_close_fullscreen_normal.png
+++ b/ash/resources/default_100_percent/common/window_close_fullscreen_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_fullscreen_pressed.png b/ash/resources/default_100_percent/common/window_close_fullscreen_pressed.png
index 16b7da6..a0a0cc3 100644
--- a/ash/resources/default_100_percent/common/window_close_fullscreen_pressed.png
+++ b/ash/resources/default_100_percent/common/window_close_fullscreen_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_short_black_hover.png b/ash/resources/default_100_percent/common/window_close_short_black_hover.png
index 414d999..3d46487 100644
--- a/ash/resources/default_100_percent/common/window_close_short_black_hover.png
+++ b/ash/resources/default_100_percent/common/window_close_short_black_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_short_black_normal.png b/ash/resources/default_100_percent/common/window_close_short_black_normal.png
index 0735ffa..23010ea 100644
--- a/ash/resources/default_100_percent/common/window_close_short_black_normal.png
+++ b/ash/resources/default_100_percent/common/window_close_short_black_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_short_black_pressed.png b/ash/resources/default_100_percent/common/window_close_short_black_pressed.png
index c991375..703fce2 100644
--- a/ash/resources/default_100_percent/common/window_close_short_black_pressed.png
+++ b/ash/resources/default_100_percent/common/window_close_short_black_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_short_hover.png b/ash/resources/default_100_percent/common/window_close_short_hover.png
index d94256a..2e27e58 100644
--- a/ash/resources/default_100_percent/common/window_close_short_hover.png
+++ b/ash/resources/default_100_percent/common/window_close_short_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_short_normal.png b/ash/resources/default_100_percent/common/window_close_short_normal.png
index 44a19c1..a49779a 100644
--- a/ash/resources/default_100_percent/common/window_close_short_normal.png
+++ b/ash/resources/default_100_percent/common/window_close_short_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_short_pressed.png b/ash/resources/default_100_percent/common/window_close_short_pressed.png
index e191da2..31f9b6e 100644
--- a/ash/resources/default_100_percent/common/window_close_short_pressed.png
+++ b/ash/resources/default_100_percent/common/window_close_short_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_tall_hover.png b/ash/resources/default_100_percent/common/window_close_tall_hover.png
index 5cef0ab..400a398 100644
--- a/ash/resources/default_100_percent/common/window_close_tall_hover.png
+++ b/ash/resources/default_100_percent/common/window_close_tall_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_tall_normal.png b/ash/resources/default_100_percent/common/window_close_tall_normal.png
index 0c6e564..a3384f8 100644
--- a/ash/resources/default_100_percent/common/window_close_tall_normal.png
+++ b/ash/resources/default_100_percent/common/window_close_tall_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_close_tall_pressed.png b/ash/resources/default_100_percent/common/window_close_tall_pressed.png
index 034d592..323135d 100644
--- a/ash/resources/default_100_percent/common/window_close_tall_pressed.png
+++ b/ash/resources/default_100_percent/common/window_close_tall_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_fullscreen_shadow.png b/ash/resources/default_100_percent/common/window_fullscreen_shadow.png
index 391ac3f..8a6e37f 100644
--- a/ash/resources/default_100_percent/common/window_fullscreen_shadow.png
+++ b/ash/resources/default_100_percent/common/window_fullscreen_shadow.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_fullscreen_shadow_rtl.png b/ash/resources/default_100_percent/common/window_fullscreen_shadow_rtl.png
index 8214fc2..6439bdc 100644
--- a/ash/resources/default_100_percent/common/window_fullscreen_shadow_rtl.png
+++ b/ash/resources/default_100_percent/common/window_fullscreen_shadow_rtl.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_enter_normal.png b/ash/resources/default_100_percent/common/window_immersive_enter_normal.png
index d6e1203..bdb7c2a 100644
--- a/ash/resources/default_100_percent/common/window_immersive_enter_normal.png
+++ b/ash/resources/default_100_percent/common/window_immersive_enter_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png b/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png
index fc99c86..095a17f 100644
--- a/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png
+++ b/ash/resources/default_100_percent/common/window_immersive_enter_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_exit_hover.png b/ash/resources/default_100_percent/common/window_immersive_exit_hover.png
index bc8f52b..a0d6e04 100644
--- a/ash/resources/default_100_percent/common/window_immersive_exit_hover.png
+++ b/ash/resources/default_100_percent/common/window_immersive_exit_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_exit_normal.png b/ash/resources/default_100_percent/common/window_immersive_exit_normal.png
index b728735..4b6fd37 100644
--- a/ash/resources/default_100_percent/common/window_immersive_exit_normal.png
+++ b/ash/resources/default_100_percent/common/window_immersive_exit_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png b/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png
index f115f8f..cc903e9 100644
--- a/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png
+++ b/ash/resources/default_100_percent/common/window_immersive_exit_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_minimize_short_hover.png b/ash/resources/default_100_percent/common/window_minimize_short_hover.png
index 379e729..ad54e15 100644
--- a/ash/resources/default_100_percent/common/window_minimize_short_hover.png
+++ b/ash/resources/default_100_percent/common/window_minimize_short_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_minimize_short_normal.png b/ash/resources/default_100_percent/common/window_minimize_short_normal.png
index 2275079..e4eb416 100644
--- a/ash/resources/default_100_percent/common/window_minimize_short_normal.png
+++ b/ash/resources/default_100_percent/common/window_minimize_short_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_minimize_short_pressed.png b/ash/resources/default_100_percent/common/window_minimize_short_pressed.png
index d2fef09..32bccbe 100644
--- a/ash/resources/default_100_percent/common/window_minimize_short_pressed.png
+++ b/ash/resources/default_100_percent/common/window_minimize_short_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_left_hover.png b/ash/resources/default_100_percent/common/window_position_left_hover.png
index 694116e..420d392 100644
--- a/ash/resources/default_100_percent/common/window_position_left_hover.png
+++ b/ash/resources/default_100_percent/common/window_position_left_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_left_hover_restore.png b/ash/resources/default_100_percent/common/window_position_left_hover_restore.png
index 9400be0..f94002b 100644
--- a/ash/resources/default_100_percent/common/window_position_left_hover_restore.png
+++ b/ash/resources/default_100_percent/common/window_position_left_hover_restore.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_left_pressed.png b/ash/resources/default_100_percent/common/window_position_left_pressed.png
index c716194..4201d2a 100644
--- a/ash/resources/default_100_percent/common/window_position_left_pressed.png
+++ b/ash/resources/default_100_percent/common/window_position_left_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_left_pressed_restore.png b/ash/resources/default_100_percent/common/window_position_left_pressed_restore.png
index 6f6e5b5..8841553 100644
--- a/ash/resources/default_100_percent/common/window_position_left_pressed_restore.png
+++ b/ash/resources/default_100_percent/common/window_position_left_pressed_restore.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_middle_hover.png b/ash/resources/default_100_percent/common/window_position_middle_hover.png
index e4d5b63..aaff1c5 100644
--- a/ash/resources/default_100_percent/common/window_position_middle_hover.png
+++ b/ash/resources/default_100_percent/common/window_position_middle_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_right_hover.png b/ash/resources/default_100_percent/common/window_position_right_hover.png
index 248d1a4..e424d95 100644
--- a/ash/resources/default_100_percent/common/window_position_right_hover.png
+++ b/ash/resources/default_100_percent/common/window_position_right_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_right_hover_restore.png b/ash/resources/default_100_percent/common/window_position_right_hover_restore.png
index 0081c21..7583d26 100644
--- a/ash/resources/default_100_percent/common/window_position_right_hover_restore.png
+++ b/ash/resources/default_100_percent/common/window_position_right_hover_restore.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_right_normal_restore.png b/ash/resources/default_100_percent/common/window_position_right_normal_restore.png
index 3abc648..9da0761 100644
--- a/ash/resources/default_100_percent/common/window_position_right_normal_restore.png
+++ b/ash/resources/default_100_percent/common/window_position_right_normal_restore.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_right_pressed.png b/ash/resources/default_100_percent/common/window_position_right_pressed.png
index 5c68cc5..f002fd4 100644
--- a/ash/resources/default_100_percent/common/window_position_right_pressed.png
+++ b/ash/resources/default_100_percent/common/window_position_right_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_position_right_pressed_restore.png b/ash/resources/default_100_percent/common/window_position_right_pressed_restore.png
index 62cae5f..790ee0e 100644
--- a/ash/resources/default_100_percent/common/window_position_right_pressed_restore.png
+++ b/ash/resources/default_100_percent/common/window_position_right_pressed_restore.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_fullscreen_hover.png b/ash/resources/default_100_percent/common/window_size_fullscreen_hover.png
index fc18aa3..a85c2b7 100644
--- a/ash/resources/default_100_percent/common/window_size_fullscreen_hover.png
+++ b/ash/resources/default_100_percent/common/window_size_fullscreen_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_fullscreen_normal.png b/ash/resources/default_100_percent/common/window_size_fullscreen_normal.png
index 2fca36c..500e8a4 100644
--- a/ash/resources/default_100_percent/common/window_size_fullscreen_normal.png
+++ b/ash/resources/default_100_percent/common/window_size_fullscreen_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_fullscreen_pressed.png b/ash/resources/default_100_percent/common/window_size_fullscreen_pressed.png
index fea4748..6afc601 100644
--- a/ash/resources/default_100_percent/common/window_size_fullscreen_pressed.png
+++ b/ash/resources/default_100_percent/common/window_size_fullscreen_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_short_hover.png b/ash/resources/default_100_percent/common/window_size_short_hover.png
index e3346e2..c52c4aa 100644
--- a/ash/resources/default_100_percent/common/window_size_short_hover.png
+++ b/ash/resources/default_100_percent/common/window_size_short_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_short_normal.png b/ash/resources/default_100_percent/common/window_size_short_normal.png
index 2ce5efb..ebe911f 100644
--- a/ash/resources/default_100_percent/common/window_size_short_normal.png
+++ b/ash/resources/default_100_percent/common/window_size_short_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_short_pressed.png b/ash/resources/default_100_percent/common/window_size_short_pressed.png
index f1f3bdc..cf7cde8 100644
--- a/ash/resources/default_100_percent/common/window_size_short_pressed.png
+++ b/ash/resources/default_100_percent/common/window_size_short_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_tall_hover.png b/ash/resources/default_100_percent/common/window_size_tall_hover.png
index 02a5adf..393beaf 100644
--- a/ash/resources/default_100_percent/common/window_size_tall_hover.png
+++ b/ash/resources/default_100_percent/common/window_size_tall_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_tall_normal.png b/ash/resources/default_100_percent/common/window_size_tall_normal.png
index 5bff897..bba5130 100644
--- a/ash/resources/default_100_percent/common/window_size_tall_normal.png
+++ b/ash/resources/default_100_percent/common/window_size_tall_normal.png
Binary files differ
diff --git a/ash/resources/default_100_percent/common/window_size_tall_pressed.png b/ash/resources/default_100_percent/common/window_size_tall_pressed.png
index a3bf843..9047b4a 100644
--- a/ash/resources/default_100_percent/common/window_size_tall_pressed.png
+++ b/ash/resources/default_100_percent/common/window_size_tall_pressed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/common/default_throbber.png b/ash/resources/default_100_percent/cros/common/default_throbber.png
index c8bdf37..47eeb2c 100644
--- a/ash/resources/default_100_percent/cros/common/default_throbber.png
+++ b/ash/resources/default_100_percent/cros/common/default_throbber.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/notification_3g.png b/ash/resources/default_100_percent/cros/network/notification_3g.png
index cfc2a15..ced1c67 100644
--- a/ash/resources/default_100_percent/cros/network/notification_3g.png
+++ b/ash/resources/default_100_percent/cros/network/notification_3g.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/notification_lte.png b/ash/resources/default_100_percent/cros/network/notification_lte.png
index f707a1c..6139623 100644
--- a/ash/resources/default_100_percent/cros/network/notification_lte.png
+++ b/ash/resources/default_100_percent/cros/network/notification_lte.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_cellular_disabled.png b/ash/resources/default_100_percent/cros/network/status_cellular_disabled.png
index 5d68e87..a00aecc 100644
--- a/ash/resources/default_100_percent/cros/network/status_cellular_disabled.png
+++ b/ash/resources/default_100_percent/cros/network/status_cellular_disabled.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_cellular_disabled_hover.png b/ash/resources/default_100_percent/cros/network/status_cellular_disabled_hover.png
index 9e599c7..348682b 100644
--- a/ash/resources/default_100_percent/cros/network/status_cellular_disabled_hover.png
+++ b/ash/resources/default_100_percent/cros/network/status_cellular_disabled_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_cellular_enabled.png b/ash/resources/default_100_percent/cros/network/status_cellular_enabled.png
index 0ce9dc4..cc5b733 100644
--- a/ash/resources/default_100_percent/cros/network/status_cellular_enabled.png
+++ b/ash/resources/default_100_percent/cros/network/status_cellular_enabled.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_cellular_enabled_hover.png b/ash/resources/default_100_percent/cros/network/status_cellular_enabled_hover.png
index d8326b1..77879ed 100644
--- a/ash/resources/default_100_percent/cros/network/status_cellular_enabled_hover.png
+++ b/ash/resources/default_100_percent/cros/network/status_cellular_enabled_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_cellular_failed.png b/ash/resources/default_100_percent/cros/network/status_cellular_failed.png
index c70d10e..412d9d4 100644
--- a/ash/resources/default_100_percent/cros/network/status_cellular_failed.png
+++ b/ash/resources/default_100_percent/cros/network/status_cellular_failed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_data_low.png b/ash/resources/default_100_percent/cros/network/status_data_low.png
index 7df1f9d..b757e5f 100644
--- a/ash/resources/default_100_percent/cros/network/status_data_low.png
+++ b/ash/resources/default_100_percent/cros/network/status_data_low.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/status_data_none.png b/ash/resources/default_100_percent/cros/network/status_data_none.png
index 1548778..07aee20 100644
--- a/ash/resources/default_100_percent/cros/network/status_data_none.png
+++ b/ash/resources/default_100_percent/cros/network/status_data_none.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_dark.png b/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_dark.png
index 1ae409f..29b6c72 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_dark.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_light.png b/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_light.png
index 512f976..2d783f6 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_light.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_arcs_light.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_bars_dark.png b/ash/resources/default_100_percent/cros/network/statusbar_network_bars_dark.png
index d0fa516..9facf9f 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_bars_dark.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_bars_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_bars_light.png b/ash/resources/default_100_percent/cros/network/statusbar_network_bars_light.png
index 15dc018..4f77026 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_bars_light.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_bars_light.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_hspa_plus_dark.png b/ash/resources/default_100_percent/cros/network/statusbar_network_hspa_plus_dark.png
index 3c74a7f..2b34e39 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_hspa_plus_dark.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_hspa_plus_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_lte_advanced_dark.png b/ash/resources/default_100_percent/cros/network/statusbar_network_lte_advanced_dark.png
index 2d37ee0..db3ab77 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_lte_advanced_dark.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_lte_advanced_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_network_lte_dark.png b/ash/resources/default_100_percent/cros/network/statusbar_network_lte_dark.png
index e93bf61..84b7c7c 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_network_lte_dark.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_network_lte_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/network/statusbar_wired.png b/ash/resources/default_100_percent/cros/network/statusbar_wired.png
index 22a8061..14335f7 100644
--- a/ash/resources/default_100_percent/cros/network/statusbar_wired.png
+++ b/ash/resources/default_100_percent/cros/network/statusbar_wired.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/notification/notification_low_power_charger.png b/ash/resources/default_100_percent/cros/notification/notification_low_power_charger.png
new file mode 100644
index 0000000..d410c4b
--- /dev/null
+++ b/ash/resources/default_100_percent/cros/notification/notification_low_power_charger.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/multiprofiles_add.png b/ash/resources/default_100_percent/cros/status/multiprofiles_add.png
index e464320..0e8a7a1 100644
--- a/ash/resources/default_100_percent/cros/status/multiprofiles_add.png
+++ b/ash/resources/default_100_percent/cros/status/multiprofiles_add.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_audio_device_bluetooth.png b/ash/resources/default_100_percent/cros/status/status_audio_device_bluetooth.png
index 6c8a44a..073b821 100644
--- a/ash/resources/default_100_percent/cros/status/status_audio_device_bluetooth.png
+++ b/ash/resources/default_100_percent/cros/status/status_audio_device_bluetooth.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_audio_device_usb.png b/ash/resources/default_100_percent/cros/status/status_audio_device_usb.png
index d3f9ddd..b093268 100644
--- a/ash/resources/default_100_percent/cros/status/status_audio_device_usb.png
+++ b/ash/resources/default_100_percent/cros/status/status_audio_device_usb.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled.png b/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled.png
index 508866a..4f8cae0 100644
--- a/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled.png
+++ b/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled_hover.png b/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled_hover.png
index de3977c..66b1dd2 100644
--- a/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled_hover.png
+++ b/ash/resources/default_100_percent/cros/status/status_bluetooth_disabled_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled.png b/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled.png
index 85298b2..c06841b 100644
--- a/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled.png
+++ b/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled_hover.png b/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled_hover.png
index 4171c0d..0bf56fb 100644
--- a/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled_hover.png
+++ b/ash/resources/default_100_percent/cros/status/status_bluetooth_enabled_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_brightness.png b/ash/resources/default_100_percent/cros/status/status_brightness.png
index 5c2f161..08ce57a 100644
--- a/ash/resources/default_100_percent/cros/status/status_brightness.png
+++ b/ash/resources/default_100_percent/cros/status/status_brightness.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_capslock.png b/ash/resources/default_100_percent/cros/status/status_capslock.png
index 55191f1..8b0e095 100644
--- a/ash/resources/default_100_percent/cros/status/status_capslock.png
+++ b/ash/resources/default_100_percent/cros/status/status_capslock.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_display.png b/ash/resources/default_100_percent/cros/status/status_display.png
index 4c82782..df3f63d 100644
--- a/ash/resources/default_100_percent/cros/status/status_display.png
+++ b/ash/resources/default_100_percent/cros/status/status_display.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_display_dark.png b/ash/resources/default_100_percent/cros/status/status_display_dark.png
index cf8cda9..6bb251b 100644
--- a/ash/resources/default_100_percent/cros/status/status_display_dark.png
+++ b/ash/resources/default_100_percent/cros/status/status_display_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_drive_item_failed.png b/ash/resources/default_100_percent/cros/status/status_drive_item_failed.png
index 99ebc18..09e0c81 100644
--- a/ash/resources/default_100_percent/cros/status/status_drive_item_failed.png
+++ b/ash/resources/default_100_percent/cros/status/status_drive_item_failed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_managed.png b/ash/resources/default_100_percent/cros/status/status_managed.png
index c0cf96f..88155e7 100644
--- a/ash/resources/default_100_percent/cros/status/status_managed.png
+++ b/ash/resources/default_100_percent/cros/status/status_managed.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_managed_mode_user.png b/ash/resources/default_100_percent/cros/status/status_managed_mode_user.png
index 02066e5..5110417 100644
--- a/ash/resources/default_100_percent/cros/status/status_managed_mode_user.png
+++ b/ash/resources/default_100_percent/cros/status/status_managed_mode_user.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_managed_tray.png b/ash/resources/default_100_percent/cros/status/status_managed_tray.png
index 398b2cc..709546e 100644
--- a/ash/resources/default_100_percent/cros/status/status_managed_tray.png
+++ b/ash/resources/default_100_percent/cros/status/status_managed_tray.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_more.png b/ash/resources/default_100_percent/cros/status/status_more.png
index 23be76e..6c27f4e 100644
--- a/ash/resources/default_100_percent/cros/status/status_more.png
+++ b/ash/resources/default_100_percent/cros/status/status_more.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_power_small_all.png b/ash/resources/default_100_percent/cros/status/status_power_small_all.png
index 58747de..f09e1fc 100644
--- a/ash/resources/default_100_percent/cros/status/status_power_small_all.png
+++ b/ash/resources/default_100_percent/cros/status/status_power_small_all.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_power_small_all_dark.png b/ash/resources/default_100_percent/cros/status/status_power_small_all_dark.png
index e827389..c5f4a86 100644
--- a/ash/resources/default_100_percent/cros/status/status_power_small_all_dark.png
+++ b/ash/resources/default_100_percent/cros/status/status_power_small_all_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_discharging.png b/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_discharging.png
index 4088573..19bcaa1 100644
--- a/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_discharging.png
+++ b/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_discharging.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_fluctuating.png b/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_fluctuating.png
index eaf7982..d8a10e8 100644
--- a/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_fluctuating.png
+++ b/ash/resources/default_100_percent/cros/status/status_power_small_all_dark_fluctuating.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_power_small_all_discharging.png b/ash/resources/default_100_percent/cros/status/status_power_small_all_discharging.png
index 52f127d..108d98b 100644
--- a/ash/resources/default_100_percent/cros/status/status_power_small_all_discharging.png
+++ b/ash/resources/default_100_percent/cros/status/status_power_small_all_discharging.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_power_small_all_fluctuating.png b/ash/resources/default_100_percent/cros/status/status_power_small_all_fluctuating.png
index 0ca74f2..3c37c59 100644
--- a/ash/resources/default_100_percent/cros/status/status_power_small_all_fluctuating.png
+++ b/ash/resources/default_100_percent/cros/status/status_power_small_all_fluctuating.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_session_length_limit_timer.png b/ash/resources/default_100_percent/cros/status/status_session_length_limit_timer.png
index c8016c1..2e0fd04 100644
--- a/ash/resources/default_100_percent/cros/status/status_session_length_limit_timer.png
+++ b/ash/resources/default_100_percent/cros/status/status_session_length_limit_timer.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_shutdown.png b/ash/resources/default_100_percent/cros/status/status_shutdown.png
index abd2a97..00328f9 100644
--- a/ash/resources/default_100_percent/cros/status/status_shutdown.png
+++ b/ash/resources/default_100_percent/cros/status/status_shutdown.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_shutdown_hover.png b/ash/resources/default_100_percent/cros/status/status_shutdown_hover.png
index 22cc83c..0658652 100644
--- a/ash/resources/default_100_percent/cros/status/status_shutdown_hover.png
+++ b/ash/resources/default_100_percent/cros/status/status_shutdown_hover.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_sms.png b/ash/resources/default_100_percent/cros/status/status_sms.png
index 6074d43..093ce58 100644
--- a/ash/resources/default_100_percent/cros/status/status_sms.png
+++ b/ash/resources/default_100_percent/cros/status/status_sms.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_update.png b/ash/resources/default_100_percent/cros/status/status_update.png
index 591a827..2489bbb 100644
--- a/ash/resources/default_100_percent/cros/status/status_update.png
+++ b/ash/resources/default_100_percent/cros/status/status_update.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_update_orange.png b/ash/resources/default_100_percent/cros/status/status_update_orange.png
index 7442d50..43d61c7 100644
--- a/ash/resources/default_100_percent/cros/status/status_update_orange.png
+++ b/ash/resources/default_100_percent/cros/status/status_update_orange.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_update_red.png b/ash/resources/default_100_percent/cros/status/status_update_red.png
index f6ef96ac..a73b85a 100644
--- a/ash/resources/default_100_percent/cros/status/status_update_red.png
+++ b/ash/resources/default_100_percent/cros/status/status_update_red.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_volume_dark.png b/ash/resources/default_100_percent/cros/status/status_volume_dark.png
index ecd5962..76dc185 100644
--- a/ash/resources/default_100_percent/cros/status/status_volume_dark.png
+++ b/ash/resources/default_100_percent/cros/status/status_volume_dark.png
Binary files differ
diff --git a/ash/resources/default_100_percent/cros/status/status_volume_mute.png b/ash/resources/default_100_percent/cros/status/status_volume_mute.png
index dca2cb4..04aded3 100644
--- a/ash/resources/default_100_percent/cros/status/status_volume_mute.png
+++ b/ash/resources/default_100_percent/cros/status/status_volume_mute.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alert_small.png b/ash/resources/default_200_percent/common/alert_small.png
index bf1b90a..544f98a 100644
--- a/ash/resources/default_200_percent/common/alert_small.png
+++ b/ash/resources/default_200_percent/common/alert_small.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/launcher_background.png b/ash/resources/default_200_percent/common/alt_launcher/launcher_background.png
index b68ef01..5a62204 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/launcher_background.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/launcher_background.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_active.png b/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_active.png
index d057bf3..f89d8f5 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_active.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_active.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_running.png b/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_running.png
index 06d9f68..ede3d2d 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_running.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/launcher_underline_running.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_app_menu_icon.png b/ash/resources/default_200_percent/common/alt_launcher/status_app_menu_icon.png
index 1ac214f..2e1a70f 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_app_menu_icon.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_app_menu_icon.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_normal.png b/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_normal.png
index 3cf8a42..cc2d1b7 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_normal.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_onblack_normal.png b/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_onblack_normal.png
index b286103..55bac58 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_onblack_normal.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_onblack_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_pressed.png b/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_pressed.png
index 082a876..498b80b 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_pressed.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_icon_background_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_notification_icon.png b/ash/resources/default_200_percent/common/alt_launcher/status_notification_icon.png
index b8f252d..19322c5 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_notification_icon.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_notification_icon.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_center.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_center.png
index 6de16b0..9b07237 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_center.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_center.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_left.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_left.png
index e50e4e3..dba3b69 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_left.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_left.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_center.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_center.png
index 6a5a985..ea17751 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_center.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_center.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_left.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_left.png
index 38ea70b..ff4e1ef 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_left.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_left.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_right.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_right.png
index ab08fd1..9284fa8 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_right.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_onblack_right.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_right.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_right.png
index 0a855c1..bb129f5 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_right.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_normal_right.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_center.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_center.png
index e397005..953e1ed 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_center.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_center.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_left.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_left.png
index 619dd69..0ad8bd1 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_left.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_left.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_right.png b/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_right.png
index a574cab..27207a1 100644
--- a/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_right.png
+++ b/ash/resources/default_200_percent/common/alt_launcher/status_tray_pressed_right.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_appmenu_hover.png b/ash/resources/default_200_percent/common/launcher/launcher_appmenu_hover.png
index 5758ec9..7762f27 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_appmenu_hover.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_appmenu_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_appmenu_pressed.png b/ash/resources/default_200_percent/common/launcher/launcher_appmenu_pressed.png
index dd40409..82ca4b2 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_appmenu_pressed.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_appmenu_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_background_right.png b/ash/resources/default_200_percent/common/launcher/launcher_background_right.png
index bbab21c..593780b 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_background_right.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_background_right.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_browser.png b/ash/resources/default_200_percent/common/launcher/launcher_browser.png
index 5fc5e5b..0f02221 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_browser.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_browser.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png b/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png
index d5a3609..457026e 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_browser_panel.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png b/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png
index 45f0cfa..f6d4821 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png b/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png
index 87e923a..d0e2c63 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_incognito_browser_panel.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_overflow.png b/ash/resources/default_200_percent/common/launcher/launcher_overflow.png
index 895d8f7..bddc06c 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_overflow.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_overflow.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_active.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_active.png
index 2246c96..2118115 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_active.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_active.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_running.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_running.png
index 58d1343..7b7e3e2 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_running.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_bottom_running.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_left_active.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_left_active.png
index f4522fe..c2937e7 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_left_active.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_left_active.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_left_hover.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_left_hover.png
index f4522fe..fc1ee96 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_left_hover.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_left_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_left_running.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_left_running.png
index c281866..d7ac049 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_left_running.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_left_running.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_right_active.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_right_active.png
index 9757703..2f9ac00 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_right_active.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_right_active.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_right_hover.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_right_hover.png
index 9757703..2f9ac00 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_right_hover.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_right_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_underline_right_running.png b/ash/resources/default_200_percent/common/launcher/launcher_underline_right_running.png
index 974c8c5..8fade19 100644
--- a/ash/resources/default_200_percent/common/launcher/launcher_underline_right_running.png
+++ b/ash/resources/default_200_percent/common/launcher/launcher_underline_right_running.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/task_manager.png b/ash/resources/default_200_percent/common/launcher/task_manager.png
index ecd9176..63b1821 100644
--- a/ash/resources/default_200_percent/common/launcher/task_manager.png
+++ b/ash/resources/default_200_percent/common/launcher/task_manager.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/window_switcher_icon_normal.png b/ash/resources/default_200_percent/common/launcher/window_switcher_icon_normal.png
index accf7d4..a718a45 100644
--- a/ash/resources/default_200_percent/common/launcher/window_switcher_icon_normal.png
+++ b/ash/resources/default_200_percent/common/launcher/window_switcher_icon_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/multi_window_resize_vertical.png b/ash/resources/default_200_percent/common/multi_window_resize_vertical.png
index 2f56fa5..80f7d62 100644
--- a/ash/resources/default_200_percent/common/multi_window_resize_vertical.png
+++ b/ash/resources/default_200_percent/common/multi_window_resize_vertical.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_button_active_hover.png b/ash/resources/default_200_percent/common/notification/notification_button_active_hover.png
index 4327172..be2ea52 100644
--- a/ash/resources/default_200_percent/common/notification/notification_button_active_hover.png
+++ b/ash/resources/default_200_percent/common/notification/notification_button_active_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_button_active_normal.png b/ash/resources/default_200_percent/common/notification/notification_button_active_normal.png
index 4327172..328e3b1 100644
--- a/ash/resources/default_200_percent/common/notification/notification_button_active_normal.png
+++ b/ash/resources/default_200_percent/common/notification/notification_button_active_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_button_active_pressed.png b/ash/resources/default_200_percent/common/notification/notification_button_active_pressed.png
index e0643d5..54cb9a0 100644
--- a/ash/resources/default_200_percent/common/notification/notification_button_active_pressed.png
+++ b/ash/resources/default_200_percent/common/notification/notification_button_active_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_button_inactive_hover.png b/ash/resources/default_200_percent/common/notification/notification_button_inactive_hover.png
index aa5b511..fde7fe1 100644
--- a/ash/resources/default_200_percent/common/notification/notification_button_inactive_hover.png
+++ b/ash/resources/default_200_percent/common/notification/notification_button_inactive_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_button_inactive_normal.png b/ash/resources/default_200_percent/common/notification/notification_button_inactive_normal.png
index 75e80fd..c2284c2 100644
--- a/ash/resources/default_200_percent/common/notification/notification_button_inactive_normal.png
+++ b/ash/resources/default_200_percent/common/notification/notification_button_inactive_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_button_inactive_pressed.png b/ash/resources/default_200_percent/common/notification/notification_button_inactive_pressed.png
index 41fdc1f..ac515e7 100644
--- a/ash/resources/default_200_percent/common/notification/notification_button_inactive_pressed.png
+++ b/ash/resources/default_200_percent/common/notification/notification_button_inactive_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/notification/notification_icon.png b/ash/resources/default_200_percent/common/notification/notification_icon.png
index d851cdd..8a44727 100644
--- a/ash/resources/default_200_percent/common/notification/notification_icon.png
+++ b/ash/resources/default_200_percent/common/notification/notification_icon.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_fullscreen_hover.png b/ash/resources/default_200_percent/common/window_close_fullscreen_hover.png
index 2523d2c..450c886 100644
--- a/ash/resources/default_200_percent/common/window_close_fullscreen_hover.png
+++ b/ash/resources/default_200_percent/common/window_close_fullscreen_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_fullscreen_normal.png b/ash/resources/default_200_percent/common/window_close_fullscreen_normal.png
index 843af1f..c077c43 100644
--- a/ash/resources/default_200_percent/common/window_close_fullscreen_normal.png
+++ b/ash/resources/default_200_percent/common/window_close_fullscreen_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_fullscreen_pressed.png b/ash/resources/default_200_percent/common/window_close_fullscreen_pressed.png
index c5ccb7f..5c2ad3f 100644
--- a/ash/resources/default_200_percent/common/window_close_fullscreen_pressed.png
+++ b/ash/resources/default_200_percent/common/window_close_fullscreen_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_short_black_hover.png b/ash/resources/default_200_percent/common/window_close_short_black_hover.png
index 12c0979..f237da7 100644
--- a/ash/resources/default_200_percent/common/window_close_short_black_hover.png
+++ b/ash/resources/default_200_percent/common/window_close_short_black_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_short_black_normal.png b/ash/resources/default_200_percent/common/window_close_short_black_normal.png
index a30ebd2..174bee2 100644
--- a/ash/resources/default_200_percent/common/window_close_short_black_normal.png
+++ b/ash/resources/default_200_percent/common/window_close_short_black_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_short_black_pressed.png b/ash/resources/default_200_percent/common/window_close_short_black_pressed.png
index fc3bd0c..c452fca 100644
--- a/ash/resources/default_200_percent/common/window_close_short_black_pressed.png
+++ b/ash/resources/default_200_percent/common/window_close_short_black_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_short_hover.png b/ash/resources/default_200_percent/common/window_close_short_hover.png
index 1b9e6fa..ad35cc7 100644
--- a/ash/resources/default_200_percent/common/window_close_short_hover.png
+++ b/ash/resources/default_200_percent/common/window_close_short_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_short_normal.png b/ash/resources/default_200_percent/common/window_close_short_normal.png
index c8afdec..d75809f 100644
--- a/ash/resources/default_200_percent/common/window_close_short_normal.png
+++ b/ash/resources/default_200_percent/common/window_close_short_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_short_pressed.png b/ash/resources/default_200_percent/common/window_close_short_pressed.png
index b11bd8f..e136ec5 100644
--- a/ash/resources/default_200_percent/common/window_close_short_pressed.png
+++ b/ash/resources/default_200_percent/common/window_close_short_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_tall_hover.png b/ash/resources/default_200_percent/common/window_close_tall_hover.png
index 8d22dae..011e670 100644
--- a/ash/resources/default_200_percent/common/window_close_tall_hover.png
+++ b/ash/resources/default_200_percent/common/window_close_tall_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_tall_normal.png b/ash/resources/default_200_percent/common/window_close_tall_normal.png
index 9c68fac..dd86f4a 100644
--- a/ash/resources/default_200_percent/common/window_close_tall_normal.png
+++ b/ash/resources/default_200_percent/common/window_close_tall_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_close_tall_pressed.png b/ash/resources/default_200_percent/common/window_close_tall_pressed.png
index 482ae01..b6cfd64 100644
--- a/ash/resources/default_200_percent/common/window_close_tall_pressed.png
+++ b/ash/resources/default_200_percent/common/window_close_tall_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_fullscreen_shadow.png b/ash/resources/default_200_percent/common/window_fullscreen_shadow.png
index 4c987c4..9c41fc0 100644
--- a/ash/resources/default_200_percent/common/window_fullscreen_shadow.png
+++ b/ash/resources/default_200_percent/common/window_fullscreen_shadow.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_header_shade_left.png b/ash/resources/default_200_percent/common/window_header_shade_left.png
index da6e234..46f341e 100644
--- a/ash/resources/default_200_percent/common/window_header_shade_left.png
+++ b/ash/resources/default_200_percent/common/window_header_shade_left.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_enter_hover.png b/ash/resources/default_200_percent/common/window_immersive_enter_hover.png
index 697cd5f..fb796a0 100644
--- a/ash/resources/default_200_percent/common/window_immersive_enter_hover.png
+++ b/ash/resources/default_200_percent/common/window_immersive_enter_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_exit_normal.png b/ash/resources/default_200_percent/common/window_immersive_exit_normal.png
index d972dda..bcf3050 100644
--- a/ash/resources/default_200_percent/common/window_immersive_exit_normal.png
+++ b/ash/resources/default_200_percent/common/window_immersive_exit_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png b/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png
index 199b91d..724f7f2 100644
--- a/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png
+++ b/ash/resources/default_200_percent/common/window_immersive_exit_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_minimize_short_hover.png b/ash/resources/default_200_percent/common/window_minimize_short_hover.png
index 79288b7..452ebc8 100644
--- a/ash/resources/default_200_percent/common/window_minimize_short_hover.png
+++ b/ash/resources/default_200_percent/common/window_minimize_short_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_minimize_short_normal.png b/ash/resources/default_200_percent/common/window_minimize_short_normal.png
index c62b876..29f8470 100644
--- a/ash/resources/default_200_percent/common/window_minimize_short_normal.png
+++ b/ash/resources/default_200_percent/common/window_minimize_short_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_minimize_short_pressed.png b/ash/resources/default_200_percent/common/window_minimize_short_pressed.png
index 3c0a40c..7511d7d 100644
--- a/ash/resources/default_200_percent/common/window_minimize_short_pressed.png
+++ b/ash/resources/default_200_percent/common/window_minimize_short_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_left_hover.png b/ash/resources/default_200_percent/common/window_position_left_hover.png
index 7e69a2a..4d79bb2 100644
--- a/ash/resources/default_200_percent/common/window_position_left_hover.png
+++ b/ash/resources/default_200_percent/common/window_position_left_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_left_hover_restore.png b/ash/resources/default_200_percent/common/window_position_left_hover_restore.png
index 1a6d54e..61c5387 100644
--- a/ash/resources/default_200_percent/common/window_position_left_hover_restore.png
+++ b/ash/resources/default_200_percent/common/window_position_left_hover_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_left_normal_restore.png b/ash/resources/default_200_percent/common/window_position_left_normal_restore.png
index f299844..bba0ff4 100644
--- a/ash/resources/default_200_percent/common/window_position_left_normal_restore.png
+++ b/ash/resources/default_200_percent/common/window_position_left_normal_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_left_pressed.png b/ash/resources/default_200_percent/common/window_position_left_pressed.png
index 73bcc2d..b7fae08 100644
--- a/ash/resources/default_200_percent/common/window_position_left_pressed.png
+++ b/ash/resources/default_200_percent/common/window_position_left_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_left_pressed_restore.png b/ash/resources/default_200_percent/common/window_position_left_pressed_restore.png
index 3507542..6b319db 100644
--- a/ash/resources/default_200_percent/common/window_position_left_pressed_restore.png
+++ b/ash/resources/default_200_percent/common/window_position_left_pressed_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_middle_hover.png b/ash/resources/default_200_percent/common/window_position_middle_hover.png
index 035fc6b..fe65289 100644
--- a/ash/resources/default_200_percent/common/window_position_middle_hover.png
+++ b/ash/resources/default_200_percent/common/window_position_middle_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_middle_pressed.png b/ash/resources/default_200_percent/common/window_position_middle_pressed.png
index 9d0b8d1..f664867 100644
--- a/ash/resources/default_200_percent/common/window_position_middle_pressed.png
+++ b/ash/resources/default_200_percent/common/window_position_middle_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_right_hover.png b/ash/resources/default_200_percent/common/window_position_right_hover.png
index 47fad5c..e31c729 100644
--- a/ash/resources/default_200_percent/common/window_position_right_hover.png
+++ b/ash/resources/default_200_percent/common/window_position_right_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_right_hover_restore.png b/ash/resources/default_200_percent/common/window_position_right_hover_restore.png
index 7d75941..e5a5be3 100644
--- a/ash/resources/default_200_percent/common/window_position_right_hover_restore.png
+++ b/ash/resources/default_200_percent/common/window_position_right_hover_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_right_normal.png b/ash/resources/default_200_percent/common/window_position_right_normal.png
index 8aec6e6..1a8e7a2 100644
--- a/ash/resources/default_200_percent/common/window_position_right_normal.png
+++ b/ash/resources/default_200_percent/common/window_position_right_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_right_normal_restore.png b/ash/resources/default_200_percent/common/window_position_right_normal_restore.png
index 62b4b5f..3cf2d4b 100644
--- a/ash/resources/default_200_percent/common/window_position_right_normal_restore.png
+++ b/ash/resources/default_200_percent/common/window_position_right_normal_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_right_pressed.png b/ash/resources/default_200_percent/common/window_position_right_pressed.png
index c8af642..b5e5336 100644
--- a/ash/resources/default_200_percent/common/window_position_right_pressed.png
+++ b/ash/resources/default_200_percent/common/window_position_right_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_position_right_pressed_restore.png b/ash/resources/default_200_percent/common/window_position_right_pressed_restore.png
index f3ba8cd..62bb151 100644
--- a/ash/resources/default_200_percent/common/window_position_right_pressed_restore.png
+++ b/ash/resources/default_200_percent/common/window_position_right_pressed_restore.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_fullscreen_hover.png b/ash/resources/default_200_percent/common/window_size_fullscreen_hover.png
index c2711ef..2855067 100644
--- a/ash/resources/default_200_percent/common/window_size_fullscreen_hover.png
+++ b/ash/resources/default_200_percent/common/window_size_fullscreen_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_fullscreen_normal.png b/ash/resources/default_200_percent/common/window_size_fullscreen_normal.png
index 0e9ecd2..688028d 100644
--- a/ash/resources/default_200_percent/common/window_size_fullscreen_normal.png
+++ b/ash/resources/default_200_percent/common/window_size_fullscreen_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_fullscreen_pressed.png b/ash/resources/default_200_percent/common/window_size_fullscreen_pressed.png
index fc7d290..ca09716 100644
--- a/ash/resources/default_200_percent/common/window_size_fullscreen_pressed.png
+++ b/ash/resources/default_200_percent/common/window_size_fullscreen_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_short_black_normal.png b/ash/resources/default_200_percent/common/window_size_short_black_normal.png
index ce21dcd..d7cd09e 100644
--- a/ash/resources/default_200_percent/common/window_size_short_black_normal.png
+++ b/ash/resources/default_200_percent/common/window_size_short_black_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_short_black_pressed.png b/ash/resources/default_200_percent/common/window_size_short_black_pressed.png
index a6d0a8d..24d36b2 100644
--- a/ash/resources/default_200_percent/common/window_size_short_black_pressed.png
+++ b/ash/resources/default_200_percent/common/window_size_short_black_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_short_hover.png b/ash/resources/default_200_percent/common/window_size_short_hover.png
index aa624e5..39c3179 100644
--- a/ash/resources/default_200_percent/common/window_size_short_hover.png
+++ b/ash/resources/default_200_percent/common/window_size_short_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_short_normal.png b/ash/resources/default_200_percent/common/window_size_short_normal.png
index 0afbf2f..a318359 100644
--- a/ash/resources/default_200_percent/common/window_size_short_normal.png
+++ b/ash/resources/default_200_percent/common/window_size_short_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_short_pressed.png b/ash/resources/default_200_percent/common/window_size_short_pressed.png
index 2ae9f1b..1aca3db 100644
--- a/ash/resources/default_200_percent/common/window_size_short_pressed.png
+++ b/ash/resources/default_200_percent/common/window_size_short_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_tall_hover.png b/ash/resources/default_200_percent/common/window_size_tall_hover.png
index 1f8ba07..8d33073 100644
--- a/ash/resources/default_200_percent/common/window_size_tall_hover.png
+++ b/ash/resources/default_200_percent/common/window_size_tall_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_tall_normal.png b/ash/resources/default_200_percent/common/window_size_tall_normal.png
index c8e5115..928eb01 100644
--- a/ash/resources/default_200_percent/common/window_size_tall_normal.png
+++ b/ash/resources/default_200_percent/common/window_size_tall_normal.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/window_size_tall_pressed.png b/ash/resources/default_200_percent/common/window_size_tall_pressed.png
index bcc2f8b..3e988f3 100644
--- a/ash/resources/default_200_percent/common/window_size_tall_pressed.png
+++ b/ash/resources/default_200_percent/common/window_size_tall_pressed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/common/default_throbber.png b/ash/resources/default_200_percent/cros/common/default_throbber.png
index ad8247e..1538e41 100644
--- a/ash/resources/default_200_percent/cros/common/default_throbber.png
+++ b/ash/resources/default_200_percent/cros/common/default_throbber.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/notification_3g.png b/ash/resources/default_200_percent/cros/network/notification_3g.png
index bacd42d..6c04076 100644
--- a/ash/resources/default_200_percent/cros/network/notification_3g.png
+++ b/ash/resources/default_200_percent/cros/network/notification_3g.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/notification_lte.png b/ash/resources/default_200_percent/cros/network/notification_lte.png
index a569889..0ca53f7 100644
--- a/ash/resources/default_200_percent/cros/network/notification_lte.png
+++ b/ash/resources/default_200_percent/cros/network/notification_lte.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_cellular_disabled.png b/ash/resources/default_200_percent/cros/network/status_cellular_disabled.png
index 7f75d5f..cd10247 100644
--- a/ash/resources/default_200_percent/cros/network/status_cellular_disabled.png
+++ b/ash/resources/default_200_percent/cros/network/status_cellular_disabled.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_cellular_disabled_hover.png b/ash/resources/default_200_percent/cros/network/status_cellular_disabled_hover.png
index 8e9991b..18ffd31 100644
--- a/ash/resources/default_200_percent/cros/network/status_cellular_disabled_hover.png
+++ b/ash/resources/default_200_percent/cros/network/status_cellular_disabled_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_cellular_enabled.png b/ash/resources/default_200_percent/cros/network/status_cellular_enabled.png
index 7cc460a..5817ab5 100644
--- a/ash/resources/default_200_percent/cros/network/status_cellular_enabled.png
+++ b/ash/resources/default_200_percent/cros/network/status_cellular_enabled.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_cellular_enabled_hover.png b/ash/resources/default_200_percent/cros/network/status_cellular_enabled_hover.png
index 0a27f99..f9c0c60 100644
--- a/ash/resources/default_200_percent/cros/network/status_cellular_enabled_hover.png
+++ b/ash/resources/default_200_percent/cros/network/status_cellular_enabled_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_cellular_failed.png b/ash/resources/default_200_percent/cros/network/status_cellular_failed.png
index a4f254f..282cc07 100644
--- a/ash/resources/default_200_percent/cros/network/status_cellular_failed.png
+++ b/ash/resources/default_200_percent/cros/network/status_cellular_failed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_data_low.png b/ash/resources/default_200_percent/cros/network/status_data_low.png
index 480e25c..e32346c 100644
--- a/ash/resources/default_200_percent/cros/network/status_data_low.png
+++ b/ash/resources/default_200_percent/cros/network/status_data_low.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_data_none.png b/ash/resources/default_200_percent/cros/network/status_data_none.png
index 9c22d8e..3f20d4a 100644
--- a/ash/resources/default_200_percent/cros/network/status_data_none.png
+++ b/ash/resources/default_200_percent/cros/network/status_data_none.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_network_failed.png b/ash/resources/default_200_percent/cros/network/status_network_failed.png
index 1776332..803a48e 100644
--- a/ash/resources/default_200_percent/cros/network/status_network_failed.png
+++ b/ash/resources/default_200_percent/cros/network/status_network_failed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_network_info.png b/ash/resources/default_200_percent/cros/network/status_network_info.png
index 2a42c36..98a9209 100644
--- a/ash/resources/default_200_percent/cros/network/status_network_info.png
+++ b/ash/resources/default_200_percent/cros/network/status_network_info.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_network_info_hover.png b/ash/resources/default_200_percent/cros/network/status_network_info_hover.png
index 3a9c90c..db3e266 100644
--- a/ash/resources/default_200_percent/cros/network/status_network_info_hover.png
+++ b/ash/resources/default_200_percent/cros/network/status_network_info_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_wifi_disabled.png b/ash/resources/default_200_percent/cros/network/status_wifi_disabled.png
index f9e2427..a9d54c7 100644
--- a/ash/resources/default_200_percent/cros/network/status_wifi_disabled.png
+++ b/ash/resources/default_200_percent/cros/network/status_wifi_disabled.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_wifi_disabled_hover.png b/ash/resources/default_200_percent/cros/network/status_wifi_disabled_hover.png
index d1ec838..b485bae 100644
--- a/ash/resources/default_200_percent/cros/network/status_wifi_disabled_hover.png
+++ b/ash/resources/default_200_percent/cros/network/status_wifi_disabled_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_wifi_enabled.png b/ash/resources/default_200_percent/cros/network/status_wifi_enabled.png
index c140f7c..4fae330 100644
--- a/ash/resources/default_200_percent/cros/network/status_wifi_enabled.png
+++ b/ash/resources/default_200_percent/cros/network/status_wifi_enabled.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/status_wifi_enabled_hover.png b/ash/resources/default_200_percent/cros/network/status_wifi_enabled_hover.png
index 1f91e30..bfb35bd 100644
--- a/ash/resources/default_200_percent/cros/network/status_wifi_enabled_hover.png
+++ b/ash/resources/default_200_percent/cros/network/status_wifi_enabled_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_3g_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_3g_light.png
index e493160..095a889 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_3g_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_3g_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_4g_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_4g_light.png
index 3f190f5..e783596 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_4g_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_4g_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_dark.png
index b87ecdc..f3c281f 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_light.png
index 4886542..108249c 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_arcs_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_bars_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_bars_dark.png
index e7d82aa..548ef5f 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_bars_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_bars_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_bars_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_bars_light.png
index f5c2661..90f2232 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_bars_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_bars_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_gprs_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_gprs_dark.png
index 12aa268..c45081d 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_gprs_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_gprs_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_hspa_plus_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_hspa_plus_dark.png
index a4d9b21..a352289 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_hspa_plus_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_hspa_plus_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_dark.png
index ca86c91..50c496a 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_light.png
index 72b5cc3..ef9f7ce 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_advanced_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_dark.png
index d6bc7fb..1c72085 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_light.png
index e1b5bab..53f9ab1 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_lte_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_lte_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_dark.png
index 72a8057..f59974f 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_light.png
index 0196152..ae7fade 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_roaming_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_secure_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_network_secure_dark.png
index 2708a23..1089307 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_secure_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_secure_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_secure_light.png b/ash/resources/default_200_percent/cros/network/statusbar_network_secure_light.png
index d2190ea..022dd66 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_secure_light.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_secure_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_network_vpn_badge.png b/ash/resources/default_200_percent/cros/network/statusbar_network_vpn_badge.png
index 7f60574..07a0756 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_network_vpn_badge.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_network_vpn_badge.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/network/statusbar_vpn_dark.png b/ash/resources/default_200_percent/cros/network/statusbar_vpn_dark.png
index 237e189..5503851 100644
--- a/ash/resources/default_200_percent/cros/network/statusbar_vpn_dark.png
+++ b/ash/resources/default_200_percent/cros/network/statusbar_vpn_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/notification/notification_low_power_charger.png b/ash/resources/default_200_percent/cros/notification/notification_low_power_charger.png
new file mode 100644
index 0000000..78a6b88
--- /dev/null
+++ b/ash/resources/default_200_percent/cros/notification/notification_low_power_charger.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/multiprofiles_add.png b/ash/resources/default_200_percent/cros/status/multiprofiles_add.png
index e62b626..2fb62cd 100644
--- a/ash/resources/default_200_percent/cros/status/multiprofiles_add.png
+++ b/ash/resources/default_200_percent/cros/status/multiprofiles_add.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_audio_device_bluetooth.png b/ash/resources/default_200_percent/cros/status/status_audio_device_bluetooth.png
index 203f2ae..28d36d0 100644
--- a/ash/resources/default_200_percent/cros/status/status_audio_device_bluetooth.png
+++ b/ash/resources/default_200_percent/cros/status/status_audio_device_bluetooth.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_audio_device_hdmi.png b/ash/resources/default_200_percent/cros/status/status_audio_device_hdmi.png
index 69d263f..d6ddc09 100644
--- a/ash/resources/default_200_percent/cros/status/status_audio_device_hdmi.png
+++ b/ash/resources/default_200_percent/cros/status/status_audio_device_hdmi.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_audio_device_headphones.png b/ash/resources/default_200_percent/cros/status/status_audio_device_headphones.png
index 4266d86..4c0e648 100644
--- a/ash/resources/default_200_percent/cros/status/status_audio_device_headphones.png
+++ b/ash/resources/default_200_percent/cros/status/status_audio_device_headphones.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_audio_device_usb.png b/ash/resources/default_200_percent/cros/status/status_audio_device_usb.png
index a418ecd..f17edcf 100644
--- a/ash/resources/default_200_percent/cros/status/status_audio_device_usb.png
+++ b/ash/resources/default_200_percent/cros/status/status_audio_device_usb.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_bluetooth.png b/ash/resources/default_200_percent/cros/status/status_bluetooth.png
index 4c6b397..d4cf7c7 100644
--- a/ash/resources/default_200_percent/cros/status/status_bluetooth.png
+++ b/ash/resources/default_200_percent/cros/status/status_bluetooth.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled.png b/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled.png
index 4ec7293..6676cf9 100644
--- a/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled.png
+++ b/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled_hover.png b/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled_hover.png
index 793ad0c..66f368d 100644
--- a/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled_hover.png
+++ b/ash/resources/default_200_percent/cros/status/status_bluetooth_disabled_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled.png b/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled.png
index e478713..d9ee935 100644
--- a/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled.png
+++ b/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled_hover.png b/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled_hover.png
index 40223f3..a4fa7eb 100644
--- a/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled_hover.png
+++ b/ash/resources/default_200_percent/cros/status/status_bluetooth_enabled_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_brightness.png b/ash/resources/default_200_percent/cros/status/status_brightness.png
index 86880a6..547982e 100644
--- a/ash/resources/default_200_percent/cros/status/status_brightness.png
+++ b/ash/resources/default_200_percent/cros/status/status_brightness.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_capslock.png b/ash/resources/default_200_percent/cros/status/status_capslock.png
index 6e118a6..7ceed83 100644
--- a/ash/resources/default_200_percent/cros/status/status_capslock.png
+++ b/ash/resources/default_200_percent/cros/status/status_capslock.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_capslock_dark.png b/ash/resources/default_200_percent/cros/status/status_capslock_dark.png
index cbb24b6..1d78d5d 100644
--- a/ash/resources/default_200_percent/cros/status/status_capslock_dark.png
+++ b/ash/resources/default_200_percent/cros/status/status_capslock_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_display.png b/ash/resources/default_200_percent/cros/status/status_display.png
index d511b29..b89a120 100644
--- a/ash/resources/default_200_percent/cros/status/status_display.png
+++ b/ash/resources/default_200_percent/cros/status/status_display.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_display_dark.png b/ash/resources/default_200_percent/cros/status/status_display_dark.png
index 6fce73c..9d28ca2 100644
--- a/ash/resources/default_200_percent/cros/status/status_display_dark.png
+++ b/ash/resources/default_200_percent/cros/status/status_display_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_drive.png b/ash/resources/default_200_percent/cros/status/status_drive.png
index 84490ce..43e6877 100644
--- a/ash/resources/default_200_percent/cros/status/status_drive.png
+++ b/ash/resources/default_200_percent/cros/status/status_drive.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_drive_item_cancel_hover.png b/ash/resources/default_200_percent/cros/status/status_drive_item_cancel_hover.png
index dc9f9e0..9b02e58 100644
--- a/ash/resources/default_200_percent/cros/status/status_drive_item_cancel_hover.png
+++ b/ash/resources/default_200_percent/cros/status/status_drive_item_cancel_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_drive_item_failed.png b/ash/resources/default_200_percent/cros/status/status_drive_item_failed.png
index c455e5a..3ed87e1 100644
--- a/ash/resources/default_200_percent/cros/status/status_drive_item_failed.png
+++ b/ash/resources/default_200_percent/cros/status/status_drive_item_failed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_drive_light.png b/ash/resources/default_200_percent/cros/status/status_drive_light.png
index 34f9ba8..bfea7bd 100644
--- a/ash/resources/default_200_percent/cros/status/status_drive_light.png
+++ b/ash/resources/default_200_percent/cros/status/status_drive_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_ime.png b/ash/resources/default_200_percent/cros/status/status_ime.png
index 30df35c..f0104f0 100644
--- a/ash/resources/default_200_percent/cros/status/status_ime.png
+++ b/ash/resources/default_200_percent/cros/status/status_ime.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_less.png b/ash/resources/default_200_percent/cros/status/status_less.png
index 2c756c2..8c5e81f 100644
--- a/ash/resources/default_200_percent/cros/status/status_less.png
+++ b/ash/resources/default_200_percent/cros/status/status_less.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_locale.png b/ash/resources/default_200_percent/cros/status/status_locale.png
index 8339309..dab0994 100644
--- a/ash/resources/default_200_percent/cros/status/status_locale.png
+++ b/ash/resources/default_200_percent/cros/status/status_locale.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_lockscreen.png b/ash/resources/default_200_percent/cros/status/status_lockscreen.png
index 60166ce..9d7fdcd 100644
--- a/ash/resources/default_200_percent/cros/status/status_lockscreen.png
+++ b/ash/resources/default_200_percent/cros/status/status_lockscreen.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_lockscreen_hover.png b/ash/resources/default_200_percent/cros/status/status_lockscreen_hover.png
index df585f2..e4aa68a 100644
--- a/ash/resources/default_200_percent/cros/status/status_lockscreen_hover.png
+++ b/ash/resources/default_200_percent/cros/status/status_lockscreen_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_logout_button_hover_bottom_right.png b/ash/resources/default_200_percent/cros/status/status_logout_button_hover_bottom_right.png
index c42cfb9..216bffd 100644
--- a/ash/resources/default_200_percent/cros/status/status_logout_button_hover_bottom_right.png
+++ b/ash/resources/default_200_percent/cros/status/status_logout_button_hover_bottom_right.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_managed.png b/ash/resources/default_200_percent/cros/status/status_managed.png
index c9937a3..19161c1 100644
--- a/ash/resources/default_200_percent/cros/status/status_managed.png
+++ b/ash/resources/default_200_percent/cros/status/status_managed.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_managed_mode_user.png b/ash/resources/default_200_percent/cros/status/status_managed_mode_user.png
index 023a55e..b0b522d 100644
--- a/ash/resources/default_200_percent/cros/status/status_managed_mode_user.png
+++ b/ash/resources/default_200_percent/cros/status/status_managed_mode_user.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_managed_tray.png b/ash/resources/default_200_percent/cros/status/status_managed_tray.png
index 8f1c9f5..5019650 100644
--- a/ash/resources/default_200_percent/cros/status/status_managed_tray.png
+++ b/ash/resources/default_200_percent/cros/status/status_managed_tray.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_more.png b/ash/resources/default_200_percent/cros/status/status_more.png
index dbf5ebc..a33620b 100644
--- a/ash/resources/default_200_percent/cros/status/status_more.png
+++ b/ash/resources/default_200_percent/cros/status/status_more.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_power_small_all.png b/ash/resources/default_200_percent/cros/status/status_power_small_all.png
index ce25997..42b706d 100644
--- a/ash/resources/default_200_percent/cros/status/status_power_small_all.png
+++ b/ash/resources/default_200_percent/cros/status/status_power_small_all.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_power_small_all_dark.png b/ash/resources/default_200_percent/cros/status/status_power_small_all_dark.png
index 2c06574..d0ea331 100644
--- a/ash/resources/default_200_percent/cros/status/status_power_small_all_dark.png
+++ b/ash/resources/default_200_percent/cros/status/status_power_small_all_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_discharging.png b/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_discharging.png
index d31c677..3c5557e 100644
--- a/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_discharging.png
+++ b/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_discharging.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_fluctuating.png b/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_fluctuating.png
index e7f5a4e..8be069f 100644
--- a/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_fluctuating.png
+++ b/ash/resources/default_200_percent/cros/status/status_power_small_all_dark_fluctuating.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_power_small_all_discharging.png b/ash/resources/default_200_percent/cros/status/status_power_small_all_discharging.png
index b1f8725..8c8490f 100644
--- a/ash/resources/default_200_percent/cros/status/status_power_small_all_discharging.png
+++ b/ash/resources/default_200_percent/cros/status/status_power_small_all_discharging.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_power_small_all_fluctuating.png b/ash/resources/default_200_percent/cros/status/status_power_small_all_fluctuating.png
index 555c229..d46f7ca 100644
--- a/ash/resources/default_200_percent/cros/status/status_power_small_all_fluctuating.png
+++ b/ash/resources/default_200_percent/cros/status/status_power_small_all_fluctuating.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_screen_share_dark.png b/ash/resources/default_200_percent/cros/status/status_screen_share_dark.png
index 84490ce..392b99d 100644
--- a/ash/resources/default_200_percent/cros/status/status_screen_share_dark.png
+++ b/ash/resources/default_200_percent/cros/status/status_screen_share_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_screen_share_light.png b/ash/resources/default_200_percent/cros/status/status_screen_share_light.png
index 34f9ba8..4845e0a 100644
--- a/ash/resources/default_200_percent/cros/status/status_screen_share_light.png
+++ b/ash/resources/default_200_percent/cros/status/status_screen_share_light.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_session_length_limit_timer.png b/ash/resources/default_200_percent/cros/status/status_session_length_limit_timer.png
index 85becc0..0dbae98 100644
--- a/ash/resources/default_200_percent/cros/status/status_session_length_limit_timer.png
+++ b/ash/resources/default_200_percent/cros/status/status_session_length_limit_timer.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_settings.png b/ash/resources/default_200_percent/cros/status/status_settings.png
index 0ecd530..a5655d6 100644
--- a/ash/resources/default_200_percent/cros/status/status_settings.png
+++ b/ash/resources/default_200_percent/cros/status/status_settings.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_shutdown.png b/ash/resources/default_200_percent/cros/status/status_shutdown.png
index dc3a8f5..15ce5e1 100644
--- a/ash/resources/default_200_percent/cros/status/status_shutdown.png
+++ b/ash/resources/default_200_percent/cros/status/status_shutdown.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_shutdown_hover.png b/ash/resources/default_200_percent/cros/status/status_shutdown_hover.png
index c428613..f19ca3c 100644
--- a/ash/resources/default_200_percent/cros/status/status_shutdown_hover.png
+++ b/ash/resources/default_200_percent/cros/status/status_shutdown_hover.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_sms.png b/ash/resources/default_200_percent/cros/status/status_sms.png
index f737f84..101156b 100644
--- a/ash/resources/default_200_percent/cros/status/status_sms.png
+++ b/ash/resources/default_200_percent/cros/status/status_sms.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_update_dark.png b/ash/resources/default_200_percent/cros/status/status_update_dark.png
index 48bbd44..4cba931 100644
--- a/ash/resources/default_200_percent/cros/status/status_update_dark.png
+++ b/ash/resources/default_200_percent/cros/status/status_update_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_update_dark_green.png b/ash/resources/default_200_percent/cros/status/status_update_dark_green.png
index 3a38219..c9ca473 100644
--- a/ash/resources/default_200_percent/cros/status/status_update_dark_green.png
+++ b/ash/resources/default_200_percent/cros/status/status_update_dark_green.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_update_dark_orange.png b/ash/resources/default_200_percent/cros/status/status_update_dark_orange.png
index 068fc4d..467b3f1 100644
--- a/ash/resources/default_200_percent/cros/status/status_update_dark_orange.png
+++ b/ash/resources/default_200_percent/cros/status/status_update_dark_orange.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_update_dark_red.png b/ash/resources/default_200_percent/cros/status/status_update_dark_red.png
index b52893f..e861ab6 100644
--- a/ash/resources/default_200_percent/cros/status/status_update_dark_red.png
+++ b/ash/resources/default_200_percent/cros/status/status_update_dark_red.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_update_orange.png b/ash/resources/default_200_percent/cros/status/status_update_orange.png
index 2283a4b..02e0469 100644
--- a/ash/resources/default_200_percent/cros/status/status_update_orange.png
+++ b/ash/resources/default_200_percent/cros/status/status_update_orange.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_update_red.png b/ash/resources/default_200_percent/cros/status/status_update_red.png
index 4ed4318..ef49b2d 100644
--- a/ash/resources/default_200_percent/cros/status/status_update_red.png
+++ b/ash/resources/default_200_percent/cros/status/status_update_red.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_volume_dark.png b/ash/resources/default_200_percent/cros/status/status_volume_dark.png
index d518f65..b8593a6 100644
--- a/ash/resources/default_200_percent/cros/status/status_volume_dark.png
+++ b/ash/resources/default_200_percent/cros/status/status_volume_dark.png
Binary files differ
diff --git a/ash/resources/default_200_percent/cros/status/status_volume_mute.png b/ash/resources/default_200_percent/cros/status/status_volume_mute.png
index ea8e9a3..7a6abc8 100644
--- a/ash/resources/default_200_percent/cros/status/status_volume_mute.png
+++ b/ash/resources/default_200_percent/cros/status/status_volume_mute.png
Binary files differ
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index af6e946..ba20f58 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -9,7 +9,7 @@
 #include "ash/ash_constants.h"
 #include "ash/ash_switches.h"
 #include "ash/desktop_background/desktop_background_widget_controller.h"
-#include "ash/display/display_controller.h"
+#include "ash/desktop_background/user_wallpaper_delegate.h"
 #include "ash/display/display_manager.h"
 #include "ash/focus_cycler.h"
 #include "ash/session_state_delegate.h"
@@ -22,11 +22,15 @@
 #include "ash/shell_window_ids.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray_delegate.h"
+#include "ash/touch/touch_hud_debug.h"
+#include "ash/touch/touch_hud_projection.h"
 #include "ash/touch/touch_observer_hud.h"
+#include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/base_layout_manager.h"
 #include "ash/wm/boot_splash_screen.h"
 #include "ash/wm/dock/docked_window_layout_manager.h"
 #include "ash/wm/panels/panel_layout_manager.h"
+#include "ash/wm/panels/panel_window_event_handler.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/root_window_layout_manager.h"
 #include "ash/wm/screen_dimmer.h"
@@ -39,14 +43,13 @@
 #include "ash/wm/window_util.h"
 #include "ash/wm/workspace_controller.h"
 #include "base/command_line.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/tooltip_client.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/models/menu_model.h"
-#include "ui/gfx/display.h"
 #include "ui/gfx/screen.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/keyboard/keyboard_util.h"
@@ -170,11 +173,12 @@
       root_window_layout_(NULL),
       docked_layout_manager_(NULL),
       panel_layout_manager_(NULL),
-      touch_observer_hud_(NULL) {
+      touch_hud_debug_(NULL),
+      touch_hud_projection_(NULL) {
   SetRootWindowController(root_window, this);
   screen_dimmer_.reset(new ScreenDimmer(root_window));
 
-  stacking_controller_.reset(new ash::StackingController);
+  stacking_controller_.reset(new StackingController);
   aura::client::SetStackingClient(root_window, stacking_controller_.get());
 }
 
@@ -199,7 +203,36 @@
   return GetRootWindowController(Shell::GetActiveRootWindow());
 }
 
+void RootWindowController::EnableTouchHudProjection() {
+  if (touch_hud_projection_)
+    return;
+  set_touch_hud_projection(new TouchHudProjection(root_window_.get()));
+}
+
+void RootWindowController::DisableTouchHudProjection() {
+  if (!touch_hud_projection_)
+    return;
+  touch_hud_projection_->Remove();
+}
+
+void RootWindowController::SetWallpaperController(
+    DesktopBackgroundWidgetController* controller) {
+  wallpaper_controller_.reset(controller);
+}
+
+void RootWindowController::SetAnimatingWallpaperController(
+    AnimatingDesktopController* controller) {
+  if (animating_wallpaper_controller_.get())
+    animating_wallpaper_controller_->StopAnimating();
+  animating_wallpaper_controller_.reset(controller);
+}
+
 void RootWindowController::Shutdown() {
+  if (animating_wallpaper_controller_.get())
+    animating_wallpaper_controller_->StopAnimating();
+  wallpaper_controller_.reset();
+  animating_wallpaper_controller_.reset();
+
   CloseChildWindows();
   if (Shell::GetActiveRootWindow() == root_window_) {
     Shell::GetInstance()->set_active_root_window(
@@ -246,92 +279,26 @@
   return root_window_->GetChildById(container_id);
 }
 
-void RootWindowController::InitLayoutManagers() {
-  root_window_layout_ =
-      new RootWindowLayoutManager(root_window_.get());
-  root_window_->SetLayoutManager(root_window_layout_);
-
-  aura::Window* default_container =
-      GetContainer(kShellWindowId_DefaultContainer);
-  // Workspace manager has its own layout managers.
-  workspace_controller_.reset(
-      new WorkspaceController(default_container));
-
-  aura::Window* always_on_top_container =
-      GetContainer(kShellWindowId_AlwaysOnTopContainer);
-  always_on_top_container->SetLayoutManager(
-      new BaseLayoutManager(
-          always_on_top_container->GetRootWindow()));
-}
-
-void RootWindowController::InitForPrimaryDisplay() {
-  DCHECK(!shelf_.get());
-  aura::Window* shelf_container =
-      GetContainer(ash::internal::kShellWindowId_ShelfContainer);
-  // TODO(harrym): Remove when status area is view.
-  aura::Window* status_container =
-      GetContainer(ash::internal::kShellWindowId_StatusContainer);
-  shelf_.reset(new ash::ShelfWidget(
-      shelf_container, status_container, workspace_controller()));
-
-  // Create Docked windows layout manager
-  aura::Window* docked_container = GetContainer(
-      internal::kShellWindowId_DockedContainer);
-  docked_layout_manager_ =
-      new internal::DockedWindowLayoutManager(docked_container);
-  docked_container_handler_.reset(
-      new ToplevelWindowEventHandler(docked_container));
-  docked_container->SetLayoutManager(docked_layout_manager_);
-
-  // Create Panel layout manager
-  aura::Window* panel_container = GetContainer(
-      internal::kShellWindowId_PanelContainer);
-  panel_layout_manager_ =
-      new internal::PanelLayoutManager(panel_container);
-  panel_container_handler_.reset(
-      new ToplevelWindowEventHandler(panel_container));
-  panel_container->SetLayoutManager(panel_layout_manager_);
-
-  // TODO(stevenjb/oshima): Remove this call to CreateLauncher() and call
-  // ash::Shell::CreateLauncher() explicitly in ash_shell and ash_unittests
-  // so that the behavior and construction order is consistent betwheen Ash
-  // and Chrome.
-  if (Shell::GetInstance()->session_state_delegate()->NumberOfLoggedInUsers())
-    shelf_->CreateLauncher();
-
-  InitKeyboard();
-}
-
-void RootWindowController::CreateContainers() {
+void RootWindowController::Init(bool first_run_after_boot) {
+  root_window_->SetCursor(ui::kCursorPointer);
   CreateContainersInRootWindow(root_window_.get());
+  CreateSystemBackground(first_run_after_boot);
 
-  // Create touch observer HUD if needed. HUD should be created after the
-  // containers have been created, so that its widget can be added to them.
-  CommandLine* command_line = CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kAshTouchHud))
-    touch_observer_hud_ = new TouchObserverHUD(root_window_.get());
+  InitLayoutManagers();
+  InitKeyboard();
+  InitTouchHuds();
+
+  if (Shell::GetPrimaryRootWindowController()->
+      GetSystemModalLayoutManager(NULL)->has_modal_background()) {
+    GetSystemModalLayoutManager(NULL)->CreateModalBackground();
+  }
 }
 
-void RootWindowController::CreateSystemBackground(
-    bool is_first_run_after_boot) {
-  SkColor color = SK_ColorBLACK;
-#if defined(OS_CHROMEOS)
-  if (is_first_run_after_boot)
-    color = kChromeOsBootColor;
-#endif
-  system_background_.reset(
-      new SystemBackgroundController(root_window_.get(), color));
-
-#if defined(OS_CHROMEOS)
-  // Make a copy of the system's boot splash screen so we can composite it
-  // onscreen until the desktop background is ready.
-  if (is_first_run_after_boot &&
-      (CommandLine::ForCurrentProcess()->HasSwitch(
-           switches::kAshCopyHostBackgroundAtBoot) ||
-       CommandLine::ForCurrentProcess()->HasSwitch(
-           switches::kAshAnimateFromBootSplashScreen)))
-    boot_splash_screen_.reset(new BootSplashScreen(root_window_.get()));
-#endif
+void RootWindowController::ShowLauncher() {
+  if (!shelf_->launcher())
+    return;
+  shelf_->launcher()->SetVisible(true);
+  shelf_->status_area_widget()->Show();
 }
 
 void RootWindowController::OnLauncherCreated() {
@@ -341,23 +308,13 @@
     docked_layout_manager_->SetLauncher(shelf_->launcher());
 }
 
-void RootWindowController::ShowLauncher() {
-  if (!shelf_.get() || !shelf_->launcher())
-    return;
-  shelf_->launcher()->SetVisible(true);
-  shelf_->status_area_widget()->Show();
-}
-
 void RootWindowController::OnLoginStateChanged(user::LoginStatus status) {
-  // TODO(oshima): remove if when launcher per display is enabled by
-  // default.
-  if (shelf_)
-    shelf_->shelf_layout_manager()->UpdateVisibilityState();
+  shelf_->shelf_layout_manager()->UpdateVisibilityState();
 }
 
 void RootWindowController::UpdateAfterLoginStatusChange(
     user::LoginStatus status) {
-  if (shelf_.get() && shelf_->status_area_widget())
+  if (shelf_->status_area_widget())
     shelf_->status_area_widget()->UpdateAfterLoginStatusChange(status);
 }
 
@@ -372,12 +329,31 @@
   }
 }
 
-void RootWindowController::HandleDesktopBackgroundVisible() {
+void RootWindowController::OnWallpaperAnimationFinished(views::Widget* widget) {
+  // Make sure the wallpaper is visible.
   system_background_->SetColor(SK_ColorBLACK);
   boot_splash_screen_.reset();
+
+  Shell::GetInstance()->user_wallpaper_delegate()->
+      OnWallpaperAnimationFinished();
+  // Only removes old component when wallpaper animation finished. If we
+  // remove the old one before the new wallpaper is done fading in there will
+  // be a white flash during the animation.
+  if (animating_wallpaper_controller()) {
+    DesktopBackgroundWidgetController* controller =
+        animating_wallpaper_controller()->GetController(true);
+    // |desktop_widget_| should be the same animating widget we try to move
+    // to |kDesktopController|. Otherwise, we may close |desktop_widget_|
+    // before move it to |kDesktopController|.
+    DCHECK_EQ(controller->widget(), widget);
+    // Release the old controller and close its background widget.
+    SetWallpaperController(controller);
+  }
 }
 
 void RootWindowController::CloseChildWindows() {
+  if (!shelf_.get())
+    return;
   // panel_layout_manager_ needs to be shut down before windows are destroyed.
   if (panel_layout_manager_) {
     panel_layout_manager_->Shutdown();
@@ -385,17 +361,14 @@
   }
 
   // TODO(harrym): Remove when Status Area Widget is a child view.
-  if (shelf_)
-    shelf_->ShutdownStatusAreaWidget();
+  shelf_->ShutdownStatusAreaWidget();
 
-  if (shelf_.get() && shelf_->shelf_layout_manager())
+  if (shelf_->shelf_layout_manager())
     shelf_->shelf_layout_manager()->set_workspace_controller(NULL);
 
   // Close background widget first as it depends on tooltip.
-  root_window_->SetProperty(kDesktopController,
-      static_cast<DesktopBackgroundWidgetController*>(NULL));
-  root_window_->SetProperty(kAnimatingDesktopController,
-                            static_cast<AnimatingDesktopController*>(NULL));
+  wallpaper_controller_.reset();
+  animating_wallpaper_controller_.reset();
 
   workspace_controller_.reset();
   aura::client::SetTooltipClient(root_window_.get(), NULL);
@@ -416,13 +389,13 @@
 }
 
 ShelfLayoutManager* RootWindowController::GetShelfLayoutManager() {
-  return shelf_.get() ? shelf_->shelf_layout_manager() : NULL;
+  return shelf_->shelf_layout_manager();
 }
 
 SystemTray* RootWindowController::GetSystemTray() {
   // We assume in throughout the code that this will not return NULL. If code
   // triggers this for valid reasons, it should test status_area_widget first.
-  CHECK(shelf_.get() && shelf_->status_area_widget());
+  CHECK(shelf_->status_area_widget());
   return shelf_->status_area_widget()->system_tray();
 }
 
@@ -434,15 +407,13 @@
   if (!menu_model)
     return;
 
-  internal::DesktopBackgroundWidgetController* background =
-      root_window_->GetProperty(kDesktopController);
   // Background controller may not be set yet if user clicked on status are
   // before initial animation completion. See crbug.com/222218
-  if (!background)
+  if (!wallpaper_controller_.get())
     return;
 
   views::MenuRunner menu_runner(menu_model.get());
-  if (menu_runner.RunMenuAt(background->widget(),
+  if (menu_runner.RunMenuAt(wallpaper_controller_->widget(),
           NULL, gfx::Rect(location_in_screen, gfx::Size()),
           views::MenuItemView::TOPLEFT, source_type,
           views::MenuRunner::CONTEXT_MENU) ==
@@ -461,7 +432,7 @@
   aura::Window* container = workspace_controller_->GetActiveWorkspaceWindow();
   for (size_t i = 0; i < container->children().size(); ++i) {
     aura::Window* child = container->children()[i];
-    if (ash::wm::IsWindowFullscreen(child))
+    if (wm::IsWindowFullscreen(child))
       return child;
   }
   return NULL;
@@ -490,6 +461,83 @@
 ////////////////////////////////////////////////////////////////////////////////
 // RootWindowController, private:
 
+void RootWindowController::InitLayoutManagers() {
+  root_window_layout_ =
+      new RootWindowLayoutManager(root_window_.get());
+  root_window_->SetLayoutManager(root_window_layout_);
+
+  aura::Window* default_container =
+      GetContainer(kShellWindowId_DefaultContainer);
+  // Workspace manager has its own layout managers.
+  workspace_controller_.reset(
+      new WorkspaceController(default_container));
+
+  aura::Window* always_on_top_container =
+      GetContainer(kShellWindowId_AlwaysOnTopContainer);
+  always_on_top_container->SetLayoutManager(
+      new BaseLayoutManager(
+          always_on_top_container->GetRootWindow()));
+  always_on_top_controller_.reset(new internal::AlwaysOnTopController);
+  always_on_top_controller_->SetAlwaysOnTopContainer(always_on_top_container);
+
+  DCHECK(!shelf_.get());
+  aura::Window* shelf_container =
+      GetContainer(internal::kShellWindowId_ShelfContainer);
+  // TODO(harrym): Remove when status area is view.
+  aura::Window* status_container =
+      GetContainer(internal::kShellWindowId_StatusContainer);
+  shelf_.reset(new ShelfWidget(
+      shelf_container, status_container, workspace_controller()));
+
+  // Create Docked windows layout manager
+  aura::Window* docked_container = GetContainer(
+      internal::kShellWindowId_DockedContainer);
+  docked_layout_manager_ =
+      new internal::DockedWindowLayoutManager(docked_container);
+  docked_container_handler_.reset(
+      new ToplevelWindowEventHandler(docked_container));
+  docked_container->SetLayoutManager(docked_layout_manager_);
+
+  // Create Panel layout manager
+  aura::Window* panel_container = GetContainer(
+      internal::kShellWindowId_PanelContainer);
+  panel_layout_manager_ =
+      new internal::PanelLayoutManager(panel_container);
+  panel_container_handler_.reset(
+      new PanelWindowEventHandler(panel_container));
+  panel_container->SetLayoutManager(panel_layout_manager_);
+}
+
+void RootWindowController::InitTouchHuds() {
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kAshTouchHud))
+    set_touch_hud_debug(new TouchHudDebug(root_window_.get()));
+  if (Shell::GetInstance()->is_touch_hud_projection_enabled())
+    EnableTouchHudProjection();
+}
+
+void RootWindowController::CreateSystemBackground(
+    bool is_first_run_after_boot) {
+  SkColor color = SK_ColorBLACK;
+#if defined(OS_CHROMEOS)
+  if (is_first_run_after_boot)
+    color = kChromeOsBootColor;
+#endif
+  system_background_.reset(
+      new SystemBackgroundController(root_window_.get(), color));
+
+#if defined(OS_CHROMEOS)
+  // Make a copy of the system's boot splash screen so we can composite it
+  // onscreen until the desktop background is ready.
+  if (is_first_run_after_boot &&
+      (CommandLine::ForCurrentProcess()->HasSwitch(
+           switches::kAshCopyHostBackgroundAtBoot) ||
+       CommandLine::ForCurrentProcess()->HasSwitch(
+           switches::kAshAnimateFromBootSplashScreen)))
+    boot_splash_screen_.reset(new BootSplashScreen(root_window_.get()));
+#endif
+}
+
 void RootWindowController::CreateContainersInRootWindow(
     aura::RootWindow* root_window) {
   // These containers are just used by PowerButtonController to animate groups
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h
index 2c02b20..393dcb3 100644
--- a/ash/root_window_controller.h
+++ b/ash/root_window_controller.h
@@ -5,6 +5,8 @@
 #ifndef ASH_ROOT_WINDOW_CONTROLLER_H_
 #define ASH_ROOT_WINDOW_CONTROLLER_H_
 
+#include <map>
+
 #include "ash/ash_export.h"
 #include "ash/shelf/shelf_types.h"
 #include "ash/system/user/login_status.h"
@@ -25,6 +27,8 @@
 }
 
 namespace views {
+class Widget;
+
 namespace corewm {
 class InputMethodEventFilter;
 class RootWindowEventFilter;
@@ -43,7 +47,10 @@
 
 namespace internal {
 
+class AlwaysOnTopController;
+class AnimatingDesktopController;
 class BootSplashScreen;
+class DesktopBackgroundWidgetController;
 class DockedWindowLayoutManager;
 class PanelLayoutManager;
 class RootWindowLayoutManager;
@@ -52,7 +59,8 @@
 class StatusAreaWidget;
 class SystemBackgroundController;
 class SystemModalContainerLayoutManager;
-class TouchObserverHUD;
+class TouchHudDebug;
+class TouchHudProjection;
 class WorkspaceController;
 
 // This class maintains the per root window state for ash. This class
@@ -85,19 +93,50 @@
     return workspace_controller_.get();
   }
 
+  AlwaysOnTopController* always_on_top_controller() {
+    return always_on_top_controller_.get();
+  }
+
   ScreenDimmer* screen_dimmer() { return screen_dimmer_.get(); }
 
   // Access the shelf associated with this root window controller,
   // NULL if no such shelf exists.
   ShelfWidget* shelf() { return shelf_.get(); }
 
-  TouchObserverHUD* touch_observer_hud() { return touch_observer_hud_; }
-
-  // Sets the touch HUD. The RootWindowController will not own this HUD; its
-  // lifetime is managed by itself.
-  void set_touch_observer_hud(TouchObserverHUD* hud) {
-    touch_observer_hud_ = hud;
+  // Get touch HUDs associated with this root window controller.
+  TouchHudDebug* touch_hud_debug() const {
+    return touch_hud_debug_;
   }
+  TouchHudProjection* touch_hud_projection() const {
+    return touch_hud_projection_;
+  }
+
+  // Set touch HUDs for this root window controller. The root window controller
+  // will not own the HUDs; their lifetimes are managed by themselves. Whenever
+  // the widget showing a HUD is being destroyed (e.g. because of detaching a
+  // display), the HUD deletes itself.
+  void set_touch_hud_debug(TouchHudDebug* hud) {
+    touch_hud_debug_ = hud;
+  }
+  void set_touch_hud_projection(TouchHudProjection* hud) {
+    touch_hud_projection_ = hud;
+  }
+
+  // Enables projection touch HUD.
+  void EnableTouchHudProjection();
+
+  // Disables projection touch HUD.
+  void DisableTouchHudProjection();
+
+  DesktopBackgroundWidgetController* wallpaper_controller() {
+    return wallpaper_controller_.get();
+  }
+  void SetWallpaperController(DesktopBackgroundWidgetController* controller);
+  AnimatingDesktopController* animating_wallpaper_controller() {
+    return animating_wallpaper_controller_.get();
+  }
+  void SetAnimatingWallpaperController(AnimatingDesktopController* controller);
+
   // Access the shelf layout manager associated with this root
   // window controller, NULL if no such shelf exists.
   ShelfLayoutManager* GetShelfLayoutManager();
@@ -124,16 +163,9 @@
 
   aura::Window* GetContainer(int container_id);
 
-  void InitLayoutManagers();
-  void CreateContainers();
-
-  // Initializs the RootWindowController for primary display. This
-  // creates
-  void InitForPrimaryDisplay();
-
-  // Initializes |system_background_| and possibly also |boot_splash_screen_|.
-  // |is_first_run_after_boot| determines the background's initial color.
-  void CreateSystemBackground(bool is_first_run_after_boot);
+  // Initializes the RootWindowController. |first_run_after_boot| is
+  // set to true only for primary root window after boot.
+  void Init(bool first_run_after_boot);
 
   // Show launcher view if it was created hidden (before session has started).
   void ShowLauncher();
@@ -153,9 +185,11 @@
   // hiding animation (if the screen is non-NULL).
   void HandleInitialDesktopBackgroundAnimationStarted();
 
-  // Called when the login background is fully visible.  Updates |background_|
-  // to be black and drops |boot_splash_screen_|.
-  void HandleDesktopBackgroundVisible();
+  // Called when the wallpaper ainmation is finished. Updates |background_|
+  // to be black and drops |boot_splash_screen_| and moves the wallpaper
+  // controller into the root window controller. |widget| holds the wallpaper
+  // image, or NULL if the background is a solid color.
+  void OnWallpaperAnimationFinished(views::Widget* widget);
 
   // Deletes associated objects and clears the state, but doesn't delete
   // the root window yet. This is used to delete a secondary displays'
@@ -172,12 +206,21 @@
   // Force the shelf to query for it's current visibility state.
   void UpdateShelfVisibility();
 
+  // Initialize touch HUDs if necessary.
+  void InitTouchHuds();
+
   // Returns the window, if any, which is in fullscreen mode in the active
   // workspace. Exposed here so clients of Ash don't need to know the details
   // of workspace management.
   aura::Window* GetFullscreenWindow() const;
 
  private:
+  void InitLayoutManagers();
+
+  // Initializes |system_background_| and possibly also |boot_splash_screen_|.
+  // |is_first_run_after_boot| determines the background's initial color.
+  void CreateSystemBackground(bool is_first_run_after_boot);
+
   // Creates each of the special window containers that holds windows of various
   // types in the shell UI.
   void CreateContainersInRootWindow(aura::RootWindow* root_window);
@@ -206,10 +249,12 @@
 
   scoped_ptr<ScreenDimmer> screen_dimmer_;
   scoped_ptr<WorkspaceController> workspace_controller_;
+  scoped_ptr<AlwaysOnTopController> always_on_top_controller_;
 
-  // Heads-up display for touch events. The RootWindowController does not own
-  // this HUD; its lifetime is managed by itself.
-  TouchObserverHUD* touch_observer_hud_;
+  // Heads-up displays for touch events. These HUDs are not owned by the root
+  // window controller and manage their own lifetimes.
+  TouchHudDebug* touch_hud_debug_;
+  TouchHudProjection* touch_hud_projection_;
 
   // We need to own event handlers for various containers.
   scoped_ptr<ToplevelWindowEventHandler> default_container_handler_;
@@ -219,6 +264,9 @@
   scoped_ptr<ToplevelWindowEventHandler> panel_container_handler_;
   scoped_ptr<ToplevelWindowEventHandler> docked_container_handler_;
 
+  scoped_ptr<DesktopBackgroundWidgetController> wallpaper_controller_;
+  scoped_ptr<AnimatingDesktopController> animating_wallpaper_controller_;
+
   DISALLOW_COPY_AND_ASSIGN(RootWindowController);
 };
 
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 81a3e62..9896f95 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/root_window_controller.h"
 
-#include "ash/display/display_controller.h"
 #include "ash/session_state_delegate.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
diff --git a/ash/rotator/screen_rotation.cc b/ash/rotator/screen_rotation.cc
index 2f0dc85..248394a 100644
--- a/ash/rotator/screen_rotation.cc
+++ b/ash/rotator/screen_rotation.cc
@@ -4,7 +4,7 @@
 
 #include "ash/rotator/screen_rotation.h"
 
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/interpolated_transform.h"
 #include "ui/gfx/rect.h"
diff --git a/ash/screen_ash_unittest.cc b/ash/screen_ash_unittest.cc
index f5faf3b..2243742 100644
--- a/ash/screen_ash_unittest.cc
+++ b/ash/screen_ash_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/screen_ash.h"
 
-#include "ash/display/display_controller.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_widget.h"
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index d592a8c..c53da27 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -581,7 +581,6 @@
        state_.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN &&
        state.visibility_state == SHELF_VISIBLE) ?
       BackgroundAnimator::CHANGE_IMMEDIATE : BackgroundAnimator::CHANGE_ANIMATE;
-  StopAnimating();
 
   State old_state = state_;
   state_ = state;
@@ -593,6 +592,8 @@
   launcher_animation_setter.SetTransitionDuration(
       base::TimeDelta::FromMilliseconds(kWorkspaceSwitchTimeMS));
   launcher_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
+  launcher_animation_setter.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
   GetLayer(shelf_)->SetBounds(
       target_bounds.shelf_bounds_in_root);
   GetLayer(shelf_)->SetOpacity(target_bounds.opacity);
@@ -601,6 +602,8 @@
   status_animation_setter.SetTransitionDuration(
       base::TimeDelta::FromMilliseconds(kWorkspaceSwitchTimeMS));
   status_animation_setter.SetTweenType(ui::Tween::EASE_OUT);
+  status_animation_setter.SetPreemptionStrategy(
+      ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
 
   // Delay updating the background when going from SHELF_AUTO_HIDE_SHOWN to
   // SHELF_AUTO_HIDE_HIDDEN until the shelf animates out. Otherwise during the
@@ -632,7 +635,7 @@
   status_bounds.set_y(status_bounds.y() +
                       target_bounds.shelf_bounds_in_root.y());
   layer->SetBounds(status_bounds);
-  layer->SetOpacity(target_bounds.opacity);
+  layer->SetOpacity(target_bounds.status_opacity);
   Shell::GetInstance()->SetDisplayWorkAreaInsets(
       root_window_, target_bounds.work_area_insets);
   UpdateHitTestBounds();
@@ -738,6 +741,10 @@
       (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
        state.visibility_state == SHELF_VISIBLE ||
        state.visibility_state == SHELF_AUTO_HIDE) ? 1.0f : 0.0f;
+  target_bounds->status_opacity =
+      (state.visibility_state == SHELF_AUTO_HIDE &&
+       state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) ?
+      0.0f : target_bounds->opacity;
 
   if (gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS)
     UpdateTargetBoundsForGesture(target_bounds);
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index e731cf1..d49f42a 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -17,7 +17,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/observer_list.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/aura/client/activation_change_observer.h"
 #include "ui/aura/layout_manager.h"
 #include "ui/gfx/insets.h"
@@ -207,6 +207,7 @@
     ~TargetBounds();
 
     float opacity;
+    float status_opacity;
     gfx::Rect shelf_bounds_in_root;
     gfx::Rect launcher_bounds_in_shelf;
     gfx::Rect status_bounds_in_shelf;
@@ -237,7 +238,7 @@
   // Sets the visibility of the shelf to |state|.
   void SetState(ShelfVisibilityState visibility_state);
 
-  // Stops any animations.
+  // Stops any animations and progresses them to the end.
   void StopAnimating();
 
   // Returns the width (if aligned to the side) or height (if aligned to the
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index a0db945..20b5be0 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -33,6 +33,7 @@
 #include "ui/base/animation/animation_container_element.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/screen.h"
 #include "ui/views/controls/label.h"
@@ -659,6 +660,33 @@
             Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom());
 }
 
+// Test that switching to a different visibility state does not restart the
+// shelf show / hide animation if it is already running. (crbug.com/250918)
+TEST_F(ShelfLayoutManagerTest, SetStateWhileAnimating) {
+  ShelfWidget* shelf = GetShelfWidget();
+  SetState(shelf->shelf_layout_manager(), SHELF_VISIBLE);
+  gfx::Rect initial_shelf_bounds = shelf->GetWindowBoundsInScreen();
+  gfx::Rect initial_status_bounds =
+      shelf->status_area_widget()->GetWindowBoundsInScreen();
+
+  ui::ScopedAnimationDurationScaleMode normal_animation_duration(
+      ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
+  SetState(shelf->shelf_layout_manager(), SHELF_HIDDEN);
+  SetState(shelf->shelf_layout_manager(), SHELF_VISIBLE);
+
+  gfx::Rect current_shelf_bounds = shelf->GetWindowBoundsInScreen();
+  gfx::Rect current_status_bounds =
+      shelf->status_area_widget()->GetWindowBoundsInScreen();
+
+  const int small_change = initial_shelf_bounds.height() / 2;
+  EXPECT_LE(
+      std::abs(initial_shelf_bounds.height() - current_shelf_bounds.height()),
+      small_change);
+  EXPECT_LE(
+      std::abs(initial_status_bounds.height() - current_status_bounds.height()),
+      small_change);
+}
+
 // Makes sure the launcher is sized when the status area changes size.
 TEST_F(ShelfLayoutManagerTest, LauncherUpdatedWhenStatusAreaChangesSize) {
   Launcher* launcher = Launcher::ForPrimaryDisplay();
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 9f95946..223d034 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -322,7 +322,7 @@
     dimmer_.reset(new views::Widget);
     views::Widget::InitParams params(
         views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-    params.transparent = true;
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.can_activate = false;
     params.accept_events = false;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -436,7 +436,7 @@
       window_container_(shelf_container) {
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.parent = shelf_container;
   params.delegate = delegate_view_;
@@ -489,6 +489,8 @@
 
 void ShelfWidget::SetDimsShelf(bool dimming) {
   delegate_view_->SetDimmed(dimming);
+  if (launcher_)
+    launcher_->GetAppListButtonView()->SchedulePaint();
 }
 
 bool ShelfWidget::GetDimsShelf() const {
@@ -516,7 +518,7 @@
 
   launcher_->SetVisible(
       shell->session_state_delegate()->IsActiveUserSessionStarted());
-
+  shelf_layout_manager_->LayoutShelf();
   Show();
 }
 
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index 3379e29..f043f34 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/launcher/launcher_button.h"
 #include "ash/launcher/launcher_model.h"
 #include "ash/launcher/launcher_view.h"
+#include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
@@ -23,6 +24,7 @@
 namespace ash {
 
 namespace {
+
 ShelfWidget* GetShelfWidget() {
   return Launcher::ForPrimaryDisplay()->shelf_widget();
 }
@@ -154,4 +156,40 @@
   widget->SetFullscreen(true);
 }
 
+#if defined(OS_CHROMEOS)
+// Verifies launcher is created with correct size after user login and when its
+// container and status widget has finished sizing.
+// See http://crbug.com/252533
+TEST_F(ShelfWidgetTest, LauncherInitiallySizedAfterLogin) {
+  SetUserLoggedIn(false);
+  UpdateDisplay("300x200,400x300");
+
+  ShelfWidget* shelf = NULL;
+  Shell::RootWindowControllerList controllers(
+      Shell::GetAllRootWindowControllers());
+  for (Shell::RootWindowControllerList::const_iterator i = controllers.begin();
+       i != controllers.end();
+       ++i) {
+    if (!(*i)->shelf()->launcher()) {
+      shelf = (*i)->shelf();
+      break;
+    }
+  }
+  ASSERT_TRUE(shelf != NULL);
+
+  SetUserLoggedIn(true);
+  Shell::GetInstance()->CreateLauncher();
+
+  Launcher* launcher = shelf->launcher();
+  ASSERT_TRUE(launcher != NULL);
+
+  const int status_width =
+      shelf->status_area_widget()->GetWindowBoundsInScreen().width();
+  EXPECT_GT(status_width, 0);
+  EXPECT_EQ(status_width,
+            shelf->GetContentsView()->width() -
+                launcher->GetLauncherViewForTest()->width());
+}
+#endif
+
 }  // namespace ash
diff --git a/ash/shell.cc b/ash/shell.cc
index 84fe09f..1610ae3 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -39,7 +39,6 @@
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/wm/activation_controller.h"
-#include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/app_list_controller.h"
 #include "ash/wm/ash_activation_controller.h"
 #include "ash/wm/ash_focus_rules.h"
@@ -107,7 +106,8 @@
 #include "ash/accelerators/nested_dispatcher_controller.h"
 #endif
 
-#if defined(OS_CHROMEOS) && defined(USE_X11)
+#if defined(OS_CHROMEOS)
+#if defined(USE_X11)
 #include "ash/ash_constants.h"
 #include "ash/display/display_change_observer_x11.h"
 #include "ash/display/display_error_dialog.h"
@@ -118,6 +118,8 @@
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/common/content_switches.h"
 #include "gpu/config/gpu_feature_type.h"
+#endif  // defined(USE_X11)
+#include "ash/system/chromeos/power/power_status.h"
 #endif  // defined(OS_CHROMEOS)
 
 namespace ash {
@@ -208,7 +210,8 @@
       cursor_manager_(scoped_ptr<views::corewm::NativeCursorManager>(
           native_cursor_manager_)),
       browser_context_(NULL),
-      simulate_modal_window_open_for_testing_(false) {
+      simulate_modal_window_open_for_testing_(false),
+      is_touch_hud_projection_enabled_(false) {
   DCHECK(delegate_.get());
   display_manager_.reset(new internal::DisplayManager);
   mirror_window_controller_.reset(new internal::MirrorWindowController);
@@ -233,6 +236,10 @@
       output_configurator());
 #endif  // defined(OS_CHROMEOS)
   AddPreTargetHandler(this);
+
+#if defined(OS_CHROMEOS)
+  internal::PowerStatus::Initialize();
+#endif
 }
 
 Shell::~Shell() {
@@ -325,6 +332,10 @@
   display_change_observer_.reset();
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_CHROMEOS)
+  internal::PowerStatus::Shutdown();
+#endif
+
   DCHECK(instance_ == this);
   instance_ = NULL;
 }
@@ -523,12 +534,6 @@
   if (keyboard::IsKeyboardEnabled())
     keyboard::InitializeKeyboard();
 
-  internal::RootWindowController* root_window_controller =
-      new internal::RootWindowController(root_window);
-  root_window_controller->CreateContainers();
-  root_window_controller->CreateSystemBackground(
-      delegate_->IsFirstRunAfterBoot());
-
   if (command_line->HasSwitch(ash::switches::kAshDisableNewLockAnimations))
     lock_state_controller_.reset(new SessionStateControllerImpl);
   else
@@ -566,8 +571,6 @@
 
   event_client_.reset(new internal::EventClientImpl);
 
-  InitRootWindowController(root_window_controller);
-
   // This controller needs to be set before SetupManagedWindowMode.
   desktop_background_controller_.reset(new DesktopBackgroundController());
   user_wallpaper_delegate_.reset(delegate_->CreateUserWallpaperDelegate());
@@ -593,8 +596,10 @@
   if (!system_tray_delegate_)
     system_tray_delegate_.reset(SystemTrayDelegate::CreateDummyDelegate());
 
-  // Creates StatusAreaWidget.
-  root_window_controller->InitForPrimaryDisplay();
+  internal::RootWindowController* root_window_controller =
+      new internal::RootWindowController(root_window);
+  InitRootWindowController(root_window_controller,
+                           delegate_->IsFirstRunAfterBoot());
 
   // Initialize system_tray_delegate_ after StatusAreaWidget is created.
   system_tray_delegate_->Initialize();
@@ -838,34 +843,47 @@
 
 LauncherDelegate* Shell::GetLauncherDelegate() {
   if (!launcher_delegate_) {
-    if (!launcher_model_)
-      launcher_model_.reset(new LauncherModel);
-    // Attempt to create the Launcher. This may fail if the application is not
-    // ready to create it yet, in which case the app is responsible for calling
-    // ash::Shell::CreateLauncher() when ready.
+    launcher_model_.reset(new LauncherModel);
     launcher_delegate_.reset(
         delegate_->CreateLauncherDelegate(launcher_model_.get()));
   }
   return launcher_delegate_.get();
 }
 
+void Shell::SetTouchHudProjectionEnabled(bool enabled) {
+  if (is_touch_hud_projection_enabled_ == enabled)
+    return;
+
+  RootWindowList roots = GetInstance()->GetAllRootWindows();
+  for (RootWindowList::iterator iter = roots.begin(); iter != roots.end();
+      ++iter) {
+    internal::RootWindowController* controller = GetRootWindowController(*iter);
+    if (enabled)
+      controller->EnableTouchHudProjection();
+    else
+      controller->DisableTouchHudProjection();
+  }
+  is_touch_hud_projection_enabled_ = enabled;
+}
+
 void Shell::InitRootWindowForSecondaryDisplay(aura::RootWindow* root) {
-  aura::client::SetFocusClient(root, focus_client_.get());
   internal::RootWindowController* controller =
       new internal::RootWindowController(root);
-  controller->CreateContainers();
   // Pass false for the |is_first_run_after_boot| parameter so we'll show a
   // black background on this display instead of trying to mimic the boot splash
   // screen.
-  controller->CreateSystemBackground(false);
-  InitRootWindowController(controller);
-  controller->InitForPrimaryDisplay();
+  InitRootWindowController(controller, false);
+
   controller->root_window_layout()->OnWindowResized();
   desktop_background_controller_->OnRootWindowAdded(root);
   high_contrast_controller_->OnRootWindowAdded(root);
   root->ShowRootWindow();
   // Activate new root for testing.
   active_root_window_ = root;
+
+  // Create a launcher if a user is already logged.
+  if (Shell::GetInstance()->session_state_delegate()->NumberOfLoggedInUsers())
+    controller->shelf()->CreateLauncher();
 }
 
 void Shell::DoInitialWorkspaceAnimation() {
@@ -874,7 +892,9 @@
 }
 
 void Shell::InitRootWindowController(
-    internal::RootWindowController* controller) {
+    internal::RootWindowController* controller,
+    bool first_run_after_boot) {
+
   aura::RootWindow* root_window = controller->root_window();
   DCHECK(activation_client_);
   DCHECK(visibility_controller_.get());
@@ -906,21 +926,7 @@
   if (user_action_client_)
     aura::client::SetUserActionClient(root_window, user_action_client_.get());
 
-  root_window->SetCursor(ui::kCursorPointer);
-  controller->InitLayoutManagers();
-
-  // TODO(oshima): Move the instance to RootWindowController when
-  // the extended desktop is enabled by default.
-  internal::AlwaysOnTopController* always_on_top_controller =
-      new internal::AlwaysOnTopController;
-  always_on_top_controller->SetAlwaysOnTopContainer(
-      root_window->GetChildById(internal::kShellWindowId_AlwaysOnTopContainer));
-  root_window->SetProperty(internal::kAlwaysOnTopControllerKey,
-                           always_on_top_controller);
-  if (GetPrimaryRootWindowController()->GetSystemModalLayoutManager(NULL)->
-          has_modal_background()) {
-    controller->GetSystemModalLayoutManager(NULL)->CreateModalBackground();
-  }
+  controller->Init(first_run_after_boot);
 
   window_cycle_controller_->OnRootWindowAdded(root_window);
 }
diff --git a/ash/shell.h b/ash/shell.h
index f8649dc..a602cdf 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -462,6 +462,12 @@
   // Returns the launcher delegate, creating if necesary.
   LauncherDelegate* GetLauncherDelegate();
 
+  void SetTouchHudProjectionEnabled(bool enabled);
+
+  bool is_touch_hud_projection_enabled() const {
+    return is_touch_hud_projection_enabled_;
+  }
+
  private:
   FRIEND_TEST_ALL_PREFIXES(ExtendedDesktopTest, TestCursor);
   FRIEND_TEST_ALL_PREFIXES(WindowManagerTest, MouseEventCursors);
@@ -479,8 +485,10 @@
   void Init();
 
   // Initializes the root window and root window controller so that it
-  // can host browser windows.
-  void InitRootWindowController(internal::RootWindowController* root);
+  // can host browser windows. |first_run_after_boot| is true for the
+  // primary display only first time after boot.
+  void InitRootWindowController(internal::RootWindowController* root,
+                                bool first_run_after_boot);
 
   // ash::internal::SystemModalContainerEventFilterDelegate overrides:
   virtual bool CanWindowReceiveEvents(aura::Window* window) OVERRIDE;
@@ -604,6 +612,8 @@
   // For testing only: simulate that a modal window is open
   bool simulate_modal_window_open_for_testing_;
 
+  bool is_touch_hud_projection_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(Shell);
 };
 
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index 1f6fc49..dd7adc5 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -9,6 +9,8 @@
 #include "ash/shell/example_factory.h"
 #include "ash/shell/toplevel_window.h"
 #include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
 #include "base/i18n/case_conversion.h"
 #include "base/i18n/string_search.h"
 #include "base/strings/string_util.h"
@@ -231,6 +233,11 @@
     return NULL;
   }
 
+  virtual void GetShortcutPathForApp(
+      const std::string& app_id,
+      const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE {
+  }
+
   virtual void ActivateAppListItem(app_list::AppListItemModel* item,
                                    int event_flags) OVERRIDE {
     static_cast<WindowTypeLauncherItem*>(item)->Activate(event_flags);
diff --git a/ash/shell/content_client/shell_browser_main_parts.cc b/ash/shell/content_client/shell_browser_main_parts.cc
index 03a401c..08efd79 100644
--- a/ash/shell/content_client/shell_browser_main_parts.cc
+++ b/ash/shell/content_client/shell_browser_main_parts.cc
@@ -9,6 +9,7 @@
 #include "ash/shell.h"
 #include "ash/shell/shell_delegate_impl.h"
 #include "ash/shell/window_watcher.h"
+#include "ash/system/user/login_status.h"
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/i18n/icu_util.h"
@@ -18,7 +19,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "content/public/common/content_switches.h"
 #include "content/shell/shell_browser_context.h"
-#include "googleurl/src/gurl.h"
+#include "content/shell/shell_net_log.h"
 #include "net/base/net_module.h"
 #include "ui/aura/client/stacking_client.h"
 #include "ui/aura/env.h"
@@ -39,7 +40,6 @@
 #if defined(OS_CHROMEOS)
 #include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
-#include "chromeos/power/power_manager_handler.h"
 #endif
 
 namespace ash {
@@ -102,7 +102,9 @@
 }
 
 void ShellBrowserMainParts::PreMainMessageLoopRun() {
-  browser_context_.reset(new content::ShellBrowserContext(false));
+  net_log_.reset(new content::ShellNetLog());
+  browser_context_.reset(new content::ShellBrowserContext(
+      false, net_log_.get()));
 
   // A ViewsDelegate is required.
   if (!views::ViewsDelegate::views_delegate)
@@ -119,11 +121,13 @@
     // is absent.
     chromeos::CrasAudioHandler::InitializeForTesting();
   }
-  chromeos::PowerManagerHandler::Initialize();
 #endif
 
   ash::Shell::CreateInstance(delegate_);
   ash::Shell::GetInstance()->set_browser_context(browser_context_.get());
+  ash::Shell::GetInstance()->CreateLauncher();
+  ash::Shell::GetInstance()->UpdateAfterLoginStatusChange(
+      user::LOGGED_IN_USER);
 
   window_watcher_.reset(new ash::shell::WindowWatcher);
   gfx::Screen* screen = Shell::GetInstance()->GetScreen();
@@ -132,12 +136,8 @@
 
   ash::shell::InitWindowTypeLauncher();
 
-  DesktopBackgroundController* controller =
-      Shell::GetInstance()->desktop_background_controller();
-  if (controller->GetAppropriateResolution() == WALLPAPER_RESOLUTION_LARGE)
-    controller->SetDefaultWallpaper(kDefaultLargeWallpaper);
-  else
-    controller->SetDefaultWallpaper(kDefaultSmallWallpaper);
+  Shell::GetInstance()->desktop_background_controller()->SetDefaultWallpaper(
+      false /* is_guest */);
 
   ash::Shell::GetPrimaryRootWindow()->ShowRootWindow();
 }
@@ -155,7 +155,6 @@
   message_center::MessageCenter::Shutdown();
 
 #if defined(OS_CHROMEOS)
-  chromeos::PowerManagerHandler::Shutdown();
   if (ash::switches::UseNewAudioHandler())
     chromeos::CrasAudioHandler::Shutdown();
 #endif
diff --git a/ash/shell/content_client/shell_browser_main_parts.h b/ash/shell/content_client/shell_browser_main_parts.h
index 8c51306..768ee5e 100644
--- a/ash/shell/content_client/shell_browser_main_parts.h
+++ b/ash/shell/content_client/shell_browser_main_parts.h
@@ -18,6 +18,10 @@
 struct MainFunctionParams;
 }
 
+namespace net {
+class NetLog;
+}
+
 namespace ash {
 namespace shell {
 
@@ -42,6 +46,7 @@
   }
 
  private:
+  scoped_ptr<net::NetLog> net_log_;
   scoped_ptr<content::ShellBrowserContext> browser_context_;
   scoped_ptr<ash::shell::WindowWatcher> window_watcher_;
   ShellDelegateImpl* delegate_;  // owned by Shell
diff --git a/ash/shell/shell_main_parts_mac.mm b/ash/shell/shell_main_parts_mac.mm
index d5c726e..292e1d5 100644
--- a/ash/shell/shell_main_parts_mac.mm
+++ b/ash/shell/shell_main_parts_mac.mm
@@ -8,7 +8,7 @@
 
 #include "base/i18n/icu_util.h"
 #include "base/mac/bundle_locations.h"
-#include "base/memory/scoped_nsobject.h"
+#include "base/mac/scoped_nsobject.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/ui_base_paths.h"
 
@@ -20,9 +20,9 @@
   icu_util::Initialize();
   ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL);
 
-  scoped_nsobject<NSNib>
-      nib([[NSNib alloc] initWithNibNamed:@"MainMenu"
-                                   bundle:base::mac::FrameworkBundle()]);
+  base::scoped_nsobject<NSNib> nib(
+      [[NSNib alloc] initWithNibNamed:@"MainMenu"
+                               bundle:base::mac::FrameworkBundle()]);
   [nib instantiateNibWithOwner:NSApp topLevelObjects:nil];
 }
 
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index 4bfe453..5a260a5 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -17,7 +17,7 @@
 #include "ash/system/web_notification/web_notification_tray.h"
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/window.h"
diff --git a/ash/shell/window_watcher_unittest.cc b/ash/shell/window_watcher_unittest.cc
index e35ba62..dfc7e6b 100644
--- a/ash/shell/window_watcher_unittest.cc
+++ b/ash/shell/window_watcher_unittest.cc
@@ -7,6 +7,7 @@
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
 #include "ash/shell/shell_delegate_impl.h"
+#include "ash/system/user/login_status.h"
 #include "ash/test/ash_test_base.h"
 #include "ui/aura/root_window.h"
 
@@ -22,8 +23,11 @@
 
   shell::ShellDelegateImpl* delegate = new ash::shell::ShellDelegateImpl;
   Shell::CreateInstance(delegate);
-
   Shell::GetPrimaryRootWindow()->ShowRootWindow();
+  Shell::GetInstance()->CreateLauncher();
+  Shell::GetInstance()->UpdateAfterLoginStatusChange(
+      user::LOGGED_IN_USER);
+
   window_watcher.reset(new ash::shell::WindowWatcher);
 
   delegate->SetWatcher(window_watcher.get());
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 8660c2f..0e7fcb4 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -12,7 +12,7 @@
 #include "ash/shell.h"
 #include "base/callback.h"
 #include "base/strings/string16.h"
-#include "base/time.h"
+#include "base/time/time.h"
 
 namespace app_list {
 class AppListViewDelegate;
diff --git a/ash/system/chromeos/audio/tray_audio.cc b/ash/system/chromeos/audio/tray_audio.cc
index 742a6e5..1360161 100644
--- a/ash/system/chromeos/audio/tray_audio.cc
+++ b/ash/system/chromeos/audio/tray_audio.cc
@@ -189,6 +189,7 @@
         device_type_(NULL),
         more_(NULL),
         is_default_view_(is_default_view) {
+    set_focusable(false);
     SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
           kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
 
@@ -444,7 +445,7 @@
         false);  /* no checkmark */
     for (size_t i = 0; i < output_devices_.size(); ++i) {
       HoverHighlightView* container = AddScrollListItem(
-          output_devices_[i].display_name,
+          UTF8ToUTF16(output_devices_[i].display_name),
           gfx::Font::NORMAL,
           output_devices_[i].active);  /* checkmark if active */
       device_map_[container] = output_devices_[i];
@@ -459,7 +460,7 @@
         false);  /* no checkmark */
     for (size_t i = 0; i < input_devices_.size(); ++i) {
       HoverHighlightView* container = AddScrollListItem(
-          input_devices_[i].display_name,
+          UTF8ToUTF16(input_devices_[i].display_name),
           gfx::Font::NORMAL,
           input_devices_[i].active);  /* checkmark if active */
       device_map_[container] = input_devices_[i];
@@ -586,10 +587,12 @@
   if (tray_view())
       tray_view()->SetVisible(GetInitialVisibility());
 
-  if (volume_view_)
+  if (volume_view_) {
     volume_view_->Update();
-  else
+    SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
+  } else {
     PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
+  }
 }
 
 
@@ -613,9 +616,10 @@
   if (tray_view())
       tray_view()->SetVisible(GetInitialVisibility());
 
-  if (volume_view_)
+  if (volume_view_) {
     volume_view_->Update();
-  else {
+    SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
+  } else {
     pop_up_volume_view_ = true;
     PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
   }
diff --git a/ash/system/chromeos/label_tray_view.cc b/ash/system/chromeos/label_tray_view.cc
index 8398421..5af294e 100644
--- a/ash/system/chromeos/label_tray_view.cc
+++ b/ash/system/chromeos/label_tray_view.cc
@@ -50,13 +50,14 @@
     child->set_border(
         views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal,
                                          0, kTrayPopupPaddingHorizontal));
+    child->text_label()->SetMultiLine(true);
     child->text_label()->SizeToFit(kTrayNotificationContentsWidth);
   } else {
     child->AddLabel(message, gfx::Font::NORMAL);
+    child->text_label()->SetMultiLine(true);
     child->text_label()->SizeToFit(kTrayNotificationContentsWidth +
                                    kNotificationIconWidth);
   }
-  child->text_label()->SetMultiLine(true);
   child->text_label()->SetAllowCharacterBreak(true);
   child->SetExpandable(true);
   child->SetVisible(true);
diff --git a/ash/system/chromeos/managed/tray_locally_managed_user.cc b/ash/system/chromeos/managed/tray_locally_managed_user.cc
index 1803bea..61e422d 100644
--- a/ash/system/chromeos/managed/tray_locally_managed_user.cc
+++ b/ash/system/chromeos/managed/tray_locally_managed_user.cc
@@ -49,7 +49,8 @@
 TrayLocallyManagedUser::TrayLocallyManagedUser(SystemTray* system_tray)
     : SystemTrayItem(system_tray),
       tray_view_(NULL),
-      notification_view_(NULL) {
+      notification_view_(NULL),
+      status_(ash::user::LOGGED_IN_NONE) {
 }
 
 TrayLocallyManagedUser::~TrayLocallyManagedUser() {
@@ -98,8 +99,13 @@
 
 void TrayLocallyManagedUser::UpdateAfterLoginStatusChange(
     user::LoginStatus status) {
-  if (status == ash::user::LOGGED_IN_LOCALLY_MANAGED)
+  if (status == status_)
+    return;
+  if (status == ash::user::LOGGED_IN_LOCALLY_MANAGED &&
+      status_ != ash::user::LOGGED_IN_LOCKED) {
     ShowNotificationView();
+  }
+  status_ = status;
 }
 
 } // namespace internal
diff --git a/ash/system/chromeos/managed/tray_locally_managed_user.h b/ash/system/chromeos/managed/tray_locally_managed_user.h
index fb85c31..eda2be0 100644
--- a/ash/system/chromeos/managed/tray_locally_managed_user.h
+++ b/ash/system/chromeos/managed/tray_locally_managed_user.h
@@ -41,6 +41,8 @@
  private:
   LabelTrayView* tray_view_;
   views::View* notification_view_;
+  // Previous login status to avoid showing notification upon unlock.
+  user::LoginStatus status_;
 
   DISALLOW_COPY_AND_ASSIGN(TrayLocallyManagedUser);
 };
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 5d2e553..8063b1e 100644
--- a/ash/system/chromeos/network/network_state_list_detailed_view.cc
+++ b/ash/system/chromeos/network/network_state_list_detailed_view.cc
@@ -22,7 +22,7 @@
 #include "base/command_line.h"
 #include "base/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/network/device_state.h"
 #include "chromeos/network/network_state.h"
diff --git a/ash/system/chromeos/network/network_state_notifier.h b/ash/system/chromeos/network/network_state_notifier.h
index 89d46c3..cfcc4b7 100644
--- a/ash/system/chromeos/network/network_state_notifier.h
+++ b/ash/system/chromeos/network/network_state_notifier.h
@@ -11,7 +11,7 @@
 #include "ash/system/chromeos/network/network_tray_delegate.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "chromeos/network/network_state_handler_observer.h"
 
 namespace chromeos {
diff --git a/ash/system/chromeos/network/network_state_notifier_unittest.cc b/ash/system/chromeos/network/network_state_notifier_unittest.cc
index 009274c..2f681bc 100644
--- a/ash/system/chromeos/network/network_state_notifier_unittest.cc
+++ b/ash/system/chromeos/network/network_state_notifier_unittest.cc
@@ -68,10 +68,11 @@
         DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface();
     service_test->ClearServices();
     const bool add_to_watchlist = true;
+    const bool add_to_visible = true;
     // Create wifi and cellular networks and set to online.
     service_test->AddService("wifi1", "wifi1",
                              flimflam::kTypeWifi, flimflam::kStateOnline,
-                             add_to_watchlist);
+                             add_to_visible, add_to_watchlist);
     RunAllPendingInMessageLoop();
   }
 
diff --git a/ash/system/chromeos/network/tray_network.h b/ash/system/chromeos/network/tray_network.h
index 3ba88c4..332db1d 100644
--- a/ash/system/chromeos/network/tray_network.h
+++ b/ash/system/chromeos/network/tray_network.h
@@ -12,7 +12,7 @@
 #include "ash/system/chromeos/network/tray_network_state_observer.h"
 #include "ash/system/tray/system_tray_item.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
+#include "base/time/time.h"
 
 namespace chromeos {
 class NetworkState;
diff --git a/ash/system/chromeos/power/power_status.cc b/ash/system/chromeos/power/power_status.cc
new file mode 100644
index 0000000..7982aa0
--- /dev/null
+++ b/ash/system/chromeos/power/power_status.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 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/chromeos/power/power_status.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/rect.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+// Updates |proto| to ensure that its fields are consistent.
+void SanitizeProto(power_manager::PowerSupplyProperties* proto) {
+  DCHECK(proto);
+
+  if (proto->battery_state() ==
+      power_manager::PowerSupplyProperties_BatteryState_FULL)
+    proto->set_battery_percent(100.0);
+
+  if (!proto->is_calculating_battery_time()) {
+    const bool on_line_power = proto->external_power() !=
+        power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
+    if ((on_line_power && proto->battery_time_to_full_sec() < 0) ||
+        (!on_line_power && proto->battery_time_to_empty_sec() < 0))
+      proto->set_is_calculating_battery_time(true);
+  }
+}
+
+base::string16 GetBatteryTimeAccessibilityString(int hour, int min) {
+  DCHECK(hour || min);
+  if (hour && !min) {
+    return Shell::GetInstance()->delegate()->GetTimeDurationLongString(
+        base::TimeDelta::FromHours(hour));
+  }
+  if (min && !hour) {
+    return Shell::GetInstance()->delegate()->GetTimeDurationLongString(
+        base::TimeDelta::FromMinutes(min));
+  }
+  return l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE,
+      Shell::GetInstance()->delegate()->GetTimeDurationLongString(
+          base::TimeDelta::FromHours(hour)),
+      Shell::GetInstance()->delegate()->GetTimeDurationLongString(
+          base::TimeDelta::FromMinutes(min)));
+}
+
+static PowerStatus* g_power_status = NULL;
+
+// Minimum battery percentage rendered in UI.
+const int kMinBatteryPercent = 1;
+
+// Width and height of battery images.
+const int kBatteryImageHeight = 25;
+const int kBatteryImageWidth = 25;
+
+// Number of different power states.
+const int kNumPowerImages = 15;
+
+}  // namespace
+
+// static
+void PowerStatus::Initialize() {
+  CHECK(!g_power_status);
+  g_power_status = new PowerStatus();
+}
+
+// static
+void PowerStatus::Shutdown() {
+  CHECK(g_power_status);
+  delete g_power_status;
+  g_power_status = NULL;
+}
+
+// static
+bool PowerStatus::IsInitialized() {
+  return g_power_status != NULL;
+}
+
+// static
+PowerStatus* PowerStatus::Get() {
+  CHECK(g_power_status) << "PowerStatus::Get() called before Initialize().";
+  return g_power_status;
+}
+
+void PowerStatus::AddObserver(Observer* observer) {
+  DCHECK(observer);
+  observers_.AddObserver(observer);
+}
+
+void PowerStatus::RemoveObserver(Observer* observer) {
+  DCHECK(observer);
+  observers_.RemoveObserver(observer);
+}
+
+void PowerStatus::RequestStatusUpdate() {
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+      RequestStatusUpdate();
+}
+
+bool PowerStatus::IsBatteryPresent() const {
+  return proto_.battery_state() !=
+      power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT;
+}
+
+bool PowerStatus::IsBatteryFull() const {
+  return proto_.battery_state() ==
+      power_manager::PowerSupplyProperties_BatteryState_FULL;
+}
+
+double PowerStatus::GetBatteryPercent() const {
+  return proto_.battery_percent();
+}
+
+int PowerStatus::GetRoundedBatteryPercent() const {
+  return std::max(kMinBatteryPercent,
+      static_cast<int>(GetBatteryPercent() + 0.5));
+}
+
+bool PowerStatus::IsBatteryTimeBeingCalculated() const {
+  return proto_.is_calculating_battery_time();
+}
+
+base::TimeDelta PowerStatus::GetBatteryTimeToEmpty() const {
+  return base::TimeDelta::FromSeconds(proto_.battery_time_to_empty_sec());
+}
+
+base::TimeDelta PowerStatus::GetBatteryTimeToFull() const {
+  return base::TimeDelta::FromSeconds(proto_.battery_time_to_full_sec());
+}
+
+bool PowerStatus::IsLinePowerConnected() const {
+  return proto_.external_power() !=
+      power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
+}
+
+bool PowerStatus::IsMainsChargerConnected() const {
+  return proto_.external_power() ==
+      power_manager::PowerSupplyProperties_ExternalPower_AC;
+}
+
+bool PowerStatus::IsUsbChargerConnected() const {
+  return proto_.external_power() ==
+      power_manager::PowerSupplyProperties_ExternalPower_USB;
+}
+
+gfx::ImageSkia PowerStatus::GetBatteryImage(IconSet icon_set) const {
+  gfx::Image all;
+  if (IsUsbChargerConnected()) {
+    all = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+        icon_set == ICON_DARK ?
+        IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE_DARK :
+        IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE);
+  } else {
+    all = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+        icon_set == ICON_DARK ?
+        IDR_AURA_UBER_TRAY_POWER_SMALL_DARK : IDR_AURA_UBER_TRAY_POWER_SMALL);
+  }
+
+  // Get the horizontal offset in the battery icon array image.
+  int offset = (IsUsbChargerConnected() || !IsLinePowerConnected()) ? 0 : 1;
+
+  // Get the icon index in the battery icon array image.
+  int index = -1;
+  if (GetBatteryPercent() >= 100.0) {
+    index = kNumPowerImages - 1;
+  } else if (!IsBatteryPresent()) {
+    index = kNumPowerImages;
+  } else {
+    index = static_cast<int>(
+        GetBatteryPercent() / 100.0 * (kNumPowerImages - 1));
+    index = std::max(std::min(index, kNumPowerImages - 2), 0);
+  }
+
+  gfx::Rect region(
+      offset * kBatteryImageWidth, index * kBatteryImageHeight,
+      kBatteryImageWidth, kBatteryImageHeight);
+  return gfx::ImageSkiaOperations::ExtractSubset(*all.ToImageSkia(), region);
+}
+
+base::string16 PowerStatus::GetAccessibleNameString() const {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  if (IsLinePowerConnected() && IsBatteryFull()) {
+    return rb.GetLocalizedString(
+        IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE);
+  }
+  base::string16 battery_percentage_accessible = l10n_util::GetStringFUTF16(
+      IsLinePowerConnected() ?
+      IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE :
+      IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE,
+      base::IntToString16(GetRoundedBatteryPercent()));
+  base::string16 battery_time_accessible = base::string16();
+  if (IsUsbChargerConnected()) {
+    battery_time_accessible = rb.GetLocalizedString(
+        IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE);
+  } else {
+    if (IsBatteryTimeBeingCalculated()) {
+      battery_time_accessible = rb.GetLocalizedString(
+          IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE);
+    } else {
+      base::TimeDelta time = IsLinePowerConnected() ? GetBatteryTimeToFull() :
+          GetBatteryTimeToEmpty();
+      int hour = time.InHours();
+      int min = (time - base::TimeDelta::FromHours(hour)).InMinutes();
+      if (hour || min) {
+        base::string16 minute = min < 10 ?
+            ASCIIToUTF16("0") + base::IntToString16(min) :
+            base::IntToString16(min);
+        battery_time_accessible =
+            l10n_util::GetStringFUTF16(
+                IsLinePowerConnected() ?
+                IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE :
+                IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE,
+                GetBatteryTimeAccessibilityString(hour, min));
+      }
+    }
+  }
+  return battery_time_accessible.empty() ?
+      battery_percentage_accessible :
+      battery_percentage_accessible + ASCIIToUTF16(". ") +
+      battery_time_accessible;
+}
+
+PowerStatus::PowerStatus() {
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+      AddObserver(this);
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+      RequestStatusUpdate();
+}
+
+PowerStatus::~PowerStatus() {
+  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
+      RemoveObserver(this);
+}
+
+void PowerStatus::SetProtoForTesting(
+    const power_manager::PowerSupplyProperties& proto) {
+  proto_ = proto;
+  SanitizeProto(&proto_);
+}
+
+void PowerStatus::PowerChanged(
+    const power_manager::PowerSupplyProperties& proto) {
+  proto_ = proto;
+  SanitizeProto(&proto_);
+  FOR_EACH_OBSERVER(Observer, observers_, OnPowerStatusChanged());
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/system/chromeos/power/power_status.h b/ash/system/chromeos/power/power_status.h
new file mode 100644
index 0000000..4eba247
--- /dev/null
+++ b/ash/system/chromeos/power/power_status.h
@@ -0,0 +1,126 @@
+// Copyright (c) 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_SYSTEM_CHROMEOS_POWER_POWER_STATUS_H_
+#define ASH_SYSTEM_CHROMEOS_POWER_POWER_STATUS_H_
+
+#include "ash/ash_export.h"
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+#include "chromeos/dbus/power_manager_client.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace ash {
+namespace internal {
+
+// PowerStatus is a singleton that receives updates about the system's
+// power status from chromeos::PowerManagerClient and makes the information
+// available to interested classes within Ash.
+class ASH_EXPORT PowerStatus : public chromeos::PowerManagerClient::Observer {
+ public:
+  // Different styles of battery icons.
+  enum IconSet {
+    ICON_LIGHT,
+    ICON_DARK
+  };
+
+  // Interface for classes that wish to be notified when the power status
+  // has changed.
+  class Observer {
+   public:
+    // Called when the power status changes.
+    virtual void OnPowerStatusChanged() = 0;
+
+   protected:
+    virtual ~Observer() {}
+  };
+
+  virtual ~PowerStatus();
+
+  // Sets the global instance. Must be called before any calls to Get().
+  static void Initialize();
+
+  // Destroys the global instance.
+  static void Shutdown();
+
+  // Returns true if the global instance is initialized.
+  static bool IsInitialized();
+
+  // Gets the global instance. Initialize must be called first.
+  static PowerStatus* Get();
+
+  // Adds or removes an observer.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Requests updated status from the power manager.
+  void RequestStatusUpdate();
+
+  // Returns true if a battery is present.
+  bool IsBatteryPresent() const;
+
+  // Returns true if the battery is full.
+  bool IsBatteryFull() const;
+
+  // Returns the battery's remaining charge as a value in the range [0.0,
+  // 100.0].
+  double GetBatteryPercent() const;
+
+  // Returns the battery's remaining charge, rounded to an integer with a
+  // maximum value of 100.
+  int GetRoundedBatteryPercent() const;
+
+  // Returns true if the battery's time-to-full and time-to-empty estimates
+  // should not be displayed because the power manager is still calculating
+  // them.
+  bool IsBatteryTimeBeingCalculated() const;
+
+  // Returns the estimated time until the battery is empty (if line power
+  // is disconnected) or full (if line power is connected). These estimates
+  // should only be used if IsBatteryTimeBeingCalculated() returns false.
+  base::TimeDelta GetBatteryTimeToEmpty() const;
+  base::TimeDelta GetBatteryTimeToFull() const;
+
+  // Returns true if line power (including a charger of any type) is connected.
+  bool IsLinePowerConnected() const;
+
+  // Returns true if an official, non-USB charger is connected.
+  bool IsMainsChargerConnected() const;
+
+  // Returns true if a USB charger (which is likely to only support a low
+  // charging rate) is connected.
+  bool IsUsbChargerConnected() const;
+
+  // Returns the image that should be shown for the battery's current state.
+  gfx::ImageSkia GetBatteryImage(IconSet icon_set) const;
+
+  // Returns an string describing the current state for accessibility.
+  base::string16 GetAccessibleNameString() const;
+
+  // Updates |proto_|. Does not notify observers.
+  void SetProtoForTesting(const power_manager::PowerSupplyProperties& proto);
+
+ protected:
+  PowerStatus();
+
+ private:
+  // Overriden from PowerManagerClient::Observer.
+  virtual void PowerChanged(
+      const power_manager::PowerSupplyProperties& proto) OVERRIDE;
+
+  ObserverList<Observer> observers_;
+
+  // Current state.
+  power_manager::PowerSupplyProperties proto_;
+
+  DISALLOW_COPY_AND_ASSIGN(PowerStatus);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_CHROMEOS_POWER_POWER_STATUS_H_
diff --git a/ash/system/chromeos/power/power_status_unittest.cc b/ash/system/chromeos/power/power_status_unittest.cc
new file mode 100644
index 0000000..831dd3d
--- /dev/null
+++ b/ash/system/chromeos/power/power_status_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 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/chromeos/power/power_status.h"
+
+#include <set>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+class TestObserver : public PowerStatus::Observer {
+ public:
+  TestObserver() : power_changed_count_(0) {}
+  virtual ~TestObserver() {}
+
+  int power_changed_count() const { return power_changed_count_; }
+
+  // PowerStatus::Observer overrides:
+  virtual void OnPowerStatusChanged() OVERRIDE { ++power_changed_count_; }
+
+ private:
+  int power_changed_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+}  // namespace
+
+class PowerStatusTest : public testing::Test {
+ public:
+  PowerStatusTest() : power_status_(NULL) {}
+  virtual ~PowerStatusTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    chromeos::DBusThreadManager::InitializeWithStub();
+    PowerStatus::Initialize();
+    power_status_ = PowerStatus::Get();
+    test_observer_.reset(new TestObserver);
+    power_status_->AddObserver(test_observer_.get());
+  }
+
+  virtual void TearDown() OVERRIDE {
+    power_status_->RemoveObserver(test_observer_.get());
+    test_observer_.reset();
+    PowerStatus::Shutdown();
+    chromeos::DBusThreadManager::Shutdown();
+  }
+
+ protected:
+  base::MessageLoopForUI message_loop_;
+  PowerStatus* power_status_;  // Not owned.
+  scoped_ptr<TestObserver> test_observer_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PowerStatusTest);
+};
+
+TEST_F(PowerStatusTest, PowerStatusInitializeAndUpdate) {
+  // Test that the initial power supply state should be acquired after
+  // PowerStatus is instantiated. This depends on
+  // PowerManagerClientStubImpl, which responds to power status update
+  // requests, pretends there is a battery present, and generates some valid
+  // power supply status data.
+  message_loop_.RunUntilIdle();
+  EXPECT_EQ(1, test_observer_->power_changed_count());
+
+  // Test RequestUpdate, test_obsever_ should be notified for power suuply
+  // status change.
+  power_status_->RequestStatusUpdate();
+  message_loop_.RunUntilIdle();
+  EXPECT_EQ(2, test_observer_->power_changed_count());
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/system/chromeos/power/power_status_view.cc b/ash/system/chromeos/power/power_status_view.cc
index 4267010..4c88784 100644
--- a/ash/system/chromeos/power/power_status_view.cc
+++ b/ash/system/chromeos/power/power_status_view.cc
@@ -6,6 +6,7 @@
 
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/system/chromeos/power/power_status.h"
 #include "ash/system/chromeos/power/tray_power.h"
 #include "ash/system/tray/fixed_sized_image_view.h"
 #include "ash/system/tray/tray_constants.h"
@@ -30,8 +31,6 @@
 const int kLabelMinWidth = 120;
 // Padding between battery status text and battery icon on default view.
 const int kPaddingBetweenBatteryStatusAndIcon = 3;
-// Minimum battery percentage rendered in UI.
-const int kMinBatteryPercent = 1;
 }  // namespace
 
 PowerStatusView::PowerStatusView(ViewType view_type,
@@ -42,10 +41,8 @@
       time_status_label_(NULL),
       percentage_label_(NULL),
       icon_(NULL),
-      icon_image_index_(-1),
-      icon_image_offset_(0),
-      battery_charging_unreliable_(false),
       view_type_(view_type) {
+  PowerStatus::Get()->AddObserver(this);
   if (view_type == VIEW_DEFAULT) {
     time_status_label_ = new views::Label;
     percentage_label_ = new views::Label;
@@ -56,17 +53,22 @@
     time_label_ = new views::Label;
     LayoutNotificationView();
   }
-  Update();
+  OnPowerStatusChanged();
 }
 
-void PowerStatusView::UpdatePowerStatus(
-    const chromeos::PowerSupplyStatus& status) {
-  supply_status_ = status;
-  // Sanitize.
-  if (supply_status_.battery_is_full)
-    supply_status_.battery_percentage = 100.0;
+PowerStatusView::~PowerStatusView() {
+  PowerStatus::Get()->RemoveObserver(this);
+}
 
-  Update();
+void PowerStatusView::OnPowerStatusChanged() {
+  view_type_ == VIEW_DEFAULT ?
+      UpdateTextForDefaultView() : UpdateTextForNotificationView();
+
+  if (icon_) {
+    icon_->SetImage(
+        PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
+    icon_->SetVisible(true);
+  }
 }
 
 void PowerStatusView::LayoutDefaultView() {
@@ -107,43 +109,29 @@
   AddChildView(time_label_);
 }
 
-void PowerStatusView::UpdateText() {
-  view_type_ == VIEW_DEFAULT ?
-      UpdateTextForDefaultView() : UpdateTextForNotificationView();
-  accessible_name_ = TrayPower::GetAccessibleNameString(supply_status_);
-}
-
 void PowerStatusView::UpdateTextForDefaultView() {
+  const PowerStatus& status = *PowerStatus::Get();
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   base::string16 battery_percentage;
   base::string16 battery_time_status;
-  bool is_charging_unreliable =
-      TrayPower::IsBatteryChargingUnreliable(supply_status_);
-  if (supply_status_.line_power_on && supply_status_.battery_is_full) {
+
+  if (status.IsLinePowerConnected() && status.IsBatteryFull()) {
     battery_time_status =
         rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_BATTERY_FULL);
-  } else if (supply_status_.battery_percentage < 0.0f) {
-    battery_time_status =
-        is_charging_unreliable ?
-        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE) :
-        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING);
   } else {
     battery_percentage = l10n_util::GetStringFUTF16(
         IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ONLY,
-        base::IntToString16(TrayPower::GetRoundedBatteryPercentage(
-            supply_status_.battery_percentage)));
-    if (is_charging_unreliable) {
+        base::IntToString16(status.GetRoundedBatteryPercent()));
+    if (status.IsUsbChargerConnected()) {
       battery_time_status = rb.GetLocalizedString(
           IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE);
     } else {
-      if (supply_status_.is_calculating_battery_time) {
+      if (status.IsBatteryTimeBeingCalculated()) {
         battery_time_status =
             rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING);
       } else {
-        base::TimeDelta time = base::TimeDelta::FromSeconds(
-            supply_status_.line_power_on ?
-            supply_status_.battery_seconds_to_full :
-            supply_status_.battery_seconds_to_empty);
+        base::TimeDelta time = status.IsLinePowerConnected() ?
+            status.GetBatteryTimeToFull() : status.GetBatteryTimeToEmpty();
         int hour = time.InHours();
         int min = (time - base::TimeDelta::FromHours(hour)).InMinutes();
         if (hour || min) {
@@ -152,7 +140,7 @@
               base::IntToString16(min);
           battery_time_status =
               l10n_util::GetStringFUTF16(
-                  supply_status_.line_power_on ?
+                  status.IsLinePowerConnected() ?
                   IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT :
                   IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT,
                   base::IntToString16(hour),
@@ -170,60 +158,44 @@
 }
 
 void PowerStatusView::UpdateTextForNotificationView() {
-  int hour = 0;
-  int min = 0;
-  if (!supply_status_.is_calculating_battery_time) {
-    base::TimeDelta time = base::TimeDelta::FromSeconds(
-        supply_status_.line_power_on ?
-        supply_status_.battery_seconds_to_full :
-        supply_status_.battery_seconds_to_empty);
-    hour = time.InHours();
-    min = (time - base::TimeDelta::FromHours(hour)).InMinutes();
-  }
-  bool is_charging_unreliable =
-      TrayPower::IsBatteryChargingUnreliable(supply_status_);
-  if (supply_status_.line_power_on && supply_status_.battery_is_full) {
+  const PowerStatus& status = *PowerStatus::Get();
+  if (status.IsLinePowerConnected() && status.IsBatteryFull()) {
     status_label_->SetText(
         ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
             IDS_ASH_STATUS_TRAY_BATTERY_FULL));
   } else {
-    if (supply_status_.battery_percentage < 0.0f) {
-      // If charging is unreliable and no percentage available, we
-      // leave the top field, |staus_label|, blank. We do not want to
-      // show "Calculating". The user is informed in the bottom field,
-      // |time_label|, that "Charging not reliable".
-      status_label_->SetText(
-          is_charging_unreliable ?
-          base::string16() :
-          ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
-              IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING));
-    } else {
-      status_label_->SetText(
-          l10n_util::GetStringFUTF16(
-              IDS_ASH_STATUS_TRAY_BATTERY_PERCENT,
-              base::IntToString16(TrayPower::GetRoundedBatteryPercentage(
-                  supply_status_.battery_percentage))));
-    }
+    status_label_->SetText(
+        l10n_util::GetStringFUTF16(
+            IDS_ASH_STATUS_TRAY_BATTERY_PERCENT,
+            base::IntToString16(status.GetRoundedBatteryPercent())));
   }
 
-  if (is_charging_unreliable) {
+  int hour = 0;
+  int min = 0;
+  if (!status.IsBatteryTimeBeingCalculated()) {
+    base::TimeDelta time = status.IsLinePowerConnected() ?
+        status.GetBatteryTimeToFull() : status.GetBatteryTimeToEmpty();
+    hour = time.InHours();
+    min = (time - base::TimeDelta::FromHours(hour)).InMinutes();
+  }
+
+  if (status.IsUsbChargerConnected()) {
     time_label_->SetText(
         ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
             IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE));
-  } else if (supply_status_.is_calculating_battery_time) {
+  } else if (status.IsBatteryTimeBeingCalculated()) {
     time_label_->SetText(
         ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
             IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING));
   } else if (hour || min) {
-    if (supply_status_.line_power_on) {
+    if (status.IsLinePowerConnected()) {
       time_label_->SetText(
           l10n_util::GetStringFUTF16(
               IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL,
               base::IntToString16(hour),
               base::IntToString16(min)));
     } else {
-      // This is a low battery warning, which prompts user when battery
-      // time left is not much (ie in minutes).
+      // This is a low battery warning prompting the user in minutes.
       min = hour * 60 + min;
       ShellDelegate* delegate = Shell::GetInstance()->delegate();
       if (delegate) {
@@ -238,54 +210,6 @@
   }
 }
 
-base::string16 PowerStatusView::GetBatteryTimeAccessibilityString(
-    int hour, int min) {
-  DCHECK(hour || min);
-  if (hour && !min) {
-    return Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-        base::TimeDelta::FromHours(hour));
-  } else if (min  && !hour) {
-    return Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-        base::TimeDelta::FromMinutes(min));
-  } else {
-    return l10n_util::GetStringFUTF16(
-        IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE,
-        Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-            base::TimeDelta::FromHours(hour)),
-        Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-            base::TimeDelta::FromMinutes(min)));
-  }
-}
-
-void PowerStatusView::UpdateIcon() {
-  if (icon_) {
-    int index = TrayPower::GetBatteryImageIndex(supply_status_);
-    int offset = TrayPower::GetBatteryImageOffset(supply_status_);
-    bool charging_unreliable =
-        TrayPower::IsBatteryChargingUnreliable(supply_status_);
-    if (icon_image_index_ != index ||
-        icon_image_offset_ != offset ||
-        battery_charging_unreliable_ != charging_unreliable) {
-      icon_image_index_ = index;
-      icon_image_offset_ = offset;
-      battery_charging_unreliable_ = charging_unreliable;
-      if (icon_image_index_ != -1) {
-        icon_->SetImage(
-            TrayPower::GetBatteryImage(icon_image_index_,
-                                       icon_image_offset_,
-                                       battery_charging_unreliable_,
-                                       ICON_DARK));
-      }
-    }
-    icon_->SetVisible(true);
-  }
-}
-
-void PowerStatusView::Update() {
-  UpdateText();
-  UpdateIcon();
-}
-
 void PowerStatusView::ChildPreferredSizeChanged(views::View* child) {
   PreferredSizeChanged();
 }
diff --git a/ash/system/chromeos/power/power_status_view.h b/ash/system/chromeos/power/power_status_view.h
index 421ce40..465da02 100644
--- a/ash/system/chromeos/power/power_status_view.h
+++ b/ash/system/chromeos/power/power_status_view.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SYSTEM_CHROMEOS_POWER_POWER_STATUS_VIEW_H_
 #define ASH_SYSTEM_CHROMEOS_POWER_POWER_STATUS_VIEW_H_
 
-#include "chromeos/dbus/power_supply_status.h"
+#include "ash/system/chromeos/power/power_status.h"
 #include "ui/views/view.h"
 
 namespace views {
@@ -16,7 +16,7 @@
 namespace ash {
 namespace internal {
 
-class PowerStatusView : public views::View {
+class PowerStatusView : public views::View, public PowerStatus::Observer {
  public:
   enum ViewType {
     VIEW_DEFAULT,
@@ -24,25 +24,21 @@
   };
 
   PowerStatusView(ViewType view_type, bool default_view_right_align);
-  virtual ~PowerStatusView() {}
-
-  void UpdatePowerStatus(const chromeos::PowerSupplyStatus& status);
-  const base::string16& accessible_name() const { return accessible_name_; }
+  virtual ~PowerStatusView();
 
   // Overridden from views::View.
   virtual gfx::Size GetPreferredSize() OVERRIDE;
   virtual int GetHeightForWidth(int width) OVERRIDE;
   virtual void Layout() OVERRIDE;
 
+  // Overridden from PowerStatus::Observer.
+  virtual void OnPowerStatusChanged() OVERRIDE;
+
  private:
   void LayoutDefaultView();
   void LayoutNotificationView();
-  void UpdateText();
-  void UpdateIcon();
-  void Update();
   void UpdateTextForDefaultView();
   void UpdateTextForNotificationView();
-  base::string16 GetBatteryTimeAccessibilityString(int hour, int min);
 
   // Overridden from views::View.
   virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
@@ -62,24 +58,8 @@
   // Battery status indicator icon.
   views::ImageView* icon_;
 
-  // Index of the current icon in the icon array image, or -1 if unknown.
-  int icon_image_index_;
-
-  // Horizontal offset of the current icon in the icon array image.
-  int icon_image_offset_;
-
-  // Battery charging may be unreliable for non-standard power supplies.
-  // It may change from charging to discharging frequently depending on
-  // charger power and current power consumption. We show different UIs
-  // when in this state. See TrayPower::IsBatteryChargingUnreliable.
-  bool battery_charging_unreliable_;
-
   ViewType view_type_;
 
-  chromeos::PowerSupplyStatus supply_status_;
-
-  base::string16 accessible_name_;
-
   DISALLOW_COPY_AND_ASSIGN(PowerStatusView);
 };
 
diff --git a/ash/system/chromeos/power/tray_power.cc b/ash/system/chromeos/power/tray_power.cc
index 9dc87cc..4fe4e68 100644
--- a/ash/system/chromeos/power/tray_power.cc
+++ b/ash/system/chromeos/power/tray_power.cc
@@ -5,30 +5,20 @@
 #include "ash/system/chromeos/power/tray_power.h"
 
 #include "ash/ash_switches.h"
-#include "ash/shell.h"
-#include "ash/shell_delegate.h"
 #include "ash/system/chromeos/power/power_status_view.h"
 #include "ash/system/date/date_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_notification_view.h"
 #include "ash/system/tray/tray_utils.h"
 #include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chromeos/dbus/power_supply_status.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "third_party/icu/public/i18n/unicode/fieldpos.h"
 #include "third_party/icu/public/i18n/unicode/fmtable.h"
-#include "third_party/skia/include/core/SkRect.h"
 #include "ui/base/accessibility/accessible_view_state.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia.h"
-#include "ui/gfx/image/image_skia_operations.h"
-#include "ui/gfx/size.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
@@ -38,18 +28,13 @@
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
-using chromeos::PowerManagerHandler;
-using chromeos::PowerSupplyStatus;
+using message_center::MessageCenter;
+using message_center::Notification;
 
 namespace ash {
 namespace internal {
 
 namespace {
-// Width and height of battery images.
-const int kBatteryImageHeight = 25;
-const int kBatteryImageWidth = 25;
-// Number of different power states.
-const int kNumPowerImages = 15;
 // Top/bottom padding of the text items.
 const int kPaddingVertical = 10;
 // Specify min width of status label for layout.
@@ -58,31 +43,11 @@
 const int kCriticalSeconds = 5 * 60;
 const int kLowPowerSeconds = 15 * 60;
 const int kNoWarningSeconds = 30 * 60;
-// Minimum battery percentage rendered in UI.
-const int kMinBatteryPercent = 1;
 // Notification in battery percentage.
 const double kCriticalPercentage = 5.0;
 const double kLowPowerPercentage = 10.0;
 const double kNoWarningPercentage = 15.0;
 
-base::string16 GetBatteryTimeAccessibilityString(int hour, int min) {
-  DCHECK(hour || min);
-  if (hour && !min) {
-    return Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-        base::TimeDelta::FromHours(hour));
-  }
-  if (min && !hour) {
-    return Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-        base::TimeDelta::FromMinutes(min));
-  }
-  return l10n_util::GetStringFUTF16(
-      IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE,
-      Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-          base::TimeDelta::FromHours(hour)),
-      Shell::GetInstance()->delegate()->GetTimeDurationLongString(
-          base::TimeDelta::FromMinutes(min)));
-}
-
 }  // namespace
 
 namespace tray {
@@ -90,10 +55,7 @@
 // This view is used only for the tray.
 class PowerTrayView : public views::ImageView {
  public:
-  PowerTrayView()
-      : battery_icon_index_(-1),
-        battery_icon_offset_(0),
-        battery_charging_unreliable_(false) {
+  PowerTrayView() {
     UpdateImage();
   }
 
@@ -106,94 +68,42 @@
     state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
   }
 
-  void UpdatePowerStatus(const PowerSupplyStatus& status,
-                         bool battery_alert) {
-    supply_status_ = status;
-    // Sanitize.
-    if (supply_status_.battery_is_full)
-      supply_status_.battery_percentage = 100.0;
-
+  void UpdateStatus(bool battery_alert) {
     UpdateImage();
-    SetVisible(status.battery_is_present);
+    SetVisible(PowerStatus::Get()->IsBatteryPresent());
 
     if (battery_alert) {
-      accessible_name_ = TrayPower::GetAccessibleNameString(status);
+      accessible_name_ = PowerStatus::Get()->GetAccessibleNameString();
       NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
     }
   }
 
  private:
   void UpdateImage() {
-    int index = TrayPower::GetBatteryImageIndex(supply_status_);
-    int offset = TrayPower::GetBatteryImageOffset(supply_status_);
-    bool charging_unreliable =
-        TrayPower::IsBatteryChargingUnreliable(supply_status_);
-    if (battery_icon_index_ != index ||
-        battery_icon_offset_ != offset ||
-        battery_charging_unreliable_ != charging_unreliable) {
-      battery_icon_index_ = index;
-      battery_icon_offset_ = offset;
-      battery_charging_unreliable_ = charging_unreliable;
-      if (battery_icon_index_ != -1)
-        SetImage(TrayPower::GetBatteryImage(battery_icon_index_,
-                                            battery_icon_offset_,
-                                            battery_charging_unreliable_,
-                                            ICON_LIGHT));
-    }
+    SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
   }
 
-  PowerSupplyStatus supply_status_;
   base::string16 accessible_name_;
 
-  // Index of the current icon in the icon array image, or -1 if unknown.
-  int battery_icon_index_;
-  int battery_icon_offset_;
-  bool battery_charging_unreliable_;
-
   DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
 };
 
 class PowerNotificationView : public TrayNotificationView {
  public:
   explicit PowerNotificationView(TrayPower* owner)
-      : TrayNotificationView(owner, 0),
-        battery_icon_index_(-1),
-        battery_icon_offset_(0),
-        battery_charging_unreliable_(false) {
+      : TrayNotificationView(owner, 0) {
     power_status_view_ =
         new PowerStatusView(PowerStatusView::VIEW_NOTIFICATION, true);
     InitView(power_status_view_);
   }
 
-  void UpdatePowerStatus(const PowerSupplyStatus& status) {
-    int index = TrayPower::GetBatteryImageIndex(status);
-    int offset = TrayPower::GetBatteryImageOffset(status);
-    bool charging_unreliable = TrayPower::IsBatteryChargingUnreliable(status);
-    if (battery_icon_index_ != index ||
-        battery_icon_offset_ != offset ||
-        battery_charging_unreliable_ != charging_unreliable) {
-      battery_icon_index_ = index;
-      battery_icon_offset_ = offset;
-      battery_charging_unreliable_ = charging_unreliable;
-      if (battery_icon_index_ != -1) {
-        SetIconImage(TrayPower::GetBatteryImage(
-                         battery_icon_index_,
-                         battery_icon_offset_,
-                         battery_charging_unreliable_,
-                         ICON_DARK));
-      }
-    }
-    power_status_view_->UpdatePowerStatus(status);
+  void UpdateStatus() {
+    SetIconImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
   }
 
  private:
   PowerStatusView* power_status_view_;
 
-  // Index of the current icon in the icon array image, or -1 if unknown.
-  int battery_icon_index_;
-  int battery_icon_offset_;
-  bool battery_charging_unreliable_;
-
   DISALLOW_COPY_AND_ASSIGN(PowerNotificationView);
 };
 
@@ -201,170 +111,43 @@
 
 using tray::PowerNotificationView;
 
-TrayPower::TrayPower(SystemTray* system_tray)
+TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
     : SystemTrayItem(system_tray),
+      message_center_(message_center),
       power_tray_(NULL),
       notification_view_(NULL),
-      notification_state_(NOTIFICATION_NONE) {
-  PowerManagerHandler::Get()->AddObserver(this);
+      notification_state_(NOTIFICATION_NONE),
+      usb_charger_was_connected_(false) {
+  PowerStatus::Get()->AddObserver(this);
 }
 
 TrayPower::~TrayPower() {
-  if (PowerManagerHandler::IsInitialized())
-    PowerManagerHandler::Get()->RemoveObserver(this);
-}
-
-// static
-bool TrayPower::IsBatteryChargingUnreliable(
-    const chromeos::PowerSupplyStatus& supply_status) {
-  // Sometimes devices can get into a state where the battery is almost fully
-  // charged and the power subsystem reports "neither charging nor discharging"
-  // despite the battery not at 100%. For now, only report unreliable charging
-  // on USB.
-  // TODO(derat): Update this when the power manager code is refactored for M29.
-  return supply_status.battery_state == PowerSupplyStatus::CONNECTED_TO_USB;
-}
-
-// static
-int TrayPower::GetBatteryImageIndex(
-    const chromeos::PowerSupplyStatus& supply_status) {
-  int image_index = 0;
-  if (supply_status.battery_percentage >= 100) {
-    image_index = kNumPowerImages - 1;
-  } else if (!supply_status.battery_is_present) {
-    image_index = kNumPowerImages;
-  } else {
-    image_index = static_cast<int>(supply_status.battery_percentage /
-                                   100.0 * (kNumPowerImages - 1));
-    image_index = std::max(std::min(image_index, kNumPowerImages - 2), 0);
-  }
-  return image_index;
-}
-
-// static
-int TrayPower::GetBatteryImageOffset(
-    const chromeos::PowerSupplyStatus& supply_status) {
-  if (IsBatteryChargingUnreliable(supply_status) ||
-      !supply_status.line_power_on)
-    return 0;
-  return 1;
-}
-
-// static
-gfx::ImageSkia TrayPower::GetBatteryImage(int image_index,
-                                          int image_offset,
-                                          bool charging_unreliable,
-                                          IconSet icon_set) {
-  gfx::Image all;
-  if (charging_unreliable) {
-    all = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-        icon_set == ICON_DARK ?
-        IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE_DARK :
-        IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE);
-  } else {
-    all = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
-        icon_set == ICON_DARK ?
-        IDR_AURA_UBER_TRAY_POWER_SMALL_DARK : IDR_AURA_UBER_TRAY_POWER_SMALL);
-  }
-  gfx::Rect region(
-      image_offset * kBatteryImageWidth,
-      image_index * kBatteryImageHeight,
-      kBatteryImageWidth, kBatteryImageHeight);
-  return gfx::ImageSkiaOperations::ExtractSubset(*all.ToImageSkia(), region);
-}
-
-// static
-base::string16 TrayPower::GetAccessibleNameString(
-    const chromeos::PowerSupplyStatus& supply_status) {
-  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
-  if (supply_status.line_power_on && supply_status.battery_is_full) {
-    return rb.GetLocalizedString(
-        IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE);
-  }
-  bool charging_unreliable =
-      IsBatteryChargingUnreliable(supply_status);
-  if (supply_status.battery_percentage < 0.0f) {
-    if (charging_unreliable) {
-      return rb.GetLocalizedString(
-          IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE);
-    }
-    return rb.GetLocalizedString(
-        IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE);
-  }
-  base::string16 battery_percentage_accessbile = l10n_util::GetStringFUTF16(
-      supply_status.line_power_on ?
-      IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE:
-      IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE ,
-      base::IntToString16(GetRoundedBatteryPercentage(
-          supply_status.battery_percentage)));
-  base::string16 battery_time_accessible = base::string16();
-  if (charging_unreliable) {
-    battery_time_accessible = rb.GetLocalizedString(
-        IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE);
-  } else {
-    if (supply_status.is_calculating_battery_time) {
-      battery_time_accessible = rb.GetLocalizedString(
-          IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE);
-    } else {
-      base::TimeDelta time = base::TimeDelta::FromSeconds(
-          supply_status.line_power_on ?
-          supply_status.battery_seconds_to_full :
-          supply_status.battery_seconds_to_empty);
-      int hour = time.InHours();
-      int min = (time - base::TimeDelta::FromHours(hour)).InMinutes();
-      if (hour || min) {
-        base::string16 minute = min < 10 ?
-            ASCIIToUTF16("0") + base::IntToString16(min) :
-            base::IntToString16(min);
-        battery_time_accessible =
-            l10n_util::GetStringFUTF16(
-                supply_status.line_power_on ?
-                IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE :
-                IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE,
-                GetBatteryTimeAccessibilityString(hour, min));
-      }
-    }
-  }
-  return battery_time_accessible.empty() ?
-      battery_percentage_accessbile :
-      battery_percentage_accessbile + ASCIIToUTF16(". ")
-      + battery_time_accessible;
-}
-
-// static
-int TrayPower::GetRoundedBatteryPercentage(double battery_percentage) {
-  DCHECK(battery_percentage >= 0.0);
-  return std::max(kMinBatteryPercent,
-      static_cast<int>(battery_percentage + 0.5));
+  PowerStatus::Get()->RemoveObserver(this);
 }
 
 views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
   // There may not be enough information when this is created about whether
   // there is a battery or not. So always create this, and adjust visibility as
   // necessary.
-  PowerSupplyStatus power_status =
-      PowerManagerHandler::Get()->GetPowerSupplyStatus();
   CHECK(power_tray_ == NULL);
   power_tray_ = new tray::PowerTrayView();
-  power_tray_->UpdatePowerStatus(power_status, false);
+  power_tray_->UpdateStatus(false);
   return power_tray_;
 }
 
 views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
   // Make sure icon status is up-to-date. (Also triggers stub activation).
-  RequestStatusUpdate();
+  PowerStatus::Get()->RequestStatusUpdate();
   return NULL;
 }
 
 views::View* TrayPower::CreateNotificationView(user::LoginStatus status) {
   CHECK(notification_view_ == NULL);
-  PowerSupplyStatus power_status =
-      PowerManagerHandler::Get()->GetPowerSupplyStatus();
-  if (!power_status.battery_is_present)
+  if (!PowerStatus::Get()->IsBatteryPresent())
     return NULL;
 
   notification_view_ = new PowerNotificationView(this);
-  notification_view_->UpdatePowerStatus(power_status);
+  notification_view_->UpdateStatus();
 
   return notification_view_;
 }
@@ -387,48 +170,77 @@
   SetTrayImageItemBorder(power_tray_, alignment);
 }
 
-void TrayPower::OnPowerStatusChanged(
-    const chromeos::PowerSupplyStatus& status) {
-  bool battery_alert = UpdateNotificationState(status);
+void TrayPower::OnPowerStatusChanged() {
+  bool battery_alert = UpdateNotificationState();
   if (power_tray_)
-    power_tray_->UpdatePowerStatus(status, battery_alert);
+    power_tray_->UpdateStatus(battery_alert);
   if (notification_view_)
-    notification_view_->UpdatePowerStatus(status);
+    notification_view_->UpdateStatus();
 
   // Factory testing may place the battery into unusual states.
   if (CommandLine::ForCurrentProcess()->HasSwitch(
           ash::switches::kAshHideNotificationsForFactory))
     return;
 
+  if (ash::switches::UseUsbChargerNotification())
+    MaybeShowUsbChargerNotification();
+
   if (battery_alert)
     ShowNotificationView();
   else if (notification_state_ == NOTIFICATION_NONE)
     HideNotificationView();
+
+  usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
 }
 
-void TrayPower::RequestStatusUpdate() const {
-  PowerManagerHandler::Get()->RequestStatusUpdate();
+bool TrayPower::MaybeShowUsbChargerNotification() {
+  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+  const char kNotificationId[] = "usb-charger";
+  bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
+
+  // Check for a USB charger being connected.
+  if (usb_charger_is_connected && !usb_charger_was_connected_) {
+    scoped_ptr<Notification> notification(new Notification(
+        message_center::NOTIFICATION_TYPE_SIMPLE,
+        kNotificationId,
+        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
+        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE),
+        rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
+        base::string16(),
+        std::string(),
+        message_center::RichNotificationData(),
+        NULL));
+    message_center_->AddNotification(notification.Pass());
+    return true;
+  }
+
+  // Check for unplug of a USB charger while the USB charger notification is
+  // showing.
+  if (!usb_charger_is_connected && usb_charger_was_connected_) {
+    message_center_->RemoveNotification(kNotificationId, false);
+    return true;
+  }
+  return false;
 }
 
-bool TrayPower::UpdateNotificationState(
-    const chromeos::PowerSupplyStatus& status) {
-  if (!status.battery_is_present ||
-      status.is_calculating_battery_time ||
-      status.battery_state == PowerSupplyStatus::CHARGING) {
+bool TrayPower::UpdateNotificationState() {
+  const PowerStatus& status = *PowerStatus::Get();
+  if (!status.IsBatteryPresent() ||
+      status.IsBatteryTimeBeingCalculated() ||
+      status.IsMainsChargerConnected()) {
     notification_state_ = NOTIFICATION_NONE;
     return false;
   }
 
-  if (TrayPower::IsBatteryChargingUnreliable(status)) {
-    return UpdateNotificationStateForRemainingPercentage(
-        status.battery_percentage);
-  } else {
-    return UpdateNotificationStateForRemainingTime(
-        status.battery_seconds_to_empty);
-  }
+  return status.IsUsbChargerConnected() ?
+      UpdateNotificationStateForRemainingPercentage() :
+      UpdateNotificationStateForRemainingTime();
 }
 
-bool TrayPower::UpdateNotificationStateForRemainingTime(int remaining_seconds) {
+bool TrayPower::UpdateNotificationStateForRemainingTime() {
+  const int remaining_seconds =
+      PowerStatus::Get()->GetBatteryTimeToEmpty().InSeconds();
+
   if (remaining_seconds >= kNoWarningSeconds) {
     notification_state_ = NOTIFICATION_NONE;
     return false;
@@ -439,7 +251,8 @@
       if (remaining_seconds <= kCriticalSeconds) {
         notification_state_ = NOTIFICATION_CRITICAL;
         return true;
-      } else if (remaining_seconds <= kLowPowerSeconds) {
+      }
+      if (remaining_seconds <= kLowPowerSeconds) {
         notification_state_ = NOTIFICATION_LOW_POWER;
         return true;
       }
@@ -457,8 +270,9 @@
   return false;
 }
 
-bool TrayPower::UpdateNotificationStateForRemainingPercentage(
-    double remaining_percentage) {
+bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
+  const double remaining_percentage = PowerStatus::Get()->GetBatteryPercent();
+
   if (remaining_percentage > kNoWarningPercentage) {
     notification_state_ = NOTIFICATION_NONE;
     return false;
diff --git a/ash/system/chromeos/power/tray_power.h b/ash/system/chromeos/power/tray_power.h
index db33f35..2a66cd5 100644
--- a/ash/system/chromeos/power/tray_power.h
+++ b/ash/system/chromeos/power/tray_power.h
@@ -5,15 +5,20 @@
 #ifndef ASH_SYSTEM_CHROMEOS_POWER_TRAY_POWER_H_
 #define ASH_SYSTEM_CHROMEOS_POWER_TRAY_POWER_H_
 
+#include "ash/system/chromeos/power/power_status.h"
 #include "ash/system/tray/system_tray_item.h"
-#include "chromeos/power/power_manager_handler.h"
 
 class SkBitmap;
 
 namespace gfx {
+class Image;
 class ImageSkia;
 }
 
+namespace message_center {
+class MessageCenter;
+}
+
 namespace ash {
 namespace internal {
 
@@ -22,56 +27,27 @@
 class PowerTrayView;
 }
 
-enum IconSet {
-  ICON_LIGHT,
-  ICON_DARK
-};
-
-class TrayPower : public SystemTrayItem,
-                  public chromeos::PowerManagerHandler::Observer {
+class ASH_EXPORT TrayPower : public SystemTrayItem,
+                             public PowerStatus::Observer {
  public:
-  explicit TrayPower(SystemTray* system_tray);
-  virtual ~TrayPower();
-
-  // Gets whether battery charging is unreliable for |supply_status|.
-  // When a non-standard power supply is connected, the battery may
-  // change from being charged to discharged frequently depending on the
-  // charger power and power consumption, i.e usage. In this case we
-  // do not want to show either a charging or discharging state.
-  static bool IsBatteryChargingUnreliable(
-      const chromeos::PowerSupplyStatus& supply_status);
-
-  // Gets the icon index in the battery icon array image based on
-  // |supply_status|.  If |supply_status| is uncertain about the power state,
-  // returns -1.
-  static int GetBatteryImageIndex(
-      const chromeos::PowerSupplyStatus& supply_status);
-
-  // Gets the horizontal offset in the battery icon array image based on
-  // |supply_status|.
-  static int GetBatteryImageOffset(
-      const chromeos::PowerSupplyStatus& supply_status);
-
-  // Looks up the actual icon in the icon array image for |image_index|.
-  static gfx::ImageSkia GetBatteryImage(int image_index,
-                                        int image_offset,
-                                        bool charging_unreliable,
-                                        IconSet icon_set);
-
-  // Gets the battery accessible string for |supply_status|.
-  static base::string16 GetAccessibleNameString(
-      const chromeos::PowerSupplyStatus& supply_status);
-
-  // Gets rounded battery percentage for |battery_percentage|.
-  static int GetRoundedBatteryPercentage(double battery_percentage);
-
- private:
+  // Visible for testing.
   enum NotificationState {
     NOTIFICATION_NONE,
+
+    // Low battery charge.
     NOTIFICATION_LOW_POWER,
-    NOTIFICATION_CRITICAL
+
+    // Critically low battery charge.
+    NOTIFICATION_CRITICAL,
   };
 
+  TrayPower(SystemTray* system_tray,
+            message_center::MessageCenter* message_center);
+  virtual ~TrayPower();
+
+ private:
+  friend class TrayPowerTest;
+
   // Overridden from SystemTrayItem.
   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE;
   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE;
@@ -84,23 +60,27 @@
   virtual void UpdateAfterShelfAlignmentChange(
       ShelfAlignment alignment) OVERRIDE;
 
-  // Overridden from chromeos::PowerManagerHandler::Observer.
-  virtual void OnPowerStatusChanged(
-      const chromeos::PowerSupplyStatus& status) OVERRIDE;
+  // Overridden from PowerStatus::Observer.
+  virtual void OnPowerStatusChanged() OVERRIDE;
 
-  // Requests a power status update.
-  void RequestStatusUpdate() const;
+  // Show a notification that a low-power USB charger has been connected.
+  // Returns true if a notification was shown or explicitly hidden.
+  bool MaybeShowUsbChargerNotification();
 
   // Sets |notification_state_|. Returns true if a notification should be shown.
-  bool UpdateNotificationState(const chromeos::PowerSupplyStatus& status);
-  bool UpdateNotificationStateForRemainingTime(int remaining_seconds);
-  bool UpdateNotificationStateForRemainingPercentage(
-      double remaining_percentage);
+  bool UpdateNotificationState();
+  bool UpdateNotificationStateForRemainingTime();
+  bool UpdateNotificationStateForRemainingPercentage();
 
+  message_center::MessageCenter* message_center_;  // Not owned.
   tray::PowerTrayView* power_tray_;
   tray::PowerNotificationView* notification_view_;
   NotificationState notification_state_;
 
+  // Was a USB charger connected the last time OnPowerStatusChanged() was
+  // called?
+  bool usb_charger_was_connected_;
+
   DISALLOW_COPY_AND_ASSIGN(TrayPower);
 };
 
diff --git a/ash/system/chromeos/power/tray_power_unittest.cc b/ash/system/chromeos/power/tray_power_unittest.cc
new file mode 100644
index 0000000..82bac27
--- /dev/null
+++ b/ash/system/chromeos/power/tray_power_unittest.cc
@@ -0,0 +1,173 @@
+// 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/chromeos/power/tray_power.h"
+
+#include "ash/ash_switches.h"
+#include "ash/test/ash_test_base.h"
+#include "base/memory/scoped_ptr.h"
+#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
+#include "ui/message_center/fake_message_center.h"
+
+using message_center::Notification;
+using power_manager::PowerSupplyProperties;
+
+namespace {
+
+class MockMessageCenter : public message_center::FakeMessageCenter {
+ public:
+  MockMessageCenter() : add_count_(0), remove_count_(0) {}
+  virtual ~MockMessageCenter() {}
+
+  int add_count() const { return add_count_; }
+  int remove_count() const { return remove_count_; }
+
+  // message_center::FakeMessageCenter overrides:
+  virtual void AddNotification(scoped_ptr<Notification> notification) OVERRIDE {
+    add_count_++;
+  }
+  virtual void RemoveNotification(const std::string& id, bool by_user)
+      OVERRIDE {
+    remove_count_++;
+  }
+
+ private:
+  int add_count_;
+  int remove_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockMessageCenter);
+};
+
+}  // namespace
+
+namespace ash {
+namespace internal {
+
+class TrayPowerTest : public test::AshTestBase {
+ public:
+  TrayPowerTest() {}
+  virtual ~TrayPowerTest() {}
+
+  MockMessageCenter* message_center() { return message_center_.get(); }
+  TrayPower* tray_power() { return tray_power_.get(); }
+
+  // test::AshTestBase::SetUp() overrides:
+  virtual void SetUp() OVERRIDE {
+    test::AshTestBase::SetUp();
+    message_center_.reset(new MockMessageCenter());
+    tray_power_.reset(new TrayPower(NULL, message_center_.get()));
+  }
+
+  virtual void TearDown() OVERRIDE {
+    tray_power_.reset();
+    message_center_.reset();
+    test::AshTestBase::TearDown();
+  }
+
+  TrayPower::NotificationState notification_state() const {
+    return tray_power_->notification_state_;
+  }
+
+  bool MaybeShowUsbChargerNotification(const PowerSupplyProperties& proto) {
+    PowerStatus::Get()->SetProtoForTesting(proto);
+    return tray_power_->MaybeShowUsbChargerNotification();
+  }
+
+  bool UpdateNotificationState(const PowerSupplyProperties& proto) {
+    PowerStatus::Get()->SetProtoForTesting(proto);
+    return tray_power_->UpdateNotificationState();
+  }
+
+  void SetUsbChargerConnected(bool connected) {
+    tray_power_->usb_charger_was_connected_ = connected;
+   }
+
+  // Returns a discharging PowerSupplyProperties more appropriate for testing.
+  static PowerSupplyProperties DefaultPowerSupplyProperties() {
+    PowerSupplyProperties proto;
+    proto.set_external_power(
+        power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED);
+    proto.set_battery_state(
+        power_manager::PowerSupplyProperties_BatteryState_DISCHARGING);
+    proto.set_battery_percent(50.0);
+    proto.set_battery_time_to_empty_sec(3 * 60 * 60);
+    proto.set_battery_time_to_full_sec(2 * 60 * 60);
+    proto.set_is_calculating_battery_time(false);
+    return proto;
+  }
+
+ private:
+  scoped_ptr<MockMessageCenter> message_center_;
+  scoped_ptr<TrayPower> tray_power_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrayPowerTest);
+};
+
+TEST_F(TrayPowerTest, MaybeShowUsbChargerNotification) {
+  PowerSupplyProperties discharging = DefaultPowerSupplyProperties();
+  EXPECT_FALSE(MaybeShowUsbChargerNotification(discharging));
+  EXPECT_EQ(0, message_center()->add_count());
+  EXPECT_EQ(0, message_center()->remove_count());
+
+  // Notification shows when connecting a USB charger.
+  PowerSupplyProperties usb_connected = DefaultPowerSupplyProperties();
+  usb_connected.set_external_power(
+      power_manager::PowerSupplyProperties_ExternalPower_USB);
+  EXPECT_TRUE(MaybeShowUsbChargerNotification(usb_connected));
+  EXPECT_EQ(1, message_center()->add_count());
+  EXPECT_EQ(0, message_center()->remove_count());
+
+  // Change in charge does not trigger the notification again.
+  PowerSupplyProperties more_charge = DefaultPowerSupplyProperties();
+  more_charge.set_external_power(
+      power_manager::PowerSupplyProperties_ExternalPower_USB);
+  more_charge.set_battery_time_to_full_sec(60 * 60);
+  more_charge.set_battery_percent(75.0);
+  SetUsbChargerConnected(true);
+  EXPECT_FALSE(MaybeShowUsbChargerNotification(more_charge));
+  EXPECT_EQ(1, message_center()->add_count());
+  EXPECT_EQ(0, message_center()->remove_count());
+
+  // Disconnecting a USB charger with the notification showing should close
+  // the notification.
+  EXPECT_TRUE(MaybeShowUsbChargerNotification(discharging));
+  EXPECT_EQ(1, message_center()->add_count());
+  EXPECT_EQ(1, message_center()->remove_count());
+}
+
+TEST_F(TrayPowerTest, UpdateNotificationState) {
+  // No notifications when no battery present.
+  PowerSupplyProperties no_battery = DefaultPowerSupplyProperties();
+  no_battery.set_external_power(
+      power_manager::PowerSupplyProperties_ExternalPower_AC);
+  no_battery.set_battery_state(
+      power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT);
+  EXPECT_FALSE(UpdateNotificationState(no_battery));
+  EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state());
+
+  // No notification when calculating remaining battery time.
+  PowerSupplyProperties calculating = DefaultPowerSupplyProperties();
+  calculating.set_is_calculating_battery_time(true);
+  EXPECT_FALSE(UpdateNotificationState(calculating));
+  EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state());
+
+  // No notification when charging.
+  PowerSupplyProperties charging = DefaultPowerSupplyProperties();
+  charging.set_external_power(
+      power_manager::PowerSupplyProperties_ExternalPower_AC);
+  charging.set_battery_state(
+      power_manager::PowerSupplyProperties_BatteryState_CHARGING);
+  EXPECT_FALSE(UpdateNotificationState(charging));
+  EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state());
+
+  // Critical low battery notification.
+  PowerSupplyProperties critical = DefaultPowerSupplyProperties();
+  critical.set_battery_time_to_empty_sec(60);
+  critical.set_battery_percent(2.0);
+  EXPECT_TRUE(UpdateNotificationState(critical));
+  EXPECT_EQ(TrayPower::NOTIFICATION_CRITICAL, notification_state());
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/system/chromeos/screen_security/screen_capture_tray_item.h b/ash/system/chromeos/screen_security/screen_capture_tray_item.h
index a756416..f1fb4b5 100644
--- a/ash/system/chromeos/screen_security/screen_capture_tray_item.h
+++ b/ash/system/chromeos/screen_security/screen_capture_tray_item.h
@@ -15,8 +15,8 @@
 namespace ash {
 namespace internal {
 
-class ScreenCaptureTrayItem : public ScreenTrayItem,
-                              public ScreenCaptureObserver {
+class ASH_EXPORT ScreenCaptureTrayItem : public ScreenTrayItem,
+                                         public ScreenCaptureObserver {
  public:
   explicit ScreenCaptureTrayItem(SystemTray* system_tray);
   virtual ~ScreenCaptureTrayItem();
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 c1971e2..3e10763 100644
--- a/ash/system/chromeos/screen_security/screen_share_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
@@ -25,7 +25,7 @@
 
 views::View* ScreenShareTrayItem::CreateTrayView(user::LoginStatus status) {
   set_tray_view(
-      new tray::ScreenTrayView(this, IDR_AURA_UBER_TRAY_SCREEN_SHARE_LIGHT));
+      new tray::ScreenTrayView(this, IDR_AURA_UBER_TRAY_DISPLAY_LIGHT));
   return tray_view();
 }
 
@@ -33,7 +33,7 @@
   set_default_view(new tray::ScreenStatusView(
       this,
       tray::ScreenStatusView::VIEW_DEFAULT,
-      IDR_AURA_UBER_TRAY_SCREEN_SHARE_DARK,
+      IDR_AURA_UBER_TRAY_DISPLAY,
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_SCREEN_SHARE_BEING_HELPED),
       l10n_util::GetStringUTF16(
@@ -55,7 +55,7 @@
 
   set_notification_view(new tray::ScreenNotificationView(
       this,
-      IDR_AURA_UBER_TRAY_SCREEN_SHARE_DARK,
+      IDR_AURA_UBER_TRAY_DISPLAY,
       help_label_text,
       l10n_util::GetStringUTF16(
           IDS_ASH_STATUS_TRAY_SCREEN_SHARE_STOP)));
diff --git a/ash/system/chromeos/screen_security/screen_share_tray_item.h b/ash/system/chromeos/screen_security/screen_share_tray_item.h
index 84a29d0..4624ac2 100644
--- a/ash/system/chromeos/screen_security/screen_share_tray_item.h
+++ b/ash/system/chromeos/screen_security/screen_share_tray_item.h
@@ -15,8 +15,8 @@
 namespace ash {
 namespace internal {
 
-class ScreenShareTrayItem : public ScreenTrayItem,
-                            public ScreenShareObserver {
+class ASH_EXPORT ScreenShareTrayItem : public ScreenTrayItem,
+                                       public ScreenShareObserver {
  public:
   explicit ScreenShareTrayItem(SystemTray* system_tray);
   virtual ~ScreenShareTrayItem();
diff --git a/ash/system/chromeos/screen_security/screen_tray_item.h b/ash/system/chromeos/screen_security/screen_tray_item.h
index ff7e1bb..03b8435 100644
--- a/ash/system/chromeos/screen_security/screen_tray_item.h
+++ b/ash/system/chromeos/screen_security/screen_tray_item.h
@@ -99,7 +99,7 @@
 // The base tray item for screen capture and screen sharing. The
 // Start method brings up a notification and a tray item, and the user
 // can stop the screen capture/sharing by pressing the stop button.
-class ScreenTrayItem : public SystemTrayItem {
+class ASH_EXPORT ScreenTrayItem : public SystemTrayItem {
  public:
   explicit ScreenTrayItem(SystemTray* system_tray);
   virtual ~ScreenTrayItem();
diff --git a/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc b/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc
new file mode 100644
index 0000000..d95bed9
--- /dev/null
+++ b/ash/system/chromeos/screen_security/screen_tray_item_unittest.cc
@@ -0,0 +1,227 @@
+// 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/chromeos/screen_security/screen_tray_item.h"
+
+#include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
+#include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
+#include "ash/system/tray/tray_item_view.h"
+#include "ash/test/ash_test_base.h"
+#include "base/callback.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/events/event.h"
+#include "ui/gfx/point.h"
+#include "ui/views/view.h"
+
+namespace ash {
+namespace internal {
+
+// Test with unicode strings.
+const char kTestScreenCaptureAppName[] =
+    "\xE0\xB2\xA0\x5F\xE0\xB2\xA0 (Screen Capture Test)";
+const char kTestScreenShareHelperName[] =
+    "\xE5\xAE\x8B\xE8\x85\xBE (Screen Share Test)";
+
+SystemTray* GetSystemTray() {
+  return Shell::GetInstance()->GetPrimarySystemTray();
+}
+
+SystemTrayNotifier* GetSystemTrayNotifier() {
+  return Shell::GetInstance()->system_tray_notifier();
+}
+
+void ClickViewCenter(views::View* view) {
+  gfx::Point click_location_in_local =
+      gfx::Point(view->width() / 2, view->height() / 2);
+  view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED,
+                                      click_location_in_local,
+                                      click_location_in_local,
+                                      ui::EF_NONE));
+}
+
+class ScreenTrayItemTest : public ash::test::AshTestBase {
+ public:
+  ScreenTrayItemTest()
+      : tray_item_(NULL), stop_callback_hit_count_(0) {}
+  virtual ~ScreenTrayItemTest() {}
+
+  ScreenTrayItem* tray_item() { return tray_item_; }
+  void set_tray_item(ScreenTrayItem* tray_item) { tray_item_ = tray_item; }
+
+  int stop_callback_hit_count() const { return stop_callback_hit_count_; }
+
+  virtual void SetUp() OVERRIDE {
+    test::AshTestBase::SetUp();
+    TrayItemView::DisableAnimationsForTest();
+  }
+
+  void StartSession() {
+    tray_item_->Start(
+        base::Bind(&ScreenTrayItemTest::StopCallback, base::Unretained(this)));
+  }
+
+  void StopSession() {
+    tray_item_->Stop();
+  }
+
+  void StopCallback() {
+    stop_callback_hit_count_++;
+  }
+
+ private:
+  ScreenTrayItem* tray_item_;
+  int stop_callback_hit_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenTrayItemTest);
+};
+
+class ScreenCaptureTest : public ScreenTrayItemTest {
+ public:
+  ScreenCaptureTest() {}
+  virtual ~ScreenCaptureTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    ScreenTrayItemTest::SetUp();
+    // This tray item is owned by its parent system tray view and will
+    // be deleted automatically when its parent is destroyed in AshTestBase.
+    ScreenTrayItem* tray_item = new ScreenCaptureTrayItem(GetSystemTray());
+    GetSystemTray()->AddTrayItem(tray_item);
+    set_tray_item(tray_item);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenCaptureTest);
+};
+
+class ScreenShareTest : public ScreenTrayItemTest {
+ public:
+  ScreenShareTest() {}
+  virtual ~ScreenShareTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    ScreenTrayItemTest::SetUp();
+    // This tray item is owned by its parent system tray view and will
+    // be deleted automatically when its parent is destroyed in AshTestBase.
+    ScreenTrayItem* tray_item = new ScreenShareTrayItem(GetSystemTray());
+    GetSystemTray()->AddTrayItem(tray_item);
+    set_tray_item(tray_item);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenShareTest);
+};
+
+void TestStartAndStop(ScreenTrayItemTest* test) {
+  ScreenTrayItem* tray_item = test->tray_item();
+
+  EXPECT_FALSE(tray_item->is_started());
+  EXPECT_EQ(0, test->stop_callback_hit_count());
+
+  test->StartSession();
+  EXPECT_TRUE(tray_item->is_started());
+
+  test->StopSession();
+  EXPECT_FALSE(tray_item->is_started());
+  EXPECT_EQ(1, test->stop_callback_hit_count());
+}
+
+TEST_F(ScreenCaptureTest, StartAndStop) { TestStartAndStop(this); }
+TEST_F(ScreenShareTest, StartAndStop) { TestStartAndStop(this); }
+
+void TestNotificationStartAndStop(ScreenTrayItemTest* test,
+                                  const base::Closure& start_function,
+                                  const base::Closure& stop_function) {
+  ScreenTrayItem* tray_item = test->tray_item();
+  EXPECT_FALSE(tray_item->is_started());
+
+  start_function.Run();
+  EXPECT_TRUE(tray_item->is_started());
+
+  // The stop callback shouldn't be called because we stopped
+  // through the notification system.
+  stop_function.Run();
+  EXPECT_FALSE(tray_item->is_started());
+  EXPECT_EQ(0, test->stop_callback_hit_count());
+}
+
+TEST_F(ScreenCaptureTest, NotificationStartAndStop) {
+  base::Closure start_function =
+      base::Bind(&SystemTrayNotifier::NotifyScreenCaptureStart,
+          base::Unretained(GetSystemTrayNotifier()),
+          base::Bind(&ScreenTrayItemTest::StopCallback,
+                     base::Unretained(this)),
+                     base::UTF8ToUTF16(kTestScreenCaptureAppName));
+
+  base::Closure stop_function =
+      base::Bind(&SystemTrayNotifier::NotifyScreenCaptureStop,
+          base::Unretained(GetSystemTrayNotifier()));
+
+  TestNotificationStartAndStop(this, start_function, stop_function);
+}
+
+TEST_F(ScreenShareTest, NotificationStartAndStop) {
+  base::Closure start_func =
+      base::Bind(&SystemTrayNotifier::NotifyScreenShareStart,
+          base::Unretained(GetSystemTrayNotifier()),
+          base::Bind(&ScreenTrayItemTest::StopCallback,
+                     base::Unretained(this)),
+                     base::UTF8ToUTF16(kTestScreenShareHelperName));
+
+  base::Closure stop_func =
+      base::Bind(&SystemTrayNotifier::NotifyScreenShareStop,
+          base::Unretained(GetSystemTrayNotifier()));
+
+  TestNotificationStartAndStop(this, start_func, stop_func);
+}
+
+void TestNotificationView(ScreenTrayItemTest* test) {
+  ScreenTrayItem* tray_item = test->tray_item();
+
+  test->StartSession();
+  EXPECT_TRUE(tray_item->notification_view()->visible());
+
+  // Clicking on the notification view should dismiss the view
+  ClickViewCenter(tray_item->notification_view());
+  EXPECT_FALSE(tray_item->notification_view());
+
+  test->StopSession();
+}
+
+TEST_F(ScreenCaptureTest, NotificationView) { TestNotificationView(this); }
+TEST_F(ScreenShareTest, NotificationView) { TestNotificationView(this); }
+
+void TestSystemTrayInteraction(ScreenTrayItemTest* test) {
+  ScreenTrayItem* tray_item = test->tray_item();
+  EXPECT_FALSE(tray_item->tray_view()->visible());
+
+  const std::vector<SystemTrayItem*>& tray_items =
+      GetSystemTray()->GetTrayItems();
+  EXPECT_NE(std::find(tray_items.begin(), tray_items.end(), tray_item),
+            tray_items.end());
+
+  test->StartSession();
+  EXPECT_TRUE(tray_item->tray_view()->visible());
+
+  // The default view should be created in a new bubble.
+  GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
+  EXPECT_TRUE(tray_item->default_view());
+  GetSystemTray()->CloseSystemBubble();
+  EXPECT_FALSE(tray_item->default_view());
+
+  test->StopSession();
+  EXPECT_FALSE(tray_item->tray_view()->visible());
+
+  // The default view should not be visible because session is stopped.
+  GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
+  EXPECT_FALSE(tray_item->default_view()->visible());
+}
+
+TEST_F(ScreenCaptureTest, SystemTrayInteraction) {
+  TestSystemTrayInteraction(this);
+}
+
+TEST_F(ScreenShareTest, SystemTrayInteraction) {
+  TestSystemTrayInteraction(this);
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/chromeos/settings/tray_settings.cc b/ash/system/chromeos/settings/tray_settings.cc
index a721299..8153aea 100644
--- a/ash/system/chromeos/settings/tray_settings.cc
+++ b/ash/system/chromeos/settings/tray_settings.cc
@@ -5,6 +5,7 @@
 #include "ash/system/chromeos/settings/tray_settings.h"
 
 #include "ash/shell.h"
+#include "ash/system/chromeos/power/power_status.h"
 #include "ash/system/chromeos/power/power_status_view.h"
 #include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/fixed_sized_image_view.h"
@@ -12,7 +13,6 @@
 #include "ash/system/tray/tray_constants.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chromeos/dbus/power_manager_client.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -30,12 +30,14 @@
 
 namespace tray {
 
-class SettingsDefaultView : public ActionableView {
+class SettingsDefaultView : public ActionableView,
+                            public PowerStatus::Observer {
  public:
   explicit SettingsDefaultView(user::LoginStatus status)
       : login_status_(status),
         label_(NULL),
         power_status_view_(NULL) {
+    PowerStatus::Get()->AddObserver(this);
     SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
         ash::kTrayPopupPaddingHorizontal, 0,
         ash::kTrayPopupPaddingBetweenItems));
@@ -58,27 +60,16 @@
       power_view_right_align = true;
     }
 
-    chromeos::PowerSupplyStatus power_status =
-        chromeos::PowerManagerHandler::Get()->GetPowerSupplyStatus();
-    if (power_status.battery_is_present) {
+    if (PowerStatus::Get()->IsBatteryPresent()) {
       power_status_view_ = new ash::internal::PowerStatusView(
           ash::internal::PowerStatusView::VIEW_DEFAULT, power_view_right_align);
       AddChildView(power_status_view_);
-      UpdatePowerStatus(power_status);
+      OnPowerStatusChanged();
     }
   }
 
-  virtual ~SettingsDefaultView() {}
-
-  void UpdatePowerStatus(const chromeos::PowerSupplyStatus& status) {
-    if (!power_status_view_)
-      return;
-    power_status_view_->UpdatePowerStatus(status);
-    base::string16 accessible_name = label_ ?
-        label_->text() + ASCIIToUTF16(", ") +
-            power_status_view_->accessible_name() :
-        power_status_view_->accessible_name();
-    SetAccessibleName(accessible_name);
+  virtual ~SettingsDefaultView() {
+    PowerStatus::Get()->RemoveObserver(this);
   }
 
   // Overridden from ash::internal::ActionableView.
@@ -112,6 +103,18 @@
     Layout();
   }
 
+  // Overridden from PowerStatus::Observer.
+  virtual void OnPowerStatusChanged() OVERRIDE {
+    if (!PowerStatus::Get()->IsBatteryPresent())
+      return;
+
+    base::string16 accessible_name = label_ ?
+        label_->text() + ASCIIToUTF16(", ") +
+            PowerStatus::Get()->GetAccessibleNameString() :
+        PowerStatus::Get()->GetAccessibleNameString();
+    SetAccessibleName(accessible_name);
+  }
+
  private:
   user::LoginStatus login_status_;
   views::Label* label_;
@@ -125,12 +128,9 @@
 TraySettings::TraySettings(SystemTray* system_tray)
     : SystemTrayItem(system_tray),
       default_view_(NULL) {
-  chromeos::PowerManagerHandler::Get()->AddObserver(this);
 }
 
 TraySettings::~TraySettings() {
-  if (chromeos::PowerManagerHandler::IsInitialized())
-    chromeos::PowerManagerHandler::Get()->RemoveObserver(this);
 }
 
 views::View* TraySettings::CreateTrayView(user::LoginStatus status) {
@@ -139,9 +139,7 @@
 
 views::View* TraySettings::CreateDefaultView(user::LoginStatus status) {
   if ((status == user::LOGGED_IN_NONE || status == user::LOGGED_IN_LOCKED) &&
-      (chromeos::PowerManagerHandler::IsInitialized() &&
-       !chromeos::PowerManagerHandler::Get()->
-           GetPowerSupplyStatus().battery_is_present))
+      !PowerStatus::Get()->IsBatteryPresent())
     return NULL;
 
   CHECK(default_view_ == NULL);
@@ -167,11 +165,5 @@
 void TraySettings::UpdateAfterLoginStatusChange(user::LoginStatus status) {
 }
 
-void TraySettings::OnPowerStatusChanged(
-    const chromeos::PowerSupplyStatus& status) {
-  if (default_view_)
-    default_view_->UpdatePowerStatus(status);
-}
-
 }  // namespace internal
 }  // namespace ash
diff --git a/ash/system/chromeos/settings/tray_settings.h b/ash/system/chromeos/settings/tray_settings.h
index c0dc9db..e48aad3 100644
--- a/ash/system/chromeos/settings/tray_settings.h
+++ b/ash/system/chromeos/settings/tray_settings.h
@@ -6,7 +6,6 @@
 #define ASH_SYSTEM_CHROMEOS_SETTINGS_TRAY_SETTINGS_H_
 
 #include "ash/system/tray/system_tray_item.h"
-#include "chromeos/power/power_manager_handler.h"
 
 namespace ash {
 namespace internal {
@@ -15,8 +14,7 @@
 class SettingsDefaultView;
 }
 
-class TraySettings : public SystemTrayItem,
-                     public chromeos::PowerManagerHandler::Observer {
+class TraySettings : public SystemTrayItem {
  public:
   explicit TraySettings(SystemTray* system_tray);
   virtual ~TraySettings();
@@ -31,10 +29,6 @@
   virtual void DestroyDetailedView() OVERRIDE;
   virtual void UpdateAfterLoginStatusChange(user::LoginStatus status) OVERRIDE;
 
-  // Overridden from chromeos::PowerManagerHandler::Observer.
-  virtual void OnPowerStatusChanged(
-      const chromeos::PowerSupplyStatus& status) OVERRIDE;
-
   tray::SettingsDefaultView* default_view_;
 
   DISALLOW_COPY_AND_ASSIGN(TraySettings);
diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc
index cf3ca84..29c07aa 100644
--- a/ash/system/chromeos/tray_display.cc
+++ b/ash/system/chromeos/tray_display.cc
@@ -7,43 +7,97 @@
 #include "ash/display/display_controller.h"
 #include "ash/display/display_manager.h"
 #include "ash/shell.h"
+#include "ash/system/tray/actionable_view.h"
 #include "ash/system/tray/fixed_sized_image_view.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_notification_view.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
+#include "ui/message_center/notification_delegate.h"
+#include "ui/message_center/notification_list.h"
+#include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 
+using message_center::Notification;
+
 namespace ash {
 namespace internal {
 namespace {
 
-TrayDisplayMode GetCurrentTrayDisplayMode() {
-  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
-  if (display_manager->GetNumDisplays() > 1)
-    return TRAY_DISPLAY_EXTENDED;
+static const char kDisplayNotificationId[] = "chrome://settings/display";
 
-  if (display_manager->IsMirrored())
-    return TRAY_DISPLAY_MIRRORED;
+DisplayManager* GetDisplayManager() {
+  return Shell::GetInstance()->display_manager();
+}
 
-  int64 first_id = display_manager->first_display_id();
-  if (display_manager->HasInternalDisplay() &&
-      !display_manager->IsInternalDisplayId(first_id)) {
-    return TRAY_DISPLAY_DOCKED;
+base::string16 GetDisplayName(int64 display_id) {
+  return UTF8ToUTF16(GetDisplayManager()->GetDisplayNameForId(display_id));
+}
+
+// Returns 1-line information for the specified display, like
+// "InternalDisplay: 1280x750"
+base::string16 GetDisplayInfoLine(int64 display_id) {
+  const DisplayInfo& display_info =
+      GetDisplayManager()->GetDisplayInfo(display_id);
+
+  base::string16 size_text = UTF8ToUTF16(
+      display_info.size_in_pixel().ToString());
+  base::string16 display_data;
+  if (display_info.has_overscan()) {
+    display_data = l10n_util::GetStringFUTF16(
+        IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
+        size_text,
+        l10n_util::GetStringUTF16(
+            IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN));
+  } else {
+    display_data = size_text;
   }
 
-  return TRAY_DISPLAY_SINGLE;
+  return l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
+      GetDisplayName(display_id),
+      display_data);
+}
+
+base::string16 GetAllDisplayInfo() {
+  DisplayManager* display_manager = GetDisplayManager();
+  std::vector<base::string16> lines;
+  int64 internal_id = gfx::Display::kInvalidDisplayID;
+  // Make sure to show the internal display first.
+  if (display_manager->HasInternalDisplay() &&
+      display_manager->IsInternalDisplayId(
+          display_manager->first_display_id())) {
+    internal_id = display_manager->first_display_id();
+    lines.push_back(GetDisplayInfoLine(internal_id));
+  }
+
+  for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
+    int64 id = display_manager->GetDisplayAt(i)->id();
+    if (id == internal_id)
+      continue;
+    lines.push_back(GetDisplayInfoLine(id));
+  }
+
+  if (display_manager->IsMirrored()) {
+    lines.push_back(GetDisplayInfoLine(
+        display_manager->mirrored_display().id()));
+  }
+
+  return JoinString(lines, '\n');
 }
 
 // Returns the name of the currently connected external display.
 base::string16 GetExternalDisplayName() {
-  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  DisplayManager* display_manager = GetDisplayManager();
   int64 external_id = display_manager->mirrored_display().id();
 
   if (external_id == gfx::Display::kInvalidDisplayID) {
@@ -56,82 +110,120 @@
       }
     }
   }
-  if (external_id != gfx::Display::kInvalidDisplayID)
-    return UTF8ToUTF16(display_manager->GetDisplayNameForId(external_id));
-  return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
+
+  if (external_id == gfx::Display::kInvalidDisplayID)
+    return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
+
+  // The external display name may have an annotation of "(width x height)" in
+  // case that the display is rotated or its resolution is changed.
+  base::string16 name = GetDisplayName(external_id);
+  const DisplayInfo& display_info =
+      display_manager->GetDisplayInfo(external_id);
+  if (display_info.rotation() != gfx::Display::ROTATE_0 ||
+      display_info.ui_scale() != 1.0f ||
+      !display_info.overscan_insets_in_dip().empty()) {
+    name += UTF8ToUTF16(
+        " (" + display_info.size_in_pixel().ToString() + ")");
+  } else if (display_info.overscan_insets_in_dip().empty() &&
+             display_info.has_overscan()) {
+    name += UTF8ToUTF16(" (") + l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN) + UTF8ToUTF16(")");
+  }
+
+  return name;
 }
 
-class DisplayViewBase {
- public:
-  DisplayViewBase(user::LoginStatus login_status)
-      : login_status_(login_status) {
-    label_ = new views::Label();
-    label_->SetMultiLine(true);
-    label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+base::string16 GetTrayDisplayMessage() {
+  DisplayManager* display_manager = GetDisplayManager();
+  if (display_manager->GetNumDisplays() > 1) {
+    if (GetDisplayManager()->HasInternalDisplay()) {
+      return l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName());
+    }
+    return l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
   }
 
-  virtual ~DisplayViewBase() {
+  if (display_manager->IsMirrored()) {
+    if (GetDisplayManager()->HasInternalDisplay()) {
+      return l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetExternalDisplayName());
+    }
+    return l10n_util::GetStringUTF16(
+        IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
   }
 
+  int64 first_id = display_manager->first_display_id();
+  if (display_manager->HasInternalDisplay() &&
+      !display_manager->IsInternalDisplayId(first_id)) {
+    return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED);
+  }
+
+  return base::string16();
+}
+
+void OpenSettings(user::LoginStatus login_status) {
+  if (login_status == ash::user::LOGGED_IN_USER ||
+      login_status == ash::user::LOGGED_IN_OWNER ||
+      login_status == ash::user::LOGGED_IN_GUEST) {
+    ash::Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
+  }
+}
+
+class DisplayNotificationDelegate
+    : public message_center::NotificationDelegate {
+ public:
+  DisplayNotificationDelegate(user::LoginStatus login_status)
+      : login_status_(login_status) {}
+
+  // message_center::NotificationDelegate overrides:
+  virtual void Display() OVERRIDE {}
+  virtual void Error() OVERRIDE {}
+  virtual void Close(bool by_user) OVERRIDE {}
+  virtual bool HasClickedListener() OVERRIDE { return true; }
+  virtual void Click() OVERRIDE { OpenSettings(login_status_); }
+
  protected:
-  void OpenSettings() {
-    if (login_status_ == ash::user::LOGGED_IN_USER ||
-        login_status_ == ash::user::LOGGED_IN_OWNER ||
-        login_status_ == ash::user::LOGGED_IN_GUEST) {
-      ash::Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
-    }
-  }
-
-  bool UpdateLabelText() {
-    switch (GetCurrentTrayDisplayMode()) {
-      case TRAY_DISPLAY_SINGLE:
-        // TODO(oshima|mukai): Support single display mode for overscan
-        // alignment.
-        return false;
-      case TRAY_DISPLAY_EXTENDED:
-        if (Shell::GetInstance()->display_manager()->HasInternalDisplay()) {
-          label_->SetText(l10n_util::GetStringFUTF16(
-              IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetExternalDisplayName()));
-        } else {
-          label_->SetText(l10n_util::GetStringUTF16(
-              IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL));
-        }
-        break;
-      case TRAY_DISPLAY_MIRRORED:
-        if (Shell::GetInstance()->display_manager()->HasInternalDisplay()) {
-          label_->SetText(l10n_util::GetStringFUTF16(
-              IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetExternalDisplayName()));
-        } else {
-          label_->SetText(l10n_util::GetStringUTF16(
-              IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL));
-        }
-        break;
-      case TRAY_DISPLAY_DOCKED:
-        label_->SetText(l10n_util::GetStringUTF16(
-            IDS_ASH_STATUS_TRAY_DISPLAY_DOCKED));
-        break;
-    }
-    return true;
-  }
-
-  views::Label* label() { return label_; }
+  virtual ~DisplayNotificationDelegate() {}
 
  private:
   user::LoginStatus login_status_;
-  views::Label* label_;
 
-  DISALLOW_COPY_AND_ASSIGN(DisplayViewBase);
+  DISALLOW_COPY_AND_ASSIGN(DisplayNotificationDelegate);
 };
 
+void UpdateDisplayNotification(const base::string16& message) {
+  // Always remove the notification to make sure the notification appears
+  // as a popup in any situation.
+  message_center::MessageCenter::Get()->RemoveNotification(
+      kDisplayNotificationId, false /* by_user */);
+
+  if (message.empty())
+    return;
+
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  scoped_ptr<Notification> notification(new Notification(
+      message_center::NOTIFICATION_TYPE_SIMPLE,
+      kDisplayNotificationId,
+      message,
+      GetAllDisplayInfo(),
+      bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
+      base::string16(),  // display_source
+      "",  // extension_id
+      message_center::RichNotificationData(),
+      new DisplayNotificationDelegate(
+          Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus())));
+  message_center::MessageCenter::Get()->AddNotification(notification.Pass());
+}
+
 }  // namespace
 
-class DisplayView : public DisplayViewBase,
-                    public ash::internal::ActionableView {
+class DisplayView : public ash::internal::ActionableView {
  public:
   explicit DisplayView(user::LoginStatus login_status)
-      : DisplayViewBase(login_status) {
-    SetLayoutManager(new
-        views::BoxLayout(views::BoxLayout::kHorizontal,
+      : login_status_(login_status) {
+    SetLayoutManager(new views::BoxLayout(
+        views::BoxLayout::kHorizontal,
         ash::kTrayPopupPaddingHorizontal, 0,
         ash::kTrayPopupPaddingBetweenItems));
 
@@ -141,70 +233,107 @@
     image_->SetImage(
         bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY).ToImageSkia());
     AddChildView(image_);
-    AddChildView(label());
+
+    label_ = new views::Label();
+    label_->SetMultiLine(true);
+    label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    AddChildView(label_);
     Update();
   }
 
   virtual ~DisplayView() {}
 
   void Update() {
-    SetVisible(UpdateLabelText());
+    base::string16 message = GetTrayDisplayMessage();
+    if (message.empty() && ShouldShowFirstDisplayInfo())
+      message = GetDisplayInfoLine(GetDisplayManager()->first_display_id());
+    SetVisible(!message.empty());
+    label_->SetText(message);
+  }
+
+  views::Label* label() { return label_; }
+
+  // Overridden from views::View.
+  virtual bool GetTooltipText(const gfx::Point& p,
+                              base::string16* tooltip) const OVERRIDE {
+    base::string16 tray_message = GetTrayDisplayMessage();
+    base::string16 display_message = GetAllDisplayInfo();
+    if (tray_message.empty() && display_message.empty())
+      return false;
+
+    *tooltip = tray_message + ASCIIToUTF16("\n") + display_message;
+    return true;
   }
 
  private:
+  bool ShouldShowFirstDisplayInfo() const {
+    const DisplayInfo& display_info = GetDisplayManager()->GetDisplayInfo(
+        GetDisplayManager()->first_display_id());
+    return display_info.rotation() != gfx::Display::ROTATE_0 ||
+        display_info.ui_scale() != 1.0f ||
+        !display_info.overscan_insets_in_dip().empty() ||
+        display_info.has_overscan();
+  }
+
   // Overridden from ActionableView.
   virtual bool PerformAction(const ui::Event& event) OVERRIDE {
-    OpenSettings();
+    OpenSettings(login_status_);
     return true;
   }
 
   virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE {
     int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
         kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
-    label()->SizeToFit(label_max_width);
+    label_->SizeToFit(label_max_width);
     PreferredSizeChanged();
   }
 
+  user::LoginStatus login_status_;
   views::ImageView* image_;
+  views::Label* label_;
 
   DISALLOW_COPY_AND_ASSIGN(DisplayView);
 };
 
-class DisplayNotificationView : public DisplayViewBase,
-                                public TrayNotificationView {
+class DisplayNotificationView : public TrayNotificationView {
  public:
   DisplayNotificationView(user::LoginStatus login_status,
-                          TrayDisplay* tray_item)
-      : DisplayViewBase(login_status),
-        TrayNotificationView(tray_item, IDR_AURA_UBER_TRAY_DISPLAY) {
-    InitView(label());
+                          TrayDisplay* tray_item,
+                          const base::string16& message)
+      : TrayNotificationView(tray_item, IDR_AURA_UBER_TRAY_DISPLAY),
+        login_status_(login_status) {
     StartAutoCloseTimer(kTrayPopupAutoCloseDelayForTextInSeconds);
-    Update();
+    Update(message);
   }
 
   virtual ~DisplayNotificationView() {}
 
-  void Update() {
-    if (UpdateLabelText())
-      RestartAutoCloseTimer();
-    else
+  void Update(const base::string16& message) {
+    if (message.empty()) {
       owner()->HideNotificationView();
+    } else {
+      views::Label* label = new views::Label(message);
+      label->SetMultiLine(true);
+      label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+      UpdateView(label);
+      RestartAutoCloseTimer();
+    }
   }
 
   // Overridden from TrayNotificationView:
   virtual void OnClickAction() OVERRIDE {
-    OpenSettings();
+    OpenSettings(login_status_);
   }
 
  private:
+  user::LoginStatus login_status_;
+
   DISALLOW_COPY_AND_ASSIGN(DisplayNotificationView);
 };
 
 TrayDisplay::TrayDisplay(SystemTray* system_tray)
     : SystemTrayItem(system_tray),
-      default_(NULL),
-      notification_(NULL),
-      current_mode_(GetCurrentTrayDisplayMode()) {
+      default_(NULL) {
   Shell::GetInstance()->display_controller()->AddObserver(this);
 }
 
@@ -212,39 +341,86 @@
   Shell::GetInstance()->display_controller()->RemoveObserver(this);
 }
 
+base::string16 TrayDisplay::GetDisplayMessageForNotification() {
+  DisplayManager* display_manager = GetDisplayManager();
+  DisplayInfoMap old_info;
+  old_info.swap(display_info_);
+  for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
+    int64 id = display_manager->GetDisplayAt(i)->id();
+    display_info_[id] = display_manager->GetDisplayInfo(id);
+  }
+
+  // Display is added or removed. Use the same message as the one in
+  // the system tray.
+  if (display_info_.size() != old_info.size())
+    return GetTrayDisplayMessage();
+
+  for (DisplayInfoMap::const_iterator iter = display_info_.begin();
+       iter != display_info_.end(); ++iter) {
+    DisplayInfoMap::const_iterator old_iter = old_info.find(iter->first);
+    // A display is removed and added at the same time. It won't happen
+    // in the actual environment, but falls back to the system tray's
+    // message just in case.
+    if (old_iter == old_info.end())
+      return GetTrayDisplayMessage();
+
+    if (iter->second.ui_scale() != old_iter->second.ui_scale()) {
+      return l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
+          GetDisplayName(iter->first),
+          UTF8ToUTF16(iter->second.size_in_pixel().ToString()));
+    }
+    if (iter->second.rotation() != old_iter->second.rotation()) {
+      return l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED, GetDisplayName(iter->first));
+    }
+  }
+
+  // Found nothing special
+  return base::string16();
+}
+
 views::View* TrayDisplay::CreateDefaultView(user::LoginStatus status) {
   DCHECK(default_ == NULL);
   default_ = new DisplayView(status);
   return default_;
 }
 
-views::View* TrayDisplay::CreateNotificationView(user::LoginStatus status) {
-  DCHECK(notification_ == NULL);
-  notification_ = new DisplayNotificationView(status, this);
-  return notification_;
-}
-
 void TrayDisplay::DestroyDefaultView() {
   default_ = NULL;
 }
 
-void TrayDisplay::DestroyNotificationView() {
-  notification_ = NULL;
-}
-
-bool TrayDisplay::ShouldShowLauncher() const {
-  return false;
-}
-
 void TrayDisplay::OnDisplayConfigurationChanged() {
-  TrayDisplayMode new_mode = GetCurrentTrayDisplayMode();
-  if (current_mode_ != new_mode && new_mode != TRAY_DISPLAY_SINGLE) {
-    if (notification_)
-      notification_->Update();
-    else
-      ShowNotificationView();
+  if (!Shell::GetInstance()->system_tray_delegate()->
+          ShouldShowDisplayNotification()) {
+    return;
   }
-  current_mode_ = new_mode;
+
+  UpdateDisplayNotification(GetDisplayMessageForNotification());
+}
+
+base::string16 TrayDisplay::GetDefaultViewMessage() {
+  if (!default_ || !default_->visible())
+    return base::string16();
+
+  return static_cast<DisplayView*>(default_)->label()->text();
+}
+
+base::string16 TrayDisplay::GetNotificationMessage() {
+  message_center::NotificationList::Notifications notifications =
+      message_center::MessageCenter::Get()->GetNotifications();
+  for (message_center::NotificationList::Notifications::const_iterator iter =
+           notifications.begin(); iter != notifications.end(); ++iter) {
+    if ((*iter)->id() == kDisplayNotificationId)
+      return (*iter)->title();
+  }
+
+  return base::string16();
+}
+
+void TrayDisplay::CloseNotificationForTest() {
+  message_center::MessageCenter::Get()->RemoveNotification(
+      kDisplayNotificationId, false);
 }
 
 }  // namespace internal
diff --git a/ash/system/chromeos/tray_display.h b/ash/system/chromeos/tray_display.h
index 0265704..7ede247 100644
--- a/ash/system/chromeos/tray_display.h
+++ b/ash/system/chromeos/tray_display.h
@@ -5,47 +5,54 @@
 #ifndef ASH_SYSTEM_CHROMEOS_TRAY_DISPLAY_H_
 #define ASH_SYSTEM_CHROMEOS_TRAY_DISPLAY_H_
 
-#include "ash/display/display_controller.h"
-#include "ash/system/tray/system_tray_item.h"
+#include <map>
 
-namespace views {
-class View;
-}
+#include "ash/ash_export.h"
+#include "ash/display/display_controller.h"
+#include "ash/display/display_info.h"
+#include "ash/system/tray/system_tray_item.h"
+#include "base/strings/string16.h"
+#include "ui/views/view.h"
 
 namespace ash {
+namespace test {
+class AshTestBase;
+}
+
 namespace internal {
 
-enum TrayDisplayMode {
-  TRAY_DISPLAY_SINGLE,
-  TRAY_DISPLAY_EXTENDED,
-  TRAY_DISPLAY_MIRRORED,
-  TRAY_DISPLAY_DOCKED,
-};
-
-class DisplayView;
 class DisplayNotificationView;
 
-class TrayDisplay : public SystemTrayItem,
-                    public DisplayController::Observer {
+class ASH_EXPORT TrayDisplay : public SystemTrayItem,
+                               public DisplayController::Observer {
  public:
   explicit TrayDisplay(SystemTray* system_tray);
   virtual ~TrayDisplay();
 
  private:
+  friend class TrayDisplayTest;
+
+  typedef std::map<int64, DisplayInfo> DisplayInfoMap;
+
+  // Checks the current display settings and determine what message should be
+  // shown for notification.
+  base::string16 GetDisplayMessageForNotification();
+
   // Overridden from SystemTrayItem.
   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE;
-  virtual views::View* CreateNotificationView(
-      user::LoginStatus status) OVERRIDE;
   virtual void DestroyDefaultView() OVERRIDE;
-  virtual void DestroyNotificationView() OVERRIDE;
-  virtual bool ShouldShowLauncher() const OVERRIDE;
 
   // Overridden from DisplayControllerObserver:
   virtual void OnDisplayConfigurationChanged() OVERRIDE;
 
-  DisplayView* default_;
-  DisplayNotificationView* notification_;
-  TrayDisplayMode current_mode_;
+  // Test accessors.
+  base::string16 GetDefaultViewMessage();
+  base::string16 GetNotificationMessage();
+  void CloseNotificationForTest();
+  views::View* default_view() { return default_; }
+
+  views::View* default_;
+  DisplayInfoMap display_info_;
 
   DISALLOW_COPY_AND_ASSIGN(TrayDisplay);
 };
diff --git a/ash/system/chromeos/tray_display_unittest.cc b/ash/system/chromeos/tray_display_unittest.cc
new file mode 100644
index 0000000..1501a84
--- /dev/null
+++ b/ash/system/chromeos/tray_display_unittest.cc
@@ -0,0 +1,397 @@
+// 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/chromeos/tray_display.h"
+
+#include "ash/display/display_manager.h"
+#include "ash/root_window_controller.h"
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/test_system_tray_delegate.h"
+#include "ash/test/ash_test_base.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/display.h"
+#include "ui/views/controls/label.h"
+
+namespace ash {
+namespace internal {
+
+base::string16 GetTooltipText(const base::string16& headline,
+                              const base::string16& name1,
+                              const std::string& data1,
+                              const base::string16& name2,
+                              const std::string& data2) {
+  std::vector<base::string16> lines;
+  lines.push_back(headline);
+  lines.push_back(l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
+      name1, UTF8ToUTF16(data1)));
+  if (!name2.empty()) {
+    lines.push_back(l10n_util::GetStringFUTF16(
+        IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
+        name2, UTF8ToUTF16(data2)));
+  }
+  return JoinString(lines, '\n');
+}
+
+base::string16 GetFirstDisplayName() {
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  return UTF8ToUTF16(display_manager->GetDisplayNameForId(
+      display_manager->first_display_id()));
+}
+
+base::string16 GetSecondDisplayName() {
+  return UTF8ToUTF16(
+      Shell::GetInstance()->display_manager()->GetDisplayNameForId(
+          ScreenAsh::GetSecondaryDisplay().id()));
+}
+
+base::string16 GetMirroredDisplayName() {
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  return UTF8ToUTF16(display_manager->GetDisplayNameForId(
+      display_manager->mirrored_display().id()));
+}
+
+class TrayDisplayTest : public ash::test::AshTestBase {
+ public:
+  TrayDisplayTest();
+  virtual ~TrayDisplayTest();
+
+  virtual void SetUp() OVERRIDE;
+
+ protected:
+  SystemTray* tray() { return tray_; }
+
+  void CloseNotification();
+  bool IsDisplayVisibleInTray();
+  base::string16 GetTrayDisplayText();
+  base::string16 GetTrayDisplayTooltipText();
+  base::string16 GetDisplayNotificationText();
+
+ private:
+  // Weak reference, owned by Shell.
+  SystemTray* tray_;
+
+  // Weak reference, owned by |tray_|.
+  TrayDisplay* tray_display_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrayDisplayTest);
+};
+
+TrayDisplayTest::TrayDisplayTest() : tray_(NULL), tray_display_(NULL) {
+}
+
+TrayDisplayTest::~TrayDisplayTest() {
+}
+
+void TrayDisplayTest::SetUp() {
+  ash::test::AshTestBase::SetUp();
+  tray_ = Shell::GetPrimaryRootWindowController()->GetSystemTray();
+  tray_display_ = new TrayDisplay(tray_);
+  tray_->AddTrayItem(tray_display_);
+}
+
+void TrayDisplayTest::CloseNotification() {
+  tray_display_->CloseNotificationForTest();
+  RunAllPendingInMessageLoop();
+}
+
+bool TrayDisplayTest::IsDisplayVisibleInTray() {
+  return tray_display_->default_view() &&
+      tray_display_->default_view()->visible();
+}
+
+base::string16 TrayDisplayTest::GetTrayDisplayText() {
+  return tray_display_->GetDefaultViewMessage();
+}
+
+base::string16 TrayDisplayTest::GetTrayDisplayTooltipText() {
+  if (!tray_display_->default_view())
+    return base::string16();
+
+  base::string16 tooltip;
+  if (!tray_display_->default_view()->GetTooltipText(gfx::Point(), &tooltip))
+    return base::string16();
+  return tooltip;
+}
+
+base::string16 TrayDisplayTest::GetDisplayNotificationText() {
+  return tray_display_->GetNotificationMessage();
+}
+
+TEST_F(TrayDisplayTest, NoInternalDisplay) {
+  UpdateDisplay("400x400");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_FALSE(IsDisplayVisibleInTray());
+
+  UpdateDisplay("400x400,200x200");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  base::string16 expected = l10n_util::GetStringUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED_NO_INTERNAL);
+  base::string16 first_name = GetFirstDisplayName();
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "400x400",
+                           GetSecondDisplayName(), "200x200"),
+            GetTrayDisplayTooltipText());
+
+  // mirroring
+  Shell::GetInstance()->display_manager()->SetSoftwareMirroring(true);
+  UpdateDisplay("400x400,200x200");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  expected = l10n_util::GetStringUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING_NO_INTERNAL);
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "400x400",
+                           GetMirroredDisplayName(), "200x200"),
+            GetTrayDisplayTooltipText());
+}
+
+TEST_F(TrayDisplayTest, InternalDisplay) {
+  UpdateDisplay("400x400");
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  gfx::Display::SetInternalDisplayId(display_manager->first_display_id());
+
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_FALSE(IsDisplayVisibleInTray());
+
+  // Extended
+  UpdateDisplay("400x400,200x200");
+  string16 expected = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetSecondDisplayName());
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "400x400",
+                           GetSecondDisplayName(), "200x200"),
+            GetTrayDisplayTooltipText());
+
+  // Mirroring
+  display_manager->SetSoftwareMirroring(true);
+  UpdateDisplay("400x400,200x200");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+
+  expected = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName());
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "400x400",
+                           GetMirroredDisplayName(), "200x200"),
+            GetTrayDisplayTooltipText());
+
+  // TODO(mukai): add test case for docked mode here.
+}
+
+TEST_F(TrayDisplayTest, InternalDisplayResized) {
+  UpdateDisplay("400x400@1.5");
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  gfx::Display::SetInternalDisplayId(display_manager->first_display_id());
+
+  // Shows the tray_display even though there's a single-display.
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  base::string16 internal_info = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_SINGLE_DISPLAY,
+      GetFirstDisplayName(), UTF8ToUTF16("600x600"));
+  EXPECT_EQ(internal_info, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(base::string16(), GetFirstDisplayName(), "600x600",
+                           base::string16(), std::string()),
+            GetTrayDisplayTooltipText());
+
+  // Extended
+  UpdateDisplay("400x400@1.5,200x200");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  base::string16 expected = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetSecondDisplayName());
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "600x600",
+                           GetSecondDisplayName(), "200x200"),
+            GetTrayDisplayTooltipText());
+
+  // Mirroring
+  display_manager->SetSoftwareMirroring(true);
+  UpdateDisplay("400x400@1.5,200x200");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  expected = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName());
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "600x600",
+                           GetMirroredDisplayName(), "200x200"),
+            GetTrayDisplayTooltipText());
+}
+
+TEST_F(TrayDisplayTest, ExternalDisplayResized) {
+  UpdateDisplay("400x400");
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  gfx::Display::SetInternalDisplayId(display_manager->first_display_id());
+
+  // Shows the tray_display even though there's a single-display.
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_FALSE(IsDisplayVisibleInTray());
+
+  // Extended
+  UpdateDisplay("400x400,200x200@1.5");
+  const gfx::Display& secondary_display = ScreenAsh::GetSecondaryDisplay();
+  base::string16 secondary_annotation = UTF8ToUTF16(
+      " (" + secondary_display.size().ToString() + ")");
+
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  base::string16 expected = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED,
+      GetSecondDisplayName() + secondary_annotation);
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "400x400",
+                           GetSecondDisplayName(), "300x300"),
+            GetTrayDisplayTooltipText());
+
+  // Mirroring: in mirroring, it's not possible to lookup the DisplayInfo.
+  display_manager->SetSoftwareMirroring(true);
+  UpdateDisplay("400x400,200x200@1.5");
+  base::string16 mirror_name =
+      GetMirroredDisplayName() + UTF8ToUTF16(" (300x300)");
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+  expected = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, mirror_name);
+  EXPECT_EQ(expected, GetTrayDisplayText());
+  EXPECT_EQ(GetTooltipText(expected, GetFirstDisplayName(), "400x400",
+                           GetMirroredDisplayName(), "300x300"),
+            GetTrayDisplayTooltipText());
+}
+
+TEST_F(TrayDisplayTest, OverscanDisplay) {
+  UpdateDisplay("400x400,300x300/o");
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  gfx::Display::SetInternalDisplayId(display_manager->first_display_id());
+
+  tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+  EXPECT_TRUE(IsDisplayVisibleInTray());
+
+  // /o creates the default overscan, and if overscan is set, the annotation
+  // should be the size.
+  base::string16 size_annotation = UTF8ToUTF16(" (286x286)");
+  base::string16 overscan = l10n_util::GetStringUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION_OVERSCAN);
+  base::string16 headline = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED,
+      GetSecondDisplayName() + size_annotation);
+  std::string second_data = l10n_util::GetStringFUTF8(
+      IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
+      UTF8ToUTF16("286x286"), overscan);
+  EXPECT_EQ(GetTooltipText(headline, GetFirstDisplayName(), "400x400",
+                           GetSecondDisplayName(), second_data),
+            GetTrayDisplayTooltipText());
+
+  // reset the overscan.
+  display_manager->SetOverscanInsets(
+      ScreenAsh::GetSecondaryDisplay().id(), gfx::Insets());
+  base::string16 overscan_annotation =
+      UTF8ToUTF16(" (") + overscan + UTF8ToUTF16(")");
+  headline = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED,
+      GetSecondDisplayName() + overscan_annotation);
+  second_data = l10n_util::GetStringFUTF8(
+      IDS_ASH_STATUS_TRAY_DISPLAY_ANNOTATION,
+      UTF8ToUTF16("300x300"), overscan);
+  EXPECT_EQ(GetTooltipText(headline, GetFirstDisplayName(), "400x400",
+                           GetSecondDisplayName(), second_data),
+            GetTrayDisplayTooltipText());
+}
+
+TEST_F(TrayDisplayTest, DisplayNotifications) {
+  test::TestSystemTrayDelegate* tray_delegate =
+      static_cast<test::TestSystemTrayDelegate*>(
+          Shell::GetInstance()->system_tray_delegate());
+  tray_delegate->set_should_show_display_notification(true);
+
+  UpdateDisplay("400x400");
+  DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+  gfx::Display::SetInternalDisplayId(display_manager->first_display_id());
+  EXPECT_TRUE(GetDisplayNotificationText().empty());
+
+  // rotation.
+  UpdateDisplay("400x400/r");
+  base::string16 rotation_message = l10n_util::GetStringFUTF16(
+      IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED, GetFirstDisplayName());
+  EXPECT_EQ(rotation_message, GetDisplayNotificationText());
+
+  CloseNotification();
+  UpdateDisplay("400x400");
+  EXPECT_EQ(rotation_message, GetDisplayNotificationText());
+
+  // UI-scale
+  CloseNotification();
+  UpdateDisplay("400x400@1.5");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
+          GetFirstDisplayName(), UTF8ToUTF16("600x600")),
+      GetDisplayNotificationText());
+
+  // UI-scale to 1.0
+  CloseNotification();
+  UpdateDisplay("400x400");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
+          GetFirstDisplayName(), UTF8ToUTF16("400x400")),
+      GetDisplayNotificationText());
+
+  // No-update
+  CloseNotification();
+  UpdateDisplay("400x400");
+  EXPECT_TRUE(GetDisplayNotificationText().empty());
+
+  // Extended.
+  CloseNotification();
+  UpdateDisplay("400x400,200x200");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetSecondDisplayName()),
+      GetDisplayNotificationText());
+
+  // Mirroring.
+  CloseNotification();
+  display_manager->SetSoftwareMirroring(true);
+  UpdateDisplay("400x400,200x200");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING, GetMirroredDisplayName()),
+      GetDisplayNotificationText());
+
+  // Back to extended.
+  CloseNotification();
+  display_manager->SetSoftwareMirroring(false);
+  UpdateDisplay("400x400,200x200");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED, GetSecondDisplayName()),
+      GetDisplayNotificationText());
+
+  // Resize the first display.
+  UpdateDisplay("400x400@1.5,200x200");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
+          GetFirstDisplayName(), UTF8ToUTF16("600x600")),
+      GetDisplayNotificationText());
+
+  // rotate the second.
+  UpdateDisplay("400x400@1.5,200x200/r");
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_ASH_STATUS_TRAY_DISPLAY_ROTATED, GetSecondDisplayName()),
+      GetDisplayNotificationText());
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc
index b044897..68789fc 100644
--- a/ash/system/date/date_view.cc
+++ b/ash/system/date/date_view.cc
@@ -10,7 +10,7 @@
 #include "ash/system/tray/tray_utils.h"
 #include "base/i18n/time_formatting.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "grit/ash_strings.h"
 #include "third_party/icu/public/i18n/unicode/datefmt.h"
 #include "third_party/icu/public/i18n/unicode/dtptngen.h"
diff --git a/ash/system/date/date_view.h b/ash/system/date/date_view.h
index 6809461..98bc45f 100644
--- a/ash/system/date/date_view.h
+++ b/ash/system/date/date_view.h
@@ -8,7 +8,7 @@
 #include "ash/system/date/tray_date.h"
 #include "ash/system/tray/actionable_view.h"
 #include "base/i18n/time_formatting.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/views/view.h"
 
 namespace views {
diff --git a/ash/system/date/tray_date.cc b/ash/system/date/tray_date.cc
index 0386b3b..0e245a8 100644
--- a/ash/system/date/tray_date.cc
+++ b/ash/system/date/tray_date.cc
@@ -17,8 +17,8 @@
 #include "base/i18n/time_formatting.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "third_party/icu/public/i18n/unicode/datefmt.h"
diff --git a/ash/system/drive/tray_drive.h b/ash/system/drive/tray_drive.h
index 9680c99..6550fde 100644
--- a/ash/system/drive/tray_drive.h
+++ b/ash/system/drive/tray_drive.h
@@ -7,7 +7,7 @@
 
 #include "ash/system/drive/drive_observer.h"
 #include "ash/system/tray/tray_image_item.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 
 namespace views {
 class Label;
diff --git a/ash/system/monitor/tray_monitor.h b/ash/system/monitor/tray_monitor.h
index 4f496c4..cd5734f 100644
--- a/ash/system/monitor/tray_monitor.h
+++ b/ash/system/monitor/tray_monitor.h
@@ -9,7 +9,7 @@
 
 #include "ash/system/tray/system_tray_item.h"
 #include "base/process.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 
 namespace views {
 class Label;
diff --git a/ash/system/session_length_limit/tray_session_length_limit.h b/ash/system/session_length_limit/tray_session_length_limit.h
index 88d601d..1df4bb0 100644
--- a/ash/system/session_length_limit/tray_session_length_limit.h
+++ b/ash/system/session_length_limit/tray_session_length_limit.h
@@ -10,8 +10,8 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 
 namespace ash {
 namespace internal {
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 1430bd1..471e885 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -35,7 +35,7 @@
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
   params.delegate = status_area_widget_delegate_;
   params.parent = status_container;
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   Init(params);
   set_focus_on_creation(false);
   SetContentsView(status_area_widget_delegate_);
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index eb183ec..a766c2e 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -31,7 +31,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "grit/ash_strings.h"
 #include "ui/aura/root_window.h"
 #include "ui/base/events/event_constants.h"
@@ -58,6 +58,7 @@
 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
 #include "ash/system/chromeos/settings/tray_settings.h"
 #include "ash/system/chromeos/tray_display.h"
+#include "ui/message_center/message_center.h"
 #endif
 
 using views::TrayBubbleView;
@@ -120,7 +121,8 @@
     : internal::TrayBackgroundView(status_area_widget),
       items_(),
       default_bubble_height_(0),
-      hide_notifications_(false) {
+      hide_notifications_(false),
+      tray_accessibility_(NULL) {
   SetContentsBackground();
 }
 
@@ -164,7 +166,8 @@
   tray_accessibility_ = new internal::TrayAccessibility(this);
   AddTrayItem(tray_accessibility_);
 #if defined(OS_CHROMEOS)
-  AddTrayItem(new internal::TrayPower(this));
+  AddTrayItem(
+      new internal::TrayPower(this, message_center::MessageCenter::Get()));
 #endif
 #if defined(OS_CHROMEOS)
   AddTrayItem(new internal::TrayNetwork(this));
@@ -221,6 +224,10 @@
   NOTIMPLEMENTED();
 }
 
+const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const {
+  return items_.get();
+}
+
 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
   ShowDefaultViewWithOffset(creation_type,
                             TrayBubbleView::InitParams::kArrowDefaultOffset);
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index 1a6b6fa..1dc4d34 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -70,6 +70,9 @@
   // Removes an existing tray item.
   void RemoveTrayItem(SystemTrayItem* item);
 
+  // Returns all tray items that has been added to system tray.
+  const std::vector<SystemTrayItem*>& GetTrayItems() const;
+
   // Shows the default view of all items.
   void ShowDefaultView(BubbleCreationType creation_type);
 
diff --git a/ash/system/tray/system_tray_bubble.h b/ash/system/tray/system_tray_bubble.h
index 73edaac..61b1543 100644
--- a/ash/system/tray/system_tray_bubble.h
+++ b/ash/system/tray/system_tray_bubble.h
@@ -8,7 +8,7 @@
 #include "ash/system/user/login_status.h"
 #include "base/base_export.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/views/bubble/tray_bubble_view.h"
 
 #include <vector>
diff --git a/ash/system/tray/system_tray_delegate.h b/ash/system/tray/system_tray_delegate.h
index 847ff8c..ee60831 100644
--- a/ash/system/tray/system_tray_delegate.h
+++ b/ash/system/tray/system_tray_delegate.h
@@ -139,6 +139,9 @@
   // Returns the email of user that manages current locally managed user.
   virtual const std::string GetLocallyManagedUserManager() const = 0;
 
+  // Returns the email of user that manages current locally managed user.
+  virtual const base::string16 GetLocallyManagedUserManagerName() const = 0;
+
   // Returns notification for locally managed users.
   virtual const base::string16 GetLocallyManagedUserMessage() const = 0;
 
@@ -164,6 +167,10 @@
   // Shows settings related to multiple displays.
   virtual void ShowDisplaySettings() = 0;
 
+  // Returns true if the notification for the display configuration change
+  // should appear.
+  virtual bool ShouldShowDisplayNotification() = 0;
+
   // Shows settings related to Google Drive.
   virtual void ShowDriveSettings() = 0;
 
diff --git a/ash/system/tray/system_tray_unittest.cc b/ash/system/tray/system_tray_unittest.cc
index 936b29e..6d0b4d9 100644
--- a/ash/system/tray/system_tray_unittest.cc
+++ b/ash/system/tray/system_tray_unittest.cc
@@ -156,6 +156,13 @@
   tray->AddTrayItem(test_item);
   tray->AddTrayItem(detailed_item);
 
+  // Check items have been added
+  const std::vector<SystemTrayItem*>& items = tray->GetTrayItems();
+  ASSERT_TRUE(
+      std::find(items.begin(), items.end(), test_item) != items.end());
+  ASSERT_TRUE(
+      std::find(items.begin(), items.end(), detailed_item) != items.end());
+
   // Ensure the tray views are created.
   ASSERT_TRUE(test_item->tray_view() != NULL);
   ASSERT_TRUE(detailed_item->tray_view() != NULL);
diff --git a/ash/system/tray/test_system_tray_delegate.cc b/ash/system/tray/test_system_tray_delegate.cc
index c2eb2df..17742ba 100644
--- a/ash/system/tray/test_system_tray_delegate.cc
+++ b/ash/system/tray/test_system_tray_delegate.cc
@@ -10,7 +10,7 @@
 #include "ash/shell.h"
 #include "ash/volume_control_delegate.h"
 #include "base/message_loop.h"
-#include "base/time.h"
+#include "base/time/time.h"
 
 namespace ash {
 namespace test {
@@ -53,6 +53,7 @@
 TestSystemTrayDelegate::TestSystemTrayDelegate()
     : bluetooth_enabled_(true),
       caps_lock_enabled_(false),
+      should_show_display_notification_(false),
       volume_control_delegate_(new TestVolumeControlDelegate) {
 }
 
@@ -104,6 +105,11 @@
   return std::string();
 }
 
+const base::string16 TestSystemTrayDelegate::GetLocallyManagedUserManagerName()
+    const {
+  return string16();
+}
+
 const base::string16 TestSystemTrayDelegate::GetLocallyManagedUserMessage()
     const {
   return string16();
@@ -133,6 +139,10 @@
 void TestSystemTrayDelegate::ShowDisplaySettings() {
 }
 
+bool TestSystemTrayDelegate::ShouldShowDisplayNotification() {
+  return should_show_display_notification_;
+}
+
 void TestSystemTrayDelegate::ShowDriveSettings() {
 }
 
diff --git a/ash/system/tray/test_system_tray_delegate.h b/ash/system/tray/test_system_tray_delegate.h
index 27ee734..56cb078 100644
--- a/ash/system/tray/test_system_tray_delegate.h
+++ b/ash/system/tray/test_system_tray_delegate.h
@@ -32,6 +32,8 @@
   virtual const std::string GetEnterpriseDomain() const OVERRIDE;
   virtual const base::string16 GetEnterpriseMessage() const OVERRIDE;
   virtual const std::string GetLocallyManagedUserManager() const OVERRIDE;
+  virtual const base::string16 GetLocallyManagedUserManagerName() const
+      OVERRIDE;
   virtual const base::string16 GetLocallyManagedUserMessage() const OVERRIDE;
   virtual bool SystemShouldUpgrade() const OVERRIDE;
   virtual base::HourClockType GetHourClockType() const OVERRIDE;
@@ -40,6 +42,7 @@
   virtual void ShowNetworkSettings(const std::string& service_path) OVERRIDE;
   virtual void ShowBluetoothSettings() OVERRIDE;
   virtual void ShowDisplaySettings() OVERRIDE;
+  virtual bool ShouldShowDisplayNotification() OVERRIDE;
   virtual void ShowDriveSettings() OVERRIDE;
   virtual void ShowIMESettings() OVERRIDE;
   virtual void ShowHelp() OVERRIDE;
@@ -93,9 +96,14 @@
       const base::TimeDelta& delta) const OVERRIDE;
   virtual void MaybeSpeak(const std::string& utterance) const OVERRIDE;
 
+  void set_should_show_display_notification(bool should_show) {
+    should_show_display_notification_ = should_show;
+  }
+
  private:
   bool bluetooth_enabled_;
   bool caps_lock_enabled_;
+  bool should_show_display_notification_;
   scoped_ptr<VolumeControlDelegate> volume_control_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSystemTrayDelegate);
diff --git a/ash/system/tray/tray_item_view.cc b/ash/system/tray/tray_item_view.cc
index 632dcbf..f95ceb9 100644
--- a/ash/system/tray/tray_item_view.cc
+++ b/ash/system/tray/tray_item_view.cc
@@ -18,6 +18,9 @@
 const int kTrayIconHeight = 29;
 const int kTrayIconWidth = 29;
 const int kTrayItemAnimationDurationMS = 200;
+
+// Animations can be disabled for testing.
+bool animations_enabled = true;
 }
 
 namespace ash {
@@ -35,6 +38,11 @@
 
 TrayItemView::~TrayItemView() {}
 
+// static
+void TrayItemView::DisableAnimationsForTest() {
+  animations_enabled = false;
+}
+
 void TrayItemView::CreateLabel() {
   label_ = new views::Label;
   AddChildView(label_);
@@ -46,7 +54,7 @@
 }
 
 void TrayItemView::SetVisible(bool set_visible) {
-  if (!GetWidget()) {
+  if (!GetWidget() || !animations_enabled) {
     views::View::SetVisible(set_visible);
     return;
   }
diff --git a/ash/system/tray/tray_item_view.h b/ash/system/tray/tray_item_view.h
index ca260cf..e0d8ec4 100644
--- a/ash/system/tray/tray_item_view.h
+++ b/ash/system/tray/tray_item_view.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_TRAY_TRAY_ITEM_VIEW_H_
 #define ASH_SYSTEM_TRAY_TRAY_ITEM_VIEW_H_
 
+#include "ash/ash_export.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/views/view.h"
 
@@ -26,12 +27,14 @@
 // Base-class for items in the tray. It makes sure the widget is updated
 // correctly when the visibility/size of the tray item changes. It also adds
 // animation when showing/hiding the item in the tray.
-class TrayItemView : public views::View,
-                     public ui::AnimationDelegate {
+class ASH_EXPORT TrayItemView : public views::View,
+                                public ui::AnimationDelegate {
  public:
   explicit TrayItemView(SystemTrayItem* owner);
   virtual ~TrayItemView();
 
+  static void DisableAnimationsForTest();
+
   // Convenience function for creating a child Label or ImageView.
   void CreateLabel();
   void CreateImageView();
diff --git a/ash/system/tray/tray_notification_view.h b/ash/system/tray/tray_notification_view.h
index 403beca..314b8aa 100644
--- a/ash/system/tray/tray_notification_view.h
+++ b/ash/system/tray/tray_notification_view.h
@@ -3,9 +3,9 @@
 // found in the LICENSE file.
 
 #ifndef ASH_SYSTEM_TRAY_TRAY_NOTIFICATION_VIEW_H_
-#define ASH_SYSTEM_TRAY_TRAY_NOTIFICATION_VIEWS_H_
+#define ASH_SYSTEM_TRAY_TRAY_NOTIFICATION_VIEW_H_
 
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/slide_out_view.h"
 
@@ -95,4 +95,4 @@
 }  // namespace internal
 }  // namespace ash
 
-#endif  // ASH_SYSTEM_TRAY_TRAY_NOTIFICATION_VIEWS_H_
+#endif  // ASH_SYSTEM_TRAY_TRAY_NOTIFICATION_VIEW_H_
diff --git a/ash/system/tray_update.cc b/ash/system/tray_update.cc
index e998cc7..e01dd97 100644
--- a/ash/system/tray_update.cc
+++ b/ash/system/tray_update.cc
@@ -14,8 +14,8 @@
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "ui/aura/window.h"
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc
index 1566371..10c07bd 100644
--- a/ash/system/user/tray_user.cc
+++ b/ash/system/user/tray_user.cc
@@ -694,6 +694,7 @@
       // between the two.
       remaining_width -= kTrayPopupPaddingBetweenItems;
     }
+    user_card_area.set_width(remaining_width);
     user_card_view_->SetBoundsRect(user_card_area);
     logout_button_->SetBoundsRect(logout_area);
   } else if (user_card_view_) {
@@ -809,22 +810,28 @@
   views::Label* username = NULL;
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
   if (!multiprofile_index_) {
-    username = new views::Label(
+    base::string16 user_name_string =
         login == ash::user::LOGGED_IN_GUEST ?
             bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_GUEST_LABEL) :
-            delegate->GetUserDisplayName(multiprofile_index_));
-    username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+            delegate->GetUserDisplayName(multiprofile_index_);
+    if (!user_name_string.empty()) {
+      username = new views::Label(user_name_string);
+      username->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    }
   }
 
   views::Label* additional = NULL;
   if (login != ash::user::LOGGED_IN_GUEST) {
-    additional = new views::Label();
-    additional->SetText(login == ash::user::LOGGED_IN_LOCALLY_MANAGED ?
-        bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) :
-        UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index_)));
-
-    additional->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont));
-    additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    base::string16 user_email_string =
+        login == ash::user::LOGGED_IN_LOCALLY_MANAGED ?
+            bundle.GetLocalizedString(
+                IDS_ASH_STATUS_TRAY_LOCALLY_MANAGED_LABEL) :
+            UTF8ToUTF16(delegate->GetUserEmail(multiprofile_index_));
+    if (!user_email_string.empty()) {
+      additional = new views::Label(user_email_string);
+      additional->SetFont(bundle.GetFont(ui::ResourceBundle::SmallFont));
+      additional->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    }
   }
 
   // Adjust text properties dependent on if it is an active or inactive user.
@@ -910,7 +917,7 @@
   params.context = this->GetWidget()->GetNativeWindow();
   params.accept_events = true;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   add_menu_option_->Init(params);
   add_menu_option_->SetOpacity(0xFF);
   add_menu_option_->GetNativeWindow()->set_owned_by_parent(false);
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index 9579442..7569509 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -198,18 +198,17 @@
 
 // Public methods.
 
-bool WebNotificationTray::ShowMessageCenter() {
+bool WebNotificationTray::ShowMessageCenterInternal(bool show_settings) {
   if (!ShouldShowMessageCenter())
     return false;
 
   should_block_shelf_auto_hide_ = true;
   message_center::MessageCenterBubble* message_center_bubble =
-      new message_center::MessageCenterBubble(message_center());
+      new message_center::MessageCenterBubble(message_center(),
+                                              message_center_tray_.get());
 
-  // TODO(mukai): move this to WebNotificationBubbleWrapper if it's safe
-  // to set the height of the popup.
   int max_height = 0;
-  aura::Window* status_area_window = status_area_widget()->GetNativeWindow();
+  aura::Window* status_area_window = status_area_widget()->GetNativeView();
   switch (GetShelfLayoutManager()->GetAlignment()) {
     case SHELF_ALIGNMENT_BOTTOM: {
       gfx::Rect shelf_bounds = GetShelfLayoutManager()->GetIdealBounds();
@@ -232,8 +231,10 @@
     default:
       NOTREACHED();
   }
-  max_height = std::max(0, max_height - kTraySpacing);
-  message_center_bubble->SetMaxHeight(max_height);
+
+  message_center_bubble->SetMaxHeight(std::max(0, max_height - kTraySpacing));
+  if (show_settings)
+    message_center_bubble->SetSettingsVisible();
   message_center_bubble_.reset(
       new internal::WebNotificationBubbleWrapper(this, message_center_bubble));
 
@@ -243,6 +244,10 @@
   return true;
 }
 
+bool WebNotificationTray::ShowMessageCenter() {
+  return ShowMessageCenterInternal(false /* show_settings */);
+}
+
 void WebNotificationTray::HideMessageCenter() {
   if (!message_center_bubble())
     return;
@@ -274,7 +279,8 @@
         ash::Shell::GetContainer(
             GetWidget()->GetNativeView()->GetRootWindow(),
             internal::kShellWindowId_StatusContainer),
-        message_center()));
+        message_center(),
+        message_center_tray_.get()));
   } else {
     message_center::MessagePopupBubble* popup_bubble =
         new message_center::MessagePopupBubble(message_center());
@@ -454,6 +460,15 @@
   HideBubbleWithView(bubble_view);
 }
 
+bool WebNotificationTray::ShowNotifierSettings() {
+  if (message_center_bubble()) {
+    static_cast<message_center::MessageCenterBubble*>(
+        message_center_bubble()->bubble())->SetSettingsVisible();
+    return true;
+  }
+  return ShowMessageCenterInternal(true /* show_settings */);
+}
+
 void WebNotificationTray::ButtonPressed(views::Button* sender,
                                         const ui::Event& event) {
   DCHECK_EQ(button_, sender);
@@ -490,9 +505,10 @@
 }
 
 bool WebNotificationTray::ClickedOutsideBubble() {
-  // Only hide the message center.
+  // Only hide the message center
   if (!message_center_bubble())
     return false;
+
   message_center_tray_->HideMessageCenterBubble();
   return true;
 }
diff --git a/ash/system/web_notification/web_notification_tray.h b/ash/system/web_notification/web_notification_tray.h
index df7a663..999657e 100644
--- a/ash/system/web_notification/web_notification_tray.h
+++ b/ash/system/web_notification/web_notification_tray.h
@@ -106,6 +106,7 @@
   virtual bool ShowPopups() OVERRIDE;
   virtual void UpdatePopups() OVERRIDE;
   virtual void HidePopups() OVERRIDE;
+  virtual bool ShowNotifierSettings() OVERRIDE;
 
   message_center::MessageCenter* message_center();
 
@@ -118,6 +119,11 @@
 
   void UpdateTrayContent();
 
+  // The actual process to show the message center. Set |show_settings| to true
+  // if the message center should be initialized with the settings visible.
+  // Returns true if the center is successfully created.
+  bool ShowMessageCenterInternal(bool show_settings);
+
   // Queries login status and the status area widget to determine visibility of
   // the message center.
   bool ShouldShowMessageCenter();
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 8e93f27..95b8d99 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -30,6 +30,10 @@
 #include "ui/gfx/point.h"
 #include "ui/gfx/screen.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/system/chromeos/tray_display.h"
+#endif
+
 #if defined(OS_WIN)
 #include "ash/test/test_metro_viewer_process_host.h"
 #include "base/test/test_process_killer_win.h"
@@ -124,8 +128,7 @@
     ipc_thread_->StartWithOptions(options);
 
     metro_viewer_host_.reset(
-        new TestMetroViewerProcessHost("viewer",
-                                       ipc_thread_->message_loop_proxy()));
+        new TestMetroViewerProcessHost(ipc_thread_->message_loop_proxy()));
     CHECK(metro_viewer_host_->LaunchViewerAndWaitForConnection(
         win8::test::kDefaultTestAppUserModelId));
     aura::RemoteRootWindowHostWin* root_window_host =
@@ -189,6 +192,15 @@
 #endif
 }
 
+// static
+bool AshTestBase::SupportsHostWindowResize() {
+#if defined(OS_WIN)
+  return base::win::GetVersion() < base::win::VERSION_WIN8;
+#else
+  return true;
+#endif
+}
+
 void AshTestBase::UpdateDisplay(const std::string& display_specs) {
   DisplayManagerTestApi display_manager_test_api(
       Shell::GetInstance()->display_manager());
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index 53c2e50..69564b0 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -99,6 +99,10 @@
   // or false otherwise (e.g. win8 bot).
   static bool SupportsMultipleDisplays();
 
+  // True if the running environment supports host window resize,
+  // or false otherwise (e.g. win8 bot).
+  static bool SupportsHostWindowResize();
+
   void RunAllPendingInMessageLoop();
 
   // Utility methods to emulate user logged in or not, session started or not
diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc
index 17a3cc7..84d28c5 100644
--- a/ash/test/ash_test_helper.cc
+++ b/ash/test/ash_test_helper.cc
@@ -8,6 +8,7 @@
 #include "ash/shell.h"
 #include "ash/test/display_manager_test_api.h"
 #include "ash/test/shell_test_api.h"
+#include "ash/test/test_session_state_delegate.h"
 #include "ash/test/test_shell_delegate.h"
 #include "base/run_loop.h"
 #include "ui/aura/env.h"
@@ -17,7 +18,6 @@
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/power/power_manager_handler.h"
 #endif
 
 #if defined(USE_X11)
@@ -58,15 +58,17 @@
     // created in AshTestBase tests.
     chromeos::CrasAudioHandler::InitializeForTesting();
   }
-  chromeos::PowerManagerHandler::Initialize();
 #endif
 
   ash::Shell::CreateInstance(test_shell_delegate_);
   Shell* shell = Shell::GetInstance();
+  test_shell_delegate_->test_session_state_delegate()->
+      SetActiveUserSessionStarted(true);
+  test_shell_delegate_->test_session_state_delegate()->SetHasActiveUser(true);
+
   test::DisplayManagerTestApi(shell->display_manager()).
       DisableChangeDisplayUponHostResize();
   ShellTestApi(shell).DisableOutputConfiguratorAnimation();
-
 }
 
 void AshTestHelper::TearDown() {
@@ -79,7 +81,6 @@
 #if defined(OS_CHROMEOS)
   if (ash::switches::UseNewAudioHandler())
     chromeos::CrasAudioHandler::Shutdown();
-  chromeos::PowerManagerHandler::Shutdown();
 #endif
 
   aura::Env::DeleteInstance();
diff --git a/ash/test/test_metro_viewer_process_host.cc b/ash/test/test_metro_viewer_process_host.cc
index 3819d35..a788da9 100644
--- a/ash/test/test_metro_viewer_process_host.cc
+++ b/ash/test/test_metro_viewer_process_host.cc
@@ -4,30 +4,18 @@
 
 #include "ash/test/test_metro_viewer_process_host.h"
 
-#include <shellapi.h>
-#include <shlobj.h>
+#include <windef.h>
 
-#include "base/command_line.h"
-#include "base/file_util.h"
-#include "base/files/file_path.h"
-#include "base/strings/string16.h"
-#include "base/time.h"
-#include "base/win/scoped_com_initializer.h"
-#include "base/win/scoped_comptr.h"
-#include "ipc/ipc_channel_proxy.h"
-#include "ipc/ipc_message_macros.h"
+#include "base/logging.h"
 #include "ui/aura/remote_root_window_host_win.h"
-#include "ui/metro_viewer/metro_viewer_messages.h"
 #include "ui/surface/accelerated_surface_win.h"
 
 namespace ash {
 namespace test {
 
 TestMetroViewerProcessHost::TestMetroViewerProcessHost(
-    const std::string& ipc_channel_name,
     base::SingleThreadTaskRunner* ipc_task_runner)
-        : MetroViewerProcessHost(ipc_channel_name, ipc_task_runner),
-          closed_unexpectedly_(false) {
+        : MetroViewerProcessHost(ipc_task_runner), closed_unexpectedly_(false) {
 }
 
 TestMetroViewerProcessHost::~TestMetroViewerProcessHost() {
@@ -43,7 +31,7 @@
   DLOG(INFO) << __FUNCTION__ << ", target_surface = " << target_surface;
   HWND hwnd = reinterpret_cast<HWND>(target_surface);
 
-  backing_surface.reset(new AcceleratedSurface(hwnd));
+  backing_surface_.reset(new AcceleratedSurface(hwnd));
 
   scoped_refptr<AcceleratedPresenter> any_window =
       AcceleratedPresenter::GetForWindow(NULL);
diff --git a/ash/test/test_metro_viewer_process_host.h b/ash/test/test_metro_viewer_process_host.h
index 63ca815..e6d0a64 100644
--- a/ash/test/test_metro_viewer_process_host.h
+++ b/ash/test/test_metro_viewer_process_host.h
@@ -5,8 +5,7 @@
 #ifndef ASH_TEST_TEST_METRO_VIEWER_PROCESS_HOST_H_
 #define ASH_TEST_TEST_METRO_VIEWER_PROCESS_HOST_H_
 
-#include <string>
-
+#include "base/memory/scoped_ptr.h"
 #include "win8/viewer/metro_viewer_process_host.h"
 
 class AcceleratedSurface;
@@ -16,8 +15,7 @@
 
 class TestMetroViewerProcessHost : public win8::MetroViewerProcessHost {
  public:
-  TestMetroViewerProcessHost(const std::string& ipc_channel_name,
-                             base::SingleThreadTaskRunner* ipc_task_runner);
+  TestMetroViewerProcessHost(base::SingleThreadTaskRunner* ipc_task_runner);
   virtual ~TestMetroViewerProcessHost();
 
   bool closed_unexpectedly() { return closed_unexpectedly_; }
@@ -29,7 +27,7 @@
   virtual void OnOpenURL(const string16& url) OVERRIDE;
   virtual void OnHandleSearchRequest(const string16& search_string) OVERRIDE;
 
-  scoped_ptr<AcceleratedSurface> backing_surface;
+  scoped_ptr<AcceleratedSurface> backing_surface_;
 
   bool closed_unexpectedly_;
 
diff --git a/ash/test/test_session_state_delegate.cc b/ash/test/test_session_state_delegate.cc
index a8ee01e..7af9e3c 100644
--- a/ash/test/test_session_state_delegate.cc
+++ b/ash/test/test_session_state_delegate.cc
@@ -4,6 +4,8 @@
 
 #include "ash/test/test_session_state_delegate.h"
 
+#include "ash/shell.h"
+#include "ash/system/user/login_status.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 
@@ -11,8 +13,8 @@
 namespace test {
 
 TestSessionStateDelegate::TestSessionStateDelegate()
-    : has_active_user_(true),
-      active_user_session_started_(true),
+    : has_active_user_(false),
+      active_user_session_started_(false),
       can_lock_screen_(true),
       screen_locked_(false),
       logged_in_users_(1) {
@@ -55,13 +57,19 @@
   has_active_user_ = has_active_user;
   if (!has_active_user)
     active_user_session_started_ = false;
+  else
+    Shell::GetInstance()->ShowLauncher();
 }
 
 void TestSessionStateDelegate::SetActiveUserSessionStarted(
     bool active_user_session_started) {
   active_user_session_started_ = active_user_session_started;
-  if (active_user_session_started)
+  if (active_user_session_started) {
     has_active_user_ = true;
+    Shell::GetInstance()->CreateLauncher();
+    Shell::GetInstance()->UpdateAfterLoginStatusChange(
+        user::LOGGED_IN_USER);
+  }
 }
 
 void TestSessionStateDelegate::SetCanLockScreen(bool can_lock_screen) {
@@ -69,12 +77,12 @@
 }
 
 const base::string16 TestSessionStateDelegate::GetUserDisplayName(
-    ash::MultiProfileIndex index) const {
+    MultiProfileIndex index) const {
   return UTF8ToUTF16("Über tray Über tray Über tray Über tray");
 }
 
 const std::string TestSessionStateDelegate::GetUserEmail(
-    ash::MultiProfileIndex index) const {
+    MultiProfileIndex index) const {
   switch (index) {
     case 0: return "first@tray";
     case 1: return "second@tray";
@@ -84,7 +92,7 @@
 }
 
 const gfx::ImageSkia& TestSessionStateDelegate::GetUserImage(
-    ash::MultiProfileIndex index) const {
+    MultiProfileIndex index) const {
   return null_image_;
 }
 
@@ -96,11 +104,11 @@
 }
 
 void TestSessionStateDelegate::AddSessionStateObserver(
-    ash::SessionStateObserver* observer) {
+    SessionStateObserver* observer) {
 }
 
 void TestSessionStateDelegate::RemoveSessionStateObserver(
-    ash::SessionStateObserver* observer) {
+    SessionStateObserver* observer) {
 }
 
 }  // namespace test
diff --git a/ash/test/test_session_state_delegate.h b/ash/test/test_session_state_delegate.h
index 17062d5..f9466ff 100644
--- a/ash/test/test_session_state_delegate.h
+++ b/ash/test/test_session_state_delegate.h
@@ -42,6 +42,8 @@
   virtual void RemoveSessionStateObserver(
       ash::SessionStateObserver* observer) OVERRIDE;
 
+  // TODO(oshima): Use state machine instead of using boolean variables.
+
   // Updates the internal state that indicates whether a session is in progress
   // and there is an active user. If |has_active_user| is |false|,
   // |active_user_session_started_| is reset to |false| as well (see below for
diff --git a/ash/test/test_suite_init.mm b/ash/test/test_suite_init.mm
index 1564c38..c32df2b 100644
--- a/ash/test/test_suite_init.mm
+++ b/ash/test/test_suite_init.mm
@@ -8,7 +8,6 @@
 
 #include "base/files/file_path.h"
 #include "base/mac/bundle_locations.h"
-#include "base/memory/scoped_nsobject.h"
 #include "base/path_service.h"
 
 namespace ash {
diff --git a/ash/tooltips/tooltip_controller_unittest.cc b/ash/tooltips/tooltip_controller_unittest.cc
index ff894c8..8cf0daf 100644
--- a/ash/tooltips/tooltip_controller_unittest.cc
+++ b/ash/tooltips/tooltip_controller_unittest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/display/display_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/touch/touch_hud_debug.cc b/ash/touch/touch_hud_debug.cc
new file mode 100644
index 0000000..5b7c120
--- /dev/null
+++ b/ash/touch/touch_hud_debug.cc
@@ -0,0 +1,491 @@
+// 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/touch/touch_hud_debug.h"
+
+#include "ash/display/display_manager.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ash/wm/property_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "ui/aura/root_window.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "ui/base/events/event.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/transform.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/widget/widget.h"
+
+#if defined(USE_X11)
+#include <X11/extensions/XInput2.h>
+#include <X11/Xlib.h>
+
+#include "ui/base/x/device_data_manager.h"
+#endif
+
+namespace ash {
+namespace internal {
+
+const int kPointRadius = 20;
+const SkColor kColors[] = {
+  SK_ColorYELLOW,
+  SK_ColorGREEN,
+  SK_ColorRED,
+  SK_ColorBLUE,
+  SK_ColorGRAY,
+  SK_ColorMAGENTA,
+  SK_ColorCYAN,
+  SK_ColorWHITE,
+  SK_ColorBLACK,
+  SkColorSetRGB(0xFF, 0x8C, 0x00),
+  SkColorSetRGB(0x8B, 0x45, 0x13),
+  SkColorSetRGB(0xFF, 0xDE, 0xAD),
+};
+const int kAlpha = 0x60;
+const int kMaxPaths = arraysize(kColors);
+const int kReducedScale = 10;
+
+const char* GetTouchEventLabel(ui::EventType type) {
+  switch (type) {
+    case ui::ET_UNKNOWN:
+      return " ";
+    case ui::ET_TOUCH_PRESSED:
+      return "P";
+    case ui::ET_TOUCH_MOVED:
+      return "M";
+    case ui::ET_TOUCH_RELEASED:
+      return "R";
+    case ui::ET_TOUCH_CANCELLED:
+      return "C";
+    default:
+      break;
+  }
+  return "?";
+}
+
+int GetTrackingId(const ui::TouchEvent& event) {
+  if (!event.HasNativeEvent())
+    return 0;
+#if defined(USE_XI2_MT)
+  ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
+  double tracking_id;
+  if (manager->GetEventData(*event.native_event(),
+                            ui::DeviceDataManager::DT_TOUCH_TRACKING_ID,
+                            &tracking_id)) {
+    return static_cast<int>(tracking_id);
+  }
+#endif
+  return 0;
+}
+
+int GetSourceDeviceId(const ui::TouchEvent& event) {
+  if (!event.HasNativeEvent())
+    return 0;
+#if defined(USE_X11)
+  XEvent* xev = event.native_event();
+  return static_cast<XIDeviceEvent*>(xev->xcookie.data)->sourceid;
+#endif
+  return 0;
+}
+
+// A TouchPointLog represents a single touch-event of a touch point.
+struct TouchPointLog {
+ public:
+  explicit TouchPointLog(const ui::TouchEvent& touch)
+      : id(touch.touch_id()),
+        type(touch.type()),
+        location(touch.root_location()),
+        timestamp(touch.time_stamp().InMillisecondsF()),
+        radius_x(touch.radius_x()),
+        radius_y(touch.radius_y()),
+        pressure(touch.force()),
+        tracking_id(GetTrackingId(touch)),
+        source_device(GetSourceDeviceId(touch)) {
+  }
+
+  // Populates a dictionary value with all the information about the touch
+  // point.
+  scoped_ptr<DictionaryValue> GetAsDictionary() const {
+    scoped_ptr<DictionaryValue> value(new DictionaryValue());
+
+    value->SetInteger("id", id);
+    value->SetString("type", std::string(GetTouchEventLabel(type)));
+    value->SetString("location", location.ToString());
+    value->SetDouble("timestamp", timestamp);
+    value->SetDouble("radius_x", radius_x);
+    value->SetDouble("radius_y", radius_y);
+    value->SetDouble("pressure", pressure);
+    value->SetInteger("tracking_id", tracking_id);
+    value->SetInteger("source_device", source_device);
+
+    return value.Pass();
+  }
+
+  int id;
+  ui::EventType type;
+  gfx::Point location;
+  double timestamp;
+  float radius_x;
+  float radius_y;
+  float pressure;
+  int tracking_id;
+  int source_device;
+};
+
+// A TouchTrace keeps track of all the touch events of a single touch point
+// (starting from a touch-press and ending at a touch-release or touch-cancel).
+class TouchTrace {
+ public:
+  typedef std::vector<TouchPointLog>::iterator iterator;
+  typedef std::vector<TouchPointLog>::const_iterator const_iterator;
+  typedef std::vector<TouchPointLog>::reverse_iterator reverse_iterator;
+  typedef std::vector<TouchPointLog>::const_reverse_iterator
+      const_reverse_iterator;
+
+  TouchTrace() {
+  }
+
+  void AddTouchPoint(const ui::TouchEvent& touch) {
+    log_.push_back(TouchPointLog(touch));
+  }
+
+  const std::vector<TouchPointLog>& log() const { return log_; }
+
+  bool active() const {
+    return !log_.empty() && log_.back().type != ui::ET_TOUCH_RELEASED &&
+        log_.back().type != ui::ET_TOUCH_CANCELLED;
+  }
+
+  // Returns a list containing data from all events for the touch point.
+  scoped_ptr<ListValue> GetAsList() const {
+    scoped_ptr<ListValue> list(new ListValue());
+    for (const_iterator i = log_.begin(); i != log_.end(); ++i)
+      list->Append((*i).GetAsDictionary().release());
+    return list.Pass();
+  }
+
+  void Reset() {
+    log_.clear();
+  }
+
+ private:
+  std::vector<TouchPointLog> log_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchTrace);
+};
+
+// A TouchLog keeps track of all touch events of all touch points.
+class TouchLog {
+ public:
+  TouchLog() : next_trace_index_(0) {
+  }
+
+  void AddTouchPoint(const ui::TouchEvent& touch) {
+    if (touch.type() == ui::ET_TOUCH_PRESSED)
+      StartTrace(touch);
+    AddToTrace(touch);
+  }
+
+  void Reset() {
+    next_trace_index_ = 0;
+    for (int i = 0; i < kMaxPaths; ++i)
+      traces_[i].Reset();
+  }
+
+  scoped_ptr<ListValue> GetAsList() const {
+    scoped_ptr<ListValue> list(new ListValue());
+    for (int i = 0; i < kMaxPaths; ++i) {
+      if (!traces_[i].log().empty())
+        list->Append(traces_[i].GetAsList().release());
+    }
+    return list.Pass();
+  }
+
+  int GetTraceIndex(int touch_id) const {
+    return touch_id_to_trace_index_.at(touch_id);
+  }
+
+  const TouchTrace* traces() const {
+    return traces_;
+  }
+
+ private:
+  void StartTrace(const ui::TouchEvent& touch) {
+    // Find the first inactive spot; otherwise, overwrite the one
+    // |next_trace_index_| is pointing to.
+    int old_trace_index = next_trace_index_;
+    do {
+      if (!traces_[next_trace_index_].active())
+        break;
+      next_trace_index_ = (next_trace_index_ + 1) % kMaxPaths;
+    } while (next_trace_index_ != old_trace_index);
+    int touch_id = touch.touch_id();
+    traces_[next_trace_index_].Reset();
+    touch_id_to_trace_index_[touch_id] = next_trace_index_;
+    next_trace_index_ = (next_trace_index_ + 1) % kMaxPaths;
+  }
+
+  void AddToTrace(const ui::TouchEvent& touch) {
+    int touch_id = touch.touch_id();
+    int trace_index = touch_id_to_trace_index_[touch_id];
+    traces_[trace_index].AddTouchPoint(touch);
+  }
+
+  TouchTrace traces_[kMaxPaths];
+  int next_trace_index_;
+
+  std::map<int, int> touch_id_to_trace_index_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchLog);
+};
+
+// TouchHudCanvas draws touch traces in |FULLSCREEN| and |REDUCED_SCALE| modes.
+class TouchHudCanvas : public views::View {
+ public:
+  explicit TouchHudCanvas(const TouchLog& touch_log)
+      : touch_log_(touch_log),
+        scale_(1) {
+    SetPaintToLayer(true);
+    SetFillsBoundsOpaquely(false);
+
+    paint_.setStyle(SkPaint::kFill_Style);
+  }
+
+  virtual ~TouchHudCanvas() {}
+
+  void SetScale(int scale) {
+    if (scale_ == scale)
+      return;
+    scale_ = scale;
+    gfx::Transform transform;
+    transform.Scale(1. / scale_, 1. / scale_);
+    layer()->SetTransform(transform);
+  }
+
+  int scale() const { return scale_; }
+
+  void TouchPointAdded(int touch_id) {
+    int trace_index = touch_log_.GetTraceIndex(touch_id);
+    const TouchTrace& trace = touch_log_.traces()[trace_index];
+    const TouchPointLog& point = trace.log().back();
+    if (point.type == ui::ET_TOUCH_PRESSED)
+      StartedTrace(trace_index);
+    if (point.type != ui::ET_TOUCH_CANCELLED)
+      AddedPointToTrace(trace_index);
+  }
+
+  void Clear() {
+    for (int i = 0; i < kMaxPaths; ++i)
+      paths_[i].reset();
+
+    SchedulePaint();
+  }
+
+ private:
+  void StartedTrace(int trace_index) {
+    paths_[trace_index].reset();
+    colors_[trace_index] = SkColorSetA(kColors[trace_index], kAlpha);
+  }
+
+  void AddedPointToTrace(int trace_index) {
+    const TouchTrace& trace = touch_log_.traces()[trace_index];
+    const TouchPointLog& point = trace.log().back();
+    const gfx::Point& location = point.location;
+    SkScalar x = SkIntToScalar(location.x());
+    SkScalar y = SkIntToScalar(location.y());
+    SkPoint last;
+    if (!paths_[trace_index].getLastPt(&last) || x != last.x() ||
+        y != last.y()) {
+      paths_[trace_index].addCircle(x, y, SkIntToScalar(kPointRadius));
+      SchedulePaint();
+    }
+  }
+
+  // Overridden from views::View.
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+    for (int i = 0; i < kMaxPaths; ++i) {
+      if (paths_[i].countPoints() == 0)
+        continue;
+      paint_.setColor(colors_[i]);
+      canvas->DrawPath(paths_[i], paint_);
+    }
+  }
+
+  SkPaint paint_;
+
+  const TouchLog& touch_log_;
+  SkPath paths_[kMaxPaths];
+  SkColor colors_[kMaxPaths];
+
+  int scale_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchHudCanvas);
+};
+
+TouchHudDebug::TouchHudDebug(aura::RootWindow* initial_root)
+    : TouchObserverHUD(initial_root),
+      mode_(FULLSCREEN),
+      touch_log_(new TouchLog()),
+      canvas_(NULL),
+      label_container_(NULL) {
+  const gfx::Display& display =
+      Shell::GetInstance()->display_manager()->GetDisplayForId(display_id());
+
+  views::View* content = widget()->GetContentsView();
+
+  canvas_ = new TouchHudCanvas(*touch_log_);
+  content->AddChildView(canvas_);
+
+  const gfx::Size& display_size = display.size();
+  canvas_->SetSize(display_size);
+
+  label_container_ = new views::View;
+  label_container_->SetLayoutManager(new views::BoxLayout(
+      views::BoxLayout::kVertical, 0, 0, 0));
+
+  for (int i = 0; i < kMaxTouchPoints; ++i) {
+    touch_labels_[i] = new views::Label;
+    touch_labels_[i]->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
+    touch_labels_[i]->SetShadowColors(SK_ColorWHITE,
+                                      SK_ColorWHITE);
+    touch_labels_[i]->SetShadowOffset(1, 1);
+    label_container_->AddChildView(touch_labels_[i]);
+  }
+  label_container_->SetX(0);
+  label_container_->SetY(display_size.height() / kReducedScale);
+  label_container_->SetSize(label_container_->GetPreferredSize());
+  label_container_->SetVisible(false);
+  content->AddChildView(label_container_);
+}
+
+TouchHudDebug::~TouchHudDebug() {
+}
+
+// static
+scoped_ptr<DictionaryValue> TouchHudDebug::GetAllAsDictionary() {
+  scoped_ptr<DictionaryValue> value(new DictionaryValue());
+  Shell::RootWindowList roots = Shell::GetInstance()->GetAllRootWindows();
+  for (Shell::RootWindowList::iterator iter = roots.begin();
+      iter != roots.end(); ++iter) {
+    internal::RootWindowController* controller = GetRootWindowController(*iter);
+    internal::TouchHudDebug* hud = controller->touch_hud_debug();
+    if (hud) {
+      scoped_ptr<ListValue> list = hud->GetLogAsList();
+      if (!list->empty())
+        value->Set(base::Int64ToString(hud->display_id()), list.release());
+    }
+  }
+  return value.Pass();
+}
+
+void TouchHudDebug::ChangeToNextMode() {
+  switch (mode_) {
+    case FULLSCREEN:
+      SetMode(REDUCED_SCALE);
+      break;
+    case REDUCED_SCALE:
+      SetMode(INVISIBLE);
+      break;
+    case INVISIBLE:
+      SetMode(FULLSCREEN);
+      break;
+  }
+}
+
+scoped_ptr<ListValue> TouchHudDebug::GetLogAsList() const {
+  return touch_log_->GetAsList();
+}
+
+void TouchHudDebug::Clear() {
+  if (widget()->IsVisible()) {
+    canvas_->Clear();
+    for (int i = 0; i < kMaxTouchPoints; ++i)
+      touch_labels_[i]->SetText(string16());
+    label_container_->SetSize(label_container_->GetPreferredSize());
+  }
+}
+
+void TouchHudDebug::SetMode(Mode mode) {
+  if (mode_ == mode)
+    return;
+  mode_ = mode;
+  switch (mode) {
+    case FULLSCREEN:
+      label_container_->SetVisible(false);
+      canvas_->SetVisible(true);
+      canvas_->SetScale(1);
+      canvas_->SchedulePaint();
+      widget()->Show();
+      break;
+    case REDUCED_SCALE:
+      label_container_->SetVisible(true);
+      canvas_->SetVisible(true);
+      canvas_->SetScale(kReducedScale);
+      canvas_->SchedulePaint();
+      widget()->Show();
+      break;
+    case INVISIBLE:
+      widget()->Hide();
+      break;
+  }
+}
+
+void TouchHudDebug::UpdateTouchPointLabel(int index) {
+  int trace_index = touch_log_->GetTraceIndex(index);
+  const TouchTrace& trace = touch_log_->traces()[trace_index];
+  TouchTrace::const_reverse_iterator point = trace.log().rbegin();
+  ui::EventType touch_status = point->type;
+  float touch_radius = std::max(point->radius_x, point->radius_y);
+  while (point != trace.log().rend() && point->type == ui::ET_TOUCH_CANCELLED)
+    point++;
+  DCHECK(point != trace.log().rend());
+  gfx::Point touch_position = point->location;
+
+  std::string string = base::StringPrintf("%2d: %s %s (%.4f)",
+                                          index,
+                                          GetTouchEventLabel(touch_status),
+                                          touch_position.ToString().c_str(),
+                                          touch_radius);
+  touch_labels_[index]->SetText(UTF8ToUTF16(string));
+}
+
+void TouchHudDebug::OnTouchEvent(ui::TouchEvent* event) {
+  if (event->touch_id() >= kMaxTouchPoints)
+    return;
+
+  touch_log_->AddTouchPoint(*event);
+  canvas_->TouchPointAdded(event->touch_id());
+  UpdateTouchPointLabel(event->touch_id());
+  label_container_->SetSize(label_container_->GetPreferredSize());
+}
+
+void TouchHudDebug::OnDisplayBoundsChanged(const gfx::Display& display) {
+  TouchObserverHUD::OnDisplayBoundsChanged(display);
+
+  if (display.id() != display_id())
+    return;
+  const gfx::Size& size = display.size();
+  canvas_->SetSize(size);
+  label_container_->SetY(size.height() / kReducedScale);
+}
+
+void TouchHudDebug::SetHudForRootWindowController(
+    RootWindowController* controller) {
+  controller->set_touch_hud_debug(this);
+}
+
+void TouchHudDebug::UnsetHudForRootWindowController(
+    RootWindowController* controller) {
+  controller->set_touch_hud_debug(NULL);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/touch/touch_hud_debug.h b/ash/touch/touch_hud_debug.h
new file mode 100644
index 0000000..a02a888
--- /dev/null
+++ b/ash/touch/touch_hud_debug.h
@@ -0,0 +1,87 @@
+// 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_TOUCH_TOUCH_HUD_DEBUG_H_
+#define ASH_TOUCH_TOUCH_HUD_DEBUG_H_
+
+#include <map>
+
+#include "ash/ash_export.h"
+#include "ash/touch/touch_observer_hud.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+namespace views {
+class Label;
+class View;
+}
+
+namespace ash {
+namespace internal {
+
+class TouchHudCanvas;
+class TouchLog;
+
+// A heads-up display to show touch traces on the screen and log touch events.
+// As a derivative of TouchObserverHUD, objects of this class manage their own
+// lifetime.
+class ASH_EXPORT TouchHudDebug : public TouchObserverHUD {
+ public:
+  enum Mode {
+    FULLSCREEN,
+    REDUCED_SCALE,
+    INVISIBLE,
+  };
+
+  explicit TouchHudDebug(aura::RootWindow* initial_root);
+
+  // Returns the log of touch events for all displays as a dictionary mapping id
+  // of each display to its touch log.
+  static scoped_ptr<DictionaryValue> GetAllAsDictionary();
+
+  // Changes the display mode (e.g. scale, visibility). Calling this repeatedly
+  // cycles between a fixed number of display modes.
+  void ChangeToNextMode();
+
+  // Returns log of touch events as a list value. Each item in the list is a
+  // trace of one touch point.
+  scoped_ptr<ListValue> GetLogAsList() const;
+
+  Mode mode() const { return mode_; }
+
+  // Overriden from TouchObserverHUD.
+  virtual void Clear() OVERRIDE;
+
+ private:
+  virtual ~TouchHudDebug();
+
+  void SetMode(Mode mode);
+
+  void UpdateTouchPointLabel(int index);
+
+  // Overriden from TouchObserverHUD.
+  virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+  virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
+  virtual void SetHudForRootWindowController(
+      RootWindowController* controller) OVERRIDE;
+  virtual void UnsetHudForRootWindowController(
+      RootWindowController* controller) OVERRIDE;
+
+  static const int kMaxTouchPoints = 32;
+
+  Mode mode_;
+
+  scoped_ptr<TouchLog> touch_log_;
+
+  TouchHudCanvas* canvas_;
+  views::View* label_container_;
+  views::Label* touch_labels_[kMaxTouchPoints];
+
+  DISALLOW_COPY_AND_ASSIGN(TouchHudDebug);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_TOUCH_TOUCH_HUD_DEBUG_H_
diff --git a/ash/touch/touch_hud_projection.cc b/ash/touch/touch_hud_projection.cc
new file mode 100644
index 0000000..3a834a5
--- /dev/null
+++ b/ash/touch/touch_hud_projection.cc
@@ -0,0 +1,186 @@
+// 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/touch/touch_hud_projection.h"
+
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ash/wm/property_util.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "ui/base/animation/linear_animation.h"
+#include "ui/base/events/event.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/size.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace internal {
+
+const int kPointRadius = 20;
+const SkColor kProjectionFillColor = SkColorSetRGB(0xF5, 0xF5, 0xDC);
+const SkColor kProjectionStrokeColor = SK_ColorGRAY;
+const int kProjectionAlpha = 0xB0;
+const int kFadeoutDurationInMs = 250;
+const int kFadeoutFrameRate = 60;
+
+// TouchPointView draws a single touch point. This object manages its own
+// lifetime and deletes itself upon fade-out completion or whenever |Remove()|
+// is explicitly called.
+class TouchPointView : public views::View,
+                       public ui::AnimationDelegate,
+                       public views::WidgetObserver {
+ public:
+  explicit TouchPointView(views::Widget* parent_widget)
+      : circle_center_(kPointRadius + 1, kPointRadius + 1),
+        gradient_center_(SkPoint::Make(kPointRadius + 1,
+                                       kPointRadius + 1)) {
+    SetPaintToLayer(true);
+    SetFillsBoundsOpaquely(false);
+
+    SetSize(gfx::Size(2 * kPointRadius + 2, 2 * kPointRadius + 2));
+
+    stroke_paint_.setStyle(SkPaint::kStroke_Style);
+    stroke_paint_.setColor(kProjectionStrokeColor);
+
+    gradient_colors_[0] = kProjectionFillColor;
+    gradient_colors_[1] = kProjectionStrokeColor;
+
+    gradient_pos_[0] = SkFloatToScalar(0.9f);
+    gradient_pos_[1] = SkFloatToScalar(1.0f);
+
+    parent_widget->GetContentsView()->AddChildView(this);
+
+    parent_widget->AddObserver(this);
+  }
+
+  void UpdateTouch(const ui::TouchEvent& touch) {
+    if (touch.type() == ui::ET_TOUCH_RELEASED ||
+        touch.type() == ui::ET_TOUCH_CANCELLED) {
+      fadeout_.reset(new ui::LinearAnimation(kFadeoutDurationInMs,
+                                             kFadeoutFrameRate,
+                                             this));
+      fadeout_->Start();
+    } else {
+      SetX(touch.root_location().x() - kPointRadius - 1);
+      SetY(touch.root_location().y() - kPointRadius - 1);
+    }
+  }
+
+  void Remove() {
+    delete this;
+  }
+
+ private:
+  virtual ~TouchPointView() {
+    GetWidget()->RemoveObserver(this);
+    parent()->RemoveChildView(this);
+  }
+
+  // Overridden from views::View.
+  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+    int alpha = kProjectionAlpha;
+    if (fadeout_)
+      alpha = static_cast<int>(fadeout_->CurrentValueBetween(alpha, 0));
+    fill_paint_.setAlpha(alpha);
+    stroke_paint_.setAlpha(alpha);
+    SkShader* shader = SkGradientShader::CreateRadial(
+        gradient_center_,
+        SkIntToScalar(kPointRadius),
+        gradient_colors_,
+        gradient_pos_,
+        arraysize(gradient_colors_),
+        SkShader::kMirror_TileMode,
+        NULL);
+    fill_paint_.setShader(shader);
+    shader->unref();
+    canvas->DrawCircle(circle_center_, SkIntToScalar(kPointRadius),
+                       fill_paint_);
+    canvas->DrawCircle(circle_center_, SkIntToScalar(kPointRadius),
+                       stroke_paint_);
+  }
+
+  // Overridden from ui::AnimationDelegate.
+  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
+    DCHECK_EQ(fadeout_.get(), animation);
+    delete this;
+  }
+
+  virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
+    DCHECK_EQ(fadeout_.get(), animation);
+    SchedulePaint();
+  }
+
+  virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE {
+    AnimationEnded(animation);
+  }
+
+  // Overridden from views::WidgetObserver.
+  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
+    fadeout_->Stop();
+  }
+
+  const gfx::Point circle_center_;
+  const SkPoint gradient_center_;
+
+  SkPaint fill_paint_;
+  SkPaint stroke_paint_;
+  SkColor gradient_colors_[2];
+  SkScalar gradient_pos_[2];
+
+  scoped_ptr<ui::Animation> fadeout_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchPointView);
+};
+
+TouchHudProjection::TouchHudProjection(aura::RootWindow* initial_root)
+    : TouchObserverHUD(initial_root) {
+}
+
+TouchHudProjection::~TouchHudProjection() {
+}
+
+void TouchHudProjection::Clear() {
+  for (std::map<int, TouchPointView*>::iterator iter = points_.begin();
+      iter != points_.end(); iter++)
+    iter->second->Remove();
+  points_.clear();
+}
+
+void TouchHudProjection::OnTouchEvent(ui::TouchEvent* event) {
+  if (event->type() == ui::ET_TOUCH_PRESSED) {
+    TouchPointView* point = new TouchPointView(widget());
+    point->UpdateTouch(*event);
+    std::pair<std::map<int, TouchPointView*>::iterator, bool> result =
+        points_.insert(std::make_pair(event->touch_id(), point));
+    // If a |TouchPointView| is already mapped to the touch id, remove it and
+    // replace it with the new one.
+    if (!result.second) {
+      result.first->second->Remove();
+      result.first->second = point;
+    }
+  } else {
+    std::map<int, TouchPointView*>::iterator iter =
+        points_.find(event->touch_id());
+    if (iter != points_.end()) {
+      iter->second->UpdateTouch(*event);
+      if (event->type() == ui::ET_TOUCH_RELEASED ||
+          event->type() == ui::ET_TOUCH_CANCELLED)
+        points_.erase(iter);
+    }
+  }
+}
+
+void TouchHudProjection::SetHudForRootWindowController(
+    RootWindowController* controller) {
+  controller->set_touch_hud_projection(this);
+}
+
+void TouchHudProjection::UnsetHudForRootWindowController(
+    RootWindowController* controller) {
+  controller->set_touch_hud_projection(NULL);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/touch/touch_hud_projection.h b/ash/touch/touch_hud_projection.h
new file mode 100644
index 0000000..29fddc3
--- /dev/null
+++ b/ash/touch/touch_hud_projection.h
@@ -0,0 +1,44 @@
+// 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_TOUCH_TOUCH_HUD_PROJECTION_H_
+#define ASH_TOUCH_TOUCH_HUD_PROJECTION_H_
+
+#include <map>
+
+#include "ash/touch/touch_observer_hud.h"
+
+namespace ash {
+namespace internal {
+
+class TouchPointView;
+
+// A heads-up display to show active touch points on the screen. As a derivative
+// of TouchObserverHUD, objects of this class manage their own lifetime.
+class TouchHudProjection : public TouchObserverHUD {
+ public:
+  explicit TouchHudProjection(aura::RootWindow* initial_root);
+
+  // Overriden from TouchObserverHUD.
+  virtual void Clear() OVERRIDE;
+
+ private:
+  virtual ~TouchHudProjection();
+
+  // Overriden from TouchObserverHUD.
+  virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
+  virtual void SetHudForRootWindowController(
+      RootWindowController* controller) OVERRIDE;
+  virtual void UnsetHudForRootWindowController(
+      RootWindowController* controller) OVERRIDE;
+
+  std::map<int, TouchPointView*> points_;
+
+  DISALLOW_COPY_AND_ASSIGN(TouchHudProjection);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_TOUCH_TOUCH_HUD_PROJECTION_H_
diff --git a/ash/touch/touch_observer_hud.cc b/ash/touch/touch_observer_hud.cc
index a9ac307..ceb87fd 100644
--- a/ash/touch/touch_observer_hud.cc
+++ b/ash/touch/touch_observer_hud.cc
@@ -4,496 +4,40 @@
 
 #include "ash/touch/touch_observer_hud.h"
 
-#include "ash/display/display_controller.h"
 #include "ash/display/display_manager.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/shell_window_ids.h"
 #include "ash/wm/property_util.h"
-#include "base/json/json_string_value_serializer.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkXfermode.h"
-#include "third_party/skia/include/effects/SkGradientShader.h"
 #include "ui/aura/root_window.h"
-#include "ui/aura/window.h"
-#include "ui/base/animation/animation_delegate.h"
-#include "ui/base/animation/linear_animation.h"
-#include "ui/base/events/event.h"
-#include "ui/gfx/canvas.h"
 #include "ui/gfx/display.h"
 #include "ui/gfx/rect.h"
 #include "ui/gfx/screen.h"
 #include "ui/gfx/size.h"
-#include "ui/gfx/transform.h"
-#include "ui/views/background.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/layout/box_layout.h"
 #include "ui/views/widget/widget.h"
 
-#if defined(USE_X11)
-#include <X11/extensions/XInput2.h>
-#include <X11/Xlib.h>
-
-#include "ui/base/x/device_data_manager.h"
-#endif
-
 namespace ash {
 namespace internal {
 
-const int kPointRadius = 20;
-const SkColor kColors[] = {
-  SK_ColorYELLOW,
-  SK_ColorGREEN,
-  SK_ColorRED,
-  SK_ColorBLUE,
-  SK_ColorGRAY,
-  SK_ColorMAGENTA,
-  SK_ColorCYAN,
-  SK_ColorWHITE,
-  SK_ColorBLACK,
-  SkColorSetRGB(0xFF, 0x8C, 0x00),
-  SkColorSetRGB(0x8B, 0x45, 0x13),
-  SkColorSetRGB(0xFF, 0xDE, 0xAD),
-};
-const int kAlpha = 0x60;
-const SkColor kProjectionFillColor = SkColorSetRGB(0xF5, 0xF5, 0xDC);
-const SkColor kProjectionStrokeColor = SK_ColorGRAY;
-const int kProjectionAlpha = 0xB0;
-const int kMaxPaths = arraysize(kColors);
-const int kReducedScale = 10;
-const int kFadeoutDurationInMs = 250;
-const int kFadeoutFrameRate = 60;
-
-const char* GetTouchEventLabel(ui::EventType type) {
-  switch (type) {
-    case ui::ET_UNKNOWN:
-      return " ";
-    case ui::ET_TOUCH_PRESSED:
-      return "P";
-    case ui::ET_TOUCH_MOVED:
-      return "M";
-    case ui::ET_TOUCH_RELEASED:
-      return "R";
-    case ui::ET_TOUCH_CANCELLED:
-      return "C";
-    default:
-      break;
-  }
-  return "?";
-}
-
-int GetTrackingId(const ui::TouchEvent& event) {
-  if (!event.HasNativeEvent())
-    return 0;
-#if defined(USE_XI2_MT)
-  ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
-  double tracking_id;
-  if (manager->GetEventData(*event.native_event(),
-                            ui::DeviceDataManager::DT_TOUCH_TRACKING_ID,
-                            &tracking_id)) {
-    return static_cast<int>(tracking_id);
-  }
-#endif
-  return 0;
-}
-
-int GetSourceDeviceId(const ui::TouchEvent& event) {
-  if (!event.HasNativeEvent())
-    return 0;
-#if defined(USE_X11)
-  XEvent* xev = event.native_event();
-  return static_cast<XIDeviceEvent*>(xev->xcookie.data)->sourceid;
-#endif
-  return 0;
-}
-
-// A TouchPointLog represents a single touch-event of a touch point.
-struct TouchPointLog {
- public:
-  explicit TouchPointLog(const ui::TouchEvent& touch)
-      : id(touch.touch_id()),
-        type(touch.type()),
-        location(touch.root_location()),
-        timestamp(touch.time_stamp().InMillisecondsF()),
-        radius_x(touch.radius_x()),
-        radius_y(touch.radius_y()),
-        pressure(touch.force()),
-        tracking_id(GetTrackingId(touch)),
-        source_device(GetSourceDeviceId(touch)) {
-  }
-
-  // Populates a dictionary value with all the information about the touch
-  // point.
-  scoped_ptr<DictionaryValue> GetAsDictionary() const {
-    scoped_ptr<DictionaryValue> value(new DictionaryValue());
-
-    value->SetInteger("id", id);
-    value->SetString("type", std::string(GetTouchEventLabel(type)));
-    value->SetString("location", location.ToString());
-    value->SetDouble("timestamp", timestamp);
-    value->SetDouble("radius_x", radius_x);
-    value->SetDouble("radius_y", radius_y);
-    value->SetDouble("pressure", pressure);
-    value->SetInteger("tracking_id", tracking_id);
-    value->SetInteger("source_device", source_device);
-
-    return value.Pass();
-  }
-
-  int id;
-  ui::EventType type;
-  gfx::Point location;
-  double timestamp;
-  float radius_x;
-  float radius_y;
-  float pressure;
-  int tracking_id;
-  int source_device;
-};
-
-// A TouchTrace keeps track of all the touch events of a single touch point
-// (starting from a touch-press and ending at touch-release).
-class TouchTrace {
- public:
-  typedef std::vector<TouchPointLog>::iterator iterator;
-  typedef std::vector<TouchPointLog>::const_iterator const_iterator;
-  typedef std::vector<TouchPointLog>::reverse_iterator reverse_iterator;
-  typedef std::vector<TouchPointLog>::const_reverse_iterator
-      const_reverse_iterator;
-
-  TouchTrace() {
-  }
-
-  void AddTouchPoint(const ui::TouchEvent& touch) {
-    log_.push_back(TouchPointLog(touch));
-  }
-
-  const std::vector<TouchPointLog>& log() const { return log_; }
-
-  bool active() const {
-    return !log_.empty() && log_.back().type != ui::ET_TOUCH_RELEASED &&
-        log_.back().type != ui::ET_TOUCH_CANCELLED;
-  }
-
-  // Returns a list containing data from all events for the touch point.
-  scoped_ptr<ListValue> GetAsList() const {
-    scoped_ptr<ListValue> list(new ListValue());
-    for (const_iterator i = log_.begin(); i != log_.end(); ++i)
-      list->Append((*i).GetAsDictionary().release());
-    return list.Pass();
-  }
-
-  void Reset() {
-    log_.clear();
-  }
-
- private:
-  std::vector<TouchPointLog> log_;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchTrace);
-};
-
-// A TouchLog keeps track of all touch events of all touch points.
-class TouchLog {
- public:
-  TouchLog() : next_trace_index_(0) {
-  }
-
-  void AddTouchPoint(const ui::TouchEvent& touch) {
-    if (touch.type() == ui::ET_TOUCH_PRESSED)
-      StartTrace(touch);
-    AddToTrace(touch);
-  }
-
-  void Reset() {
-    next_trace_index_ = 0;
-    for (int i = 0; i < kMaxPaths; ++i)
-      traces_[i].Reset();
-  }
-
-  scoped_ptr<ListValue> GetAsList() const {
-    scoped_ptr<ListValue> list(new ListValue());
-    for (int i = 0; i < kMaxPaths; ++i) {
-      if (!traces_[i].log().empty())
-        list->Append(traces_[i].GetAsList().release());
-    }
-    return list.Pass();
-  }
-
-  int GetTraceIndex(int touch_id) const {
-    return touch_id_to_trace_index_.at(touch_id);
-  }
-
-  const TouchTrace* traces() const {
-    return traces_;
-  }
-
- private:
-  void StartTrace(const ui::TouchEvent& touch) {
-    // Find the first inactive spot; otherwise, overwrite the one
-    // |next_trace_index_| is pointing to.
-    int old_trace_index = next_trace_index_;
-    do {
-      if (!traces_[next_trace_index_].active())
-        break;
-      next_trace_index_ = (next_trace_index_ + 1) % kMaxPaths;
-    } while (next_trace_index_ != old_trace_index);
-    int touch_id = touch.touch_id();
-    traces_[next_trace_index_].Reset();
-    touch_id_to_trace_index_[touch_id] = next_trace_index_;
-    next_trace_index_ = (next_trace_index_ + 1) % kMaxPaths;
-  }
-
-  void AddToTrace(const ui::TouchEvent& touch) {
-    int touch_id = touch.touch_id();
-    int trace_index = touch_id_to_trace_index_[touch_id];
-    traces_[trace_index].AddTouchPoint(touch);
-  }
-
-  TouchTrace traces_[kMaxPaths];
-  int next_trace_index_;
-
-  std::map<int, int> touch_id_to_trace_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchLog);
-};
-
-// TouchHudCanvas draws touch traces in |FULLSCREEN| and |REDUCED_SCALE| modes.
-class TouchHudCanvas : public views::View {
- public:
-  explicit TouchHudCanvas(const TouchLog& touch_log)
-      : touch_log_(touch_log),
-        scale_(1) {
-    SetPaintToLayer(true);
-    SetFillsBoundsOpaquely(false);
-
-    paint_.setStyle(SkPaint::kFill_Style);
-  }
-
-  virtual ~TouchHudCanvas() {}
-
-  void SetScale(int scale) {
-    if (scale_ == scale)
-      return;
-    scale_ = scale;
-    gfx::Transform transform;
-    transform.Scale(1. / scale_, 1. / scale_);
-    layer()->SetTransform(transform);
-  }
-
-  int scale() const { return scale_; }
-
-  void TouchPointAdded(int touch_id) {
-    int trace_index = touch_log_.GetTraceIndex(touch_id);
-    const TouchTrace& trace = touch_log_.traces()[trace_index];
-    const TouchPointLog& point = trace.log().back();
-    if (point.type == ui::ET_TOUCH_PRESSED)
-      StartedTrace(trace_index);
-    if (point.type != ui::ET_TOUCH_CANCELLED)
-      AddedPointToTrace(trace_index);
-  }
-
-  void Clear() {
-    for (int i = 0; i < kMaxPaths; ++i)
-      paths_[i].reset();
-
-    SchedulePaint();
-  }
-
- private:
-  void StartedTrace(int trace_index) {
-    paths_[trace_index].reset();
-    colors_[trace_index] = SkColorSetA(kColors[trace_index], kAlpha);
-  }
-
-  void AddedPointToTrace(int trace_index) {
-    const TouchTrace& trace = touch_log_.traces()[trace_index];
-    const TouchPointLog& point = trace.log().back();
-    const gfx::Point& location = point.location;
-    SkScalar x = SkIntToScalar(location.x());
-    SkScalar y = SkIntToScalar(location.y());
-    SkPoint last;
-    if (!paths_[trace_index].getLastPt(&last) || x != last.x() ||
-        y != last.y()) {
-      paths_[trace_index].addCircle(x, y, SkIntToScalar(kPointRadius));
-      SchedulePaint();
-    }
-  }
-
-  // Overridden from views::View.
-  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
-    for (int i = 0; i < kMaxPaths; ++i) {
-      if (paths_[i].countPoints() == 0)
-        continue;
-      paint_.setColor(colors_[i]);
-      canvas->DrawPath(paths_[i], paint_);
-    }
-  }
-
-  SkPaint paint_;
-
-  const TouchLog& touch_log_;
-  SkPath paths_[kMaxPaths];
-  SkColor colors_[kMaxPaths];
-
-  int scale_;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchHudCanvas);
-};
-
-// TouchPointView draws a single touch point in |PROJECTION| mode. This object
-// manages its own lifetime and deletes itself upon fade-out completion or
-// whenever |Remove()| is explicitly called.
-class TouchPointView : public views::View,
-                       public ui::AnimationDelegate,
-                       public views::WidgetObserver {
- public:
-  explicit TouchPointView(views::Widget* parent_widget)
-      : circle_center_(kPointRadius + 1, kPointRadius + 1),
-        gradient_center_(SkPoint::Make(kPointRadius + 1,
-                                       kPointRadius + 1)) {
-    SetPaintToLayer(true);
-    SetFillsBoundsOpaquely(false);
-
-    SetSize(gfx::Size(2 * kPointRadius + 2, 2 * kPointRadius + 2));
-
-    stroke_paint_.setStyle(SkPaint::kStroke_Style);
-    stroke_paint_.setColor(kProjectionStrokeColor);
-
-    gradient_colors_[0] = kProjectionFillColor;
-    gradient_colors_[1] = kProjectionStrokeColor;
-
-    gradient_pos_[0] = SkFloatToScalar(0.9f);
-    gradient_pos_[1] = SkFloatToScalar(1.0f);
-
-    parent_widget->GetContentsView()->AddChildView(this);
-
-    parent_widget->AddObserver(this);
-  }
-
-  void UpdateTouch(const ui::TouchEvent& touch) {
-    if (touch.type() == ui::ET_TOUCH_RELEASED ||
-        touch.type() == ui::ET_TOUCH_CANCELLED) {
-      fadeout_.reset(new ui::LinearAnimation(kFadeoutDurationInMs,
-                                             kFadeoutFrameRate,
-                                             this));
-      fadeout_->Start();
-    } else {
-      SetX(touch.root_location().x() - kPointRadius - 1);
-      SetY(touch.root_location().y() - kPointRadius - 1);
-    }
-  }
-
-  void Remove() {
-    delete this;
-  }
-
- private:
-  virtual ~TouchPointView() {
-    GetWidget()->RemoveObserver(this);
-    parent()->RemoveChildView(this);
-  }
-
-  // Overridden from views::View.
-  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
-    int alpha = kProjectionAlpha;
-    if (fadeout_)
-      alpha = static_cast<int>(fadeout_->CurrentValueBetween(alpha, 0));
-    fill_paint_.setAlpha(alpha);
-    stroke_paint_.setAlpha(alpha);
-    SkShader* shader = SkGradientShader::CreateRadial(
-        gradient_center_,
-        SkIntToScalar(kPointRadius),
-        gradient_colors_,
-        gradient_pos_,
-        arraysize(gradient_colors_),
-        SkShader::kMirror_TileMode,
-        NULL);
-    fill_paint_.setShader(shader);
-    shader->unref();
-    canvas->DrawCircle(circle_center_, SkIntToScalar(kPointRadius),
-                       fill_paint_);
-    canvas->DrawCircle(circle_center_, SkIntToScalar(kPointRadius),
-                       stroke_paint_);
-  }
-
-  // Overridden from ui::AnimationDelegate.
-  virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE {
-    DCHECK_EQ(fadeout_.get(), animation);
-    delete this;
-  }
-
-  virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE {
-    DCHECK_EQ(fadeout_.get(), animation);
-    SchedulePaint();
-  }
-
-  virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE {
-    AnimationEnded(animation);
-  }
-
-  // Overridden from views::WidgetObserver.
-  virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE {
-    fadeout_->Stop();
-  }
-
-  const gfx::Point circle_center_;
-  const SkPoint gradient_center_;
-
-  SkPaint fill_paint_;
-  SkPaint stroke_paint_;
-  SkColor gradient_colors_[2];
-  SkScalar gradient_pos_[2];
-
-  scoped_ptr<ui::Animation> fadeout_;
-
-  DISALLOW_COPY_AND_ASSIGN(TouchPointView);
-};
-
 TouchObserverHUD::TouchObserverHUD(aura::RootWindow* initial_root)
     : display_id_(initial_root->GetProperty(kDisplayIdKey)),
       root_window_(initial_root),
-      mode_(FULLSCREEN),
-      touch_log_(new TouchLog()) {
+      widget_(NULL) {
   const gfx::Display& display =
       Shell::GetInstance()->display_manager()->GetDisplayForId(display_id_);
 
   views::View* content = new views::View;
 
-  canvas_ = new TouchHudCanvas(*touch_log_);
-  content->AddChildView(canvas_);
-
   const gfx::Size& display_size = display.size();
-  canvas_->SetSize(display_size);
   content->SetSize(display_size);
 
-  label_container_ = new views::View;
-  label_container_->SetLayoutManager(new views::BoxLayout(
-      views::BoxLayout::kVertical, 0, 0, 0));
-
-  for (int i = 0; i < kMaxTouchPoints; ++i) {
-    touch_labels_[i] = new views::Label;
-    touch_labels_[i]->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
-    touch_labels_[i]->SetShadowColors(SK_ColorWHITE,
-                                      SK_ColorWHITE);
-    touch_labels_[i]->SetShadowOffset(1, 1);
-    label_container_->AddChildView(touch_labels_[i]);
-  }
-  label_container_->SetX(0);
-  label_container_->SetY(display_size.height() / kReducedScale);
-  label_container_->SetSize(label_container_->GetPreferredSize());
-  label_container_->SetVisible(false);
-  content->AddChildView(label_container_);
-
   widget_ = new views::Widget();
   views::Widget::InitParams
       params(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.can_activate = false;
   params.accept_events = false;
-  params.bounds = gfx::Rect(display_size);
+  params.bounds = display.bounds();
   params.parent = Shell::GetContainer(
       root_window_,
       internal::kShellWindowId_OverlayContainer);
@@ -525,142 +69,19 @@
   widget_->RemoveObserver(this);
 }
 
-// static
-scoped_ptr<DictionaryValue> TouchObserverHUD::GetAllAsDictionary() {
-  scoped_ptr<DictionaryValue> value(new DictionaryValue());
-  Shell::RootWindowList roots = Shell::GetInstance()->GetAllRootWindows();
-  for (Shell::RootWindowList::iterator iter = roots.begin();
-      iter != roots.end(); ++iter) {
-    internal::RootWindowController* controller = GetRootWindowController(*iter);
-    if (controller->touch_observer_hud()) {
-      int64 display_id = (*iter)->GetProperty(kDisplayIdKey);
-      scoped_ptr<ListValue> list =
-          controller->touch_observer_hud()->GetLogAsList();
-      if (!list->empty())
-        value->Set(base::Int64ToString(display_id), list.release());
-    }
-  }
-  return value.Pass();
-}
-
-void TouchObserverHUD::ChangeToNextMode() {
-  switch (mode_) {
-    case FULLSCREEN:
-      SetMode(REDUCED_SCALE);
-      break;
-    case REDUCED_SCALE:
-      SetMode(PROJECTION);
-      break;
-    case PROJECTION:
-      SetMode(INVISIBLE);
-      break;
-    case INVISIBLE:
-      SetMode(FULLSCREEN);
-      break;
-  }
-}
-
 void TouchObserverHUD::Clear() {
-  if (widget_->IsVisible())
-    canvas_->Clear();
-  for (int i = 0; i < kMaxTouchPoints; ++i)
-    touch_labels_[i]->SetText(string16());
-  label_container_->SetSize(label_container_->GetPreferredSize());
 }
 
-scoped_ptr<ListValue> TouchObserverHUD::GetLogAsList() const {
-  return touch_log_->GetAsList();
+void TouchObserverHUD::Remove() {
+  root_window_->RemovePreTargetHandler(this);
+
+  RootWindowController* controller = GetRootWindowController(root_window_);
+  UnsetHudForRootWindowController(controller);
+
+  widget_->CloseNow();
 }
 
-void TouchObserverHUD::SetMode(Mode mode) {
-  if (mode_ == mode)
-    return;
-  // When going out of projection mode, hide all active touch points.
-  if (mode_ == PROJECTION) {
-    for (std::map<int, TouchPointView*>::iterator iter = points_.begin();
-        iter != points_.end(); ++iter)
-      iter->second->Remove();
-    points_.clear();
-  }
-  mode_ = mode;
-  switch (mode) {
-    case FULLSCREEN:
-      label_container_->SetVisible(false);
-      canvas_->SetVisible(true);
-      canvas_->SetScale(1);
-      canvas_->SchedulePaint();
-      widget_->Show();
-      break;
-    case REDUCED_SCALE:
-      label_container_->SetVisible(true);
-      canvas_->SetVisible(true);
-      canvas_->SetScale(kReducedScale);
-      canvas_->SchedulePaint();
-      widget_->Show();
-      break;
-    case PROJECTION:
-      label_container_->SetVisible(false);
-      canvas_->SetVisible(false);
-      widget_->Show();
-      break;
-    case INVISIBLE:
-      widget_->Hide();
-      break;
-  }
-}
-
-void TouchObserverHUD::UpdateTouchPointLabel(int index) {
-  int trace_index = touch_log_->GetTraceIndex(index);
-  const TouchTrace& trace = touch_log_->traces()[trace_index];
-  TouchTrace::const_reverse_iterator point = trace.log().rbegin();
-  ui::EventType touch_status = point->type;
-  float touch_radius = std::max(point->radius_x, point->radius_y);
-  while (point != trace.log().rend() && point->type == ui::ET_TOUCH_CANCELLED)
-    point++;
-  DCHECK(point != trace.log().rend());
-  gfx::Point touch_position = point->location;
-
-  std::string string = base::StringPrintf("%2d: %s %s (%.4f)",
-                                          index,
-                                          GetTouchEventLabel(touch_status),
-                                          touch_position.ToString().c_str(),
-                                          touch_radius);
-  touch_labels_[index]->SetText(UTF8ToUTF16(string));
-}
-
-void TouchObserverHUD::OnTouchEvent(ui::TouchEvent* event) {
-  if (event->touch_id() >= kMaxTouchPoints)
-    return;
-
-  touch_log_->AddTouchPoint(*event);
-  canvas_->TouchPointAdded(event->touch_id());
-
-  if (mode_ == PROJECTION) {
-    if (event->type() == ui::ET_TOUCH_PRESSED) {
-      TouchPointView* point = new TouchPointView(widget_);
-      point->UpdateTouch(*event);
-      std::pair<std::map<int, TouchPointView*>::iterator, bool> result =
-          points_.insert(std::make_pair(event->touch_id(), point));
-      // If a |TouchPointView| is already mapped to the touch id, remove it and
-      // replace it with the new one.
-      if (!result.second) {
-        result.first->second->Remove();
-        result.first->second = point;
-      }
-    } else {
-      std::map<int, TouchPointView*>::iterator iter =
-          points_.find(event->touch_id());
-      if (iter != points_.end()) {
-        iter->second->UpdateTouch(*event);
-        if (event->type() == ui::ET_TOUCH_RELEASED ||
-            event->type() == ui::ET_TOUCH_CANCELLED)
-          points_.erase(iter);
-      }
-    }
-  }
-
-  UpdateTouchPointLabel(event->touch_id());
-  label_container_->SetSize(label_container_->GetPreferredSize());
+void TouchObserverHUD::OnTouchEvent(ui::TouchEvent* /*event*/) {
 }
 
 void TouchObserverHUD::OnWidgetDestroying(views::Widget* widget) {
@@ -671,10 +92,7 @@
 void TouchObserverHUD::OnDisplayBoundsChanged(const gfx::Display& display) {
   if (display.id() != display_id_)
     return;
-  const gfx::Size& size = display.size();
-  widget_->SetSize(size);
-  canvas_->SetSize(size);
-  label_container_->SetY(size.height() / kReducedScale);
+  widget_->SetSize(display.size());
 }
 
 void TouchObserverHUD::OnDisplayAdded(const gfx::Display& new_display) {}
@@ -700,7 +118,7 @@
   root_window_->RemovePreTargetHandler(this);
 
   RootWindowController* controller = GetRootWindowController(root_window_);
-  controller->set_touch_observer_hud(NULL);
+  UnsetHudForRootWindowController(controller);
 
   views::Widget::ReparentNativeView(
       widget_->GetNativeView(),
@@ -723,7 +141,7 @@
                           internal::kShellWindowId_OverlayContainer));
 
   RootWindowController* controller = GetRootWindowController(root_window_);
-  controller->set_touch_observer_hud(this);
+  SetHudForRootWindowController(controller);
 
   root_window_->AddPreTargetHandler(this);
 }
diff --git a/ash/touch/touch_observer_hud.h b/ash/touch/touch_observer_hud.h
index d1c65eb..850bc5e 100644
--- a/ash/touch/touch_observer_hud.h
+++ b/ash/touch/touch_observer_hud.h
@@ -5,43 +5,23 @@
 #ifndef ASH_TOUCH_TOUCH_OBSERVER_HUD_H_
 #define ASH_TOUCH_TOUCH_OBSERVER_HUD_H_
 
-#include <map>
-
 #include "ash/ash_export.h"
 #include "ash/display/display_controller.h"
-#include "ash/shell.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/values.h"
 #include "ui/base/events/event_handler.h"
 #include "ui/gfx/display_observer.h"
-#include "ui/gfx/point.h"
 #include "ui/views/widget/widget_observer.h"
 
 #if defined(OS_CHROMEOS)
 #include "chromeos/display/output_configurator.h"
 #endif  // defined(OS_CHROMEOS)
 
-namespace aura {
-class Window;
-}
-
-namespace gfx {
-class Display;
-}
-
 namespace views {
-class Label;
-class View;
 class Widget;
 }
 
 namespace ash {
 namespace internal {
 
-class TouchHudCanvas;
-class TouchLog;
-class TouchPointView;
-
 // An event filter which handles system level gesture events. Objects of this
 // class manage their own lifetime.
 class ASH_EXPORT TouchObserverHUD
@@ -53,76 +33,54 @@
 #endif  // defined(OS_CHROMEOS)
       public DisplayController::Observer {
  public:
-  enum Mode {
-    FULLSCREEN,
-    REDUCED_SCALE,
-    PROJECTION,
-    INVISIBLE,
-  };
+  // Called to clear touch points and traces from the screen. Default
+  // implementation does nothing. Sub-classes should implement appropriately.
+  virtual void Clear();
 
+  // Removes the HUD from the screen.
+  void Remove();
+
+  int64 display_id() const { return display_id_; }
+
+ protected:
   explicit TouchObserverHUD(aura::RootWindow* initial_root);
 
-  // Returns the log of touch events as a dictionary mapping id of each display
-  // to its touch log.
-  static scoped_ptr<DictionaryValue> GetAllAsDictionary();
-
-  // Changes the display mode (e.g. scale, visibility). Calling this repeatedly
-  // cycles between a fixed number of display modes.
-  void ChangeToNextMode();
-
-  // Removes all existing touch points from the screen (only if the HUD is
-  // visible).
-  void Clear();
-
-  // Returns log of touch events as a list value. Each item in the list is a
-  // trace of one touch point.
-  scoped_ptr<ListValue> GetLogAsList() const;
-
-  Mode mode() const { return mode_; }
-
- private:
-  friend class TouchHudTest;
-
   virtual ~TouchObserverHUD();
 
-  void SetMode(Mode mode);
+  virtual void SetHudForRootWindowController(
+      RootWindowController* controller) = 0;
+  virtual void UnsetHudForRootWindowController(
+      RootWindowController* controller) = 0;
 
-  void UpdateTouchPointLabel(int index);
+  views::Widget* widget() { return widget_; }
 
-  // Overriden from ui::EventHandler:
+  // Overriden from ui::EventHandler.
   virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
 
-  // Overridden from views::WidgetObserver:
+  // Overridden from views::WidgetObserver.
   virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
 
-  // Overridden from gfx::DisplayObserver:
+  // Overridden from gfx::DisplayObserver.
   virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
   virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE;
   virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
 
 #if defined(OS_CHROMEOS)
-  // Overriden from chromeos::OutputConfigurator::Observer:
+  // Overriden from chromeos::OutputConfigurator::Observer.
   virtual void OnDisplayModeChanged() OVERRIDE;
 #endif  // defined(OS_CHROMEOS)
 
-  // Overriden form DisplayController::Observer:
+  // Overriden form DisplayController::Observer.
   virtual void OnDisplayConfigurationChanging() OVERRIDE;
   virtual void OnDisplayConfigurationChanged() OVERRIDE;
 
-  static const int kMaxTouchPoints = 32;
+ private:
+  friend class TouchHudTest;
 
   const int64 display_id_;
   aura::RootWindow* root_window_;
 
-  Mode mode_;
-
-  scoped_ptr<TouchLog> touch_log_;
-
   views::Widget* widget_;
-  TouchHudCanvas* canvas_;
-  std::map<int, TouchPointView*> points_;
-  views::View* label_container_;
-  views::Label* touch_labels_[kMaxTouchPoints];
 
   DISALLOW_COPY_AND_ASSIGN(TouchObserverHUD);
 };
diff --git a/ash/touch/touch_observer_hud_unittest.cc b/ash/touch/touch_observer_hud_unittest.cc
index 7e8b91b..0399407 100644
--- a/ash/touch/touch_observer_hud_unittest.cc
+++ b/ash/touch/touch_observer_hud_unittest.cc
@@ -7,8 +7,10 @@
 #include "ash/ash_switches.h"
 #include "ash/display/display_manager.h"
 #include "ash/root_window_controller.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/display_manager_test_api.h"
+#include "ash/touch/touch_hud_debug.h"
 #include "ash/wm/property_util.h"
 #include "base/command_line.h"
 #include "base/format_macros.h"
@@ -208,19 +210,19 @@
   }
 
   internal::TouchObserverHUD* GetInternalTouchHud() {
-    return GetInternalRootController()->touch_observer_hud();
+    return GetInternalRootController()->touch_hud_debug();
   }
 
   internal::TouchObserverHUD* GetExternalTouchHud() {
-    return GetExternalRootController()->touch_observer_hud();
+    return GetExternalRootController()->touch_hud_debug();
   }
 
   internal::TouchObserverHUD* GetPrimaryTouchHud() {
-    return GetPrimaryRootController()->touch_observer_hud();
+    return GetPrimaryRootController()->touch_hud_debug();
   }
 
   internal::TouchObserverHUD* GetSecondaryTouchHud() {
-    return GetSecondaryRootController()->touch_observer_hud();
+    return GetSecondaryRootController()->touch_hud_debug();
   }
 
   DisplayInfo CreateDisplayInfo(int64 id, const gfx::Rect& bounds) {
diff --git a/ash/touch/touch_uma.cc b/ash/touch/touch_uma.cc
index 663a319..f8ab0f1 100644
--- a/ash/touch/touch_uma.cc
+++ b/ash/touch/touch_uma.cc
@@ -22,26 +22,6 @@
 
 namespace {
 
-enum GestureActionType {
-  GESTURE_UNKNOWN,
-  GESTURE_OMNIBOX_PINCH,
-  GESTURE_OMNIBOX_SCROLL,
-  GESTURE_TABSTRIP_PINCH,
-  GESTURE_TABSTRIP_SCROLL,
-  GESTURE_BEZEL_SCROLL,
-  GESTURE_DESKTOP_SCROLL,
-  GESTURE_DESKTOP_PINCH,
-  GESTURE_WEBPAGE_PINCH,
-  GESTURE_WEBPAGE_SCROLL,
-  GESTURE_WEBPAGE_TAP,
-  GESTURE_TABSTRIP_TAP,
-  GESTURE_BEZEL_DOWN,
-// NOTE: Add new action types only immediately above this line. Also, make sure
-// the enum list in tools/histogram/histograms.xml is updated with any change in
-// here.
-  GESTURE_ACTION_COUNT
-};
-
 enum UMAEventType {
   UMA_ET_UNKNOWN,
   UMA_ET_TOUCH_RELEASED,
@@ -102,72 +82,6 @@
                                  kWindowTouchDetails,
                                  NULL);
 
-GestureActionType FindGestureActionType(aura::Window* window,
-                                        const ui::GestureEvent& event) {
-  if (!window || window->GetRootWindow() == window) {
-    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
-      return GESTURE_BEZEL_SCROLL;
-    if (event.type() == ui::ET_GESTURE_BEGIN)
-      return GESTURE_BEZEL_DOWN;
-    return GESTURE_UNKNOWN;
-  }
-
-  std::string name = window ? window->name() : std::string();
-
-  const char kDesktopBackgroundView[] = "DesktopBackgroundView";
-  if (name == kDesktopBackgroundView) {
-    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
-      return GESTURE_DESKTOP_SCROLL;
-    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
-      return GESTURE_DESKTOP_PINCH;
-    return GESTURE_UNKNOWN;
-  }
-
-  const char kWebPage[] = "RenderWidgetHostViewAura";
-  if (name == kWebPage) {
-    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
-      return GESTURE_WEBPAGE_PINCH;
-    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
-      return GESTURE_WEBPAGE_SCROLL;
-    if (event.type() == ui::ET_GESTURE_TAP)
-      return GESTURE_WEBPAGE_TAP;
-    return GESTURE_UNKNOWN;
-  }
-
-  views::Widget* widget = views::Widget::GetWidgetForNativeView(window);
-  if (!widget)
-    return GESTURE_UNKNOWN;
-
-  views::View* view = widget->GetRootView()->
-      GetEventHandlerForPoint(event.location());
-  if (!view)
-    return GESTURE_UNKNOWN;
-
-  name = view->GetClassName();
-
-  const char kTabStrip[] = "TabStrip";
-  const char kTab[] = "BrowserTab";
-  if (name == kTabStrip || name == kTab) {
-    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
-      return GESTURE_TABSTRIP_SCROLL;
-    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
-      return GESTURE_TABSTRIP_PINCH;
-    if (event.type() == ui::ET_GESTURE_TAP)
-      return GESTURE_TABSTRIP_TAP;
-    return GESTURE_UNKNOWN;
-  }
-
-  const char kOmnibox[] = "BrowserOmniboxViewViews";
-  if (name == kOmnibox) {
-    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
-      return GESTURE_OMNIBOX_SCROLL;
-    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
-      return GESTURE_OMNIBOX_PINCH;
-    return GESTURE_UNKNOWN;
-  }
-
-  return GESTURE_UNKNOWN;
-}
 
 UMAEventType UMAEventTypeFromEvent(const ui::Event& event) {
   switch (event.type()) {
@@ -257,14 +171,10 @@
 }
 
 namespace ash {
-namespace internal {
 
-TouchUMA::TouchUMA()
-    : touch_in_progress_(false),
-      burst_length_(0) {
-}
-
-TouchUMA::~TouchUMA() {
+// static
+TouchUMA* TouchUMA::GetInstance() {
+  return Singleton<TouchUMA>::get();
 }
 
 void TouchUMA::RecordGestureEvent(aura::Window* target,
@@ -274,11 +184,7 @@
                             UMA_ET_COUNT);
 
   GestureActionType action = FindGestureActionType(target, event);
-  if (action != GESTURE_UNKNOWN) {
-    UMA_HISTOGRAM_ENUMERATION("Ash.GestureTarget",
-                              action,
-                              GESTURE_ACTION_COUNT);
-  }
+  RecordGestureAction(action);
 
   if (event.type() == ui::ET_GESTURE_END &&
       event.details().touch_points() == 2) {
@@ -292,6 +198,13 @@
   }
 }
 
+void TouchUMA::RecordGestureAction(GestureActionType action) {
+  if (action == GESTURE_UNKNOWN || action >= GESTURE_ACTION_COUNT)
+    return;
+  UMA_HISTOGRAM_ENUMERATION("Ash.GestureTarget", action,
+                            GESTURE_ACTION_COUNT);
+}
+
 void TouchUMA::RecordTouchEvent(aura::Window* target,
                                 const ui::TouchEvent& event) {
   UMA_HISTOGRAM_CUSTOM_COUNTS("Ash.TouchRadius",
@@ -423,6 +336,14 @@
   }
 }
 
+TouchUMA::TouchUMA()
+    : touch_in_progress_(false),
+      burst_length_(0) {
+}
+
+TouchUMA::~TouchUMA() {
+}
+
 void TouchUMA::UpdateBurstData(const ui::TouchEvent& event) {
   if (event.type() == ui::ET_TOUCH_PRESSED) {
     if (!touch_in_progress_) {
@@ -445,5 +366,72 @@
   }
 }
 
-}  // namespace internal
+TouchUMA::GestureActionType TouchUMA::FindGestureActionType(
+    aura::Window* window,
+    const ui::GestureEvent& event) {
+  if (!window || window->GetRootWindow() == window) {
+    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
+      return GESTURE_BEZEL_SCROLL;
+    if (event.type() == ui::ET_GESTURE_BEGIN)
+      return GESTURE_BEZEL_DOWN;
+    return GESTURE_UNKNOWN;
+  }
+
+  std::string name = window ? window->name() : std::string();
+
+  const char kDesktopBackgroundView[] = "DesktopBackgroundView";
+  if (name == kDesktopBackgroundView) {
+    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
+      return GESTURE_DESKTOP_SCROLL;
+    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
+      return GESTURE_DESKTOP_PINCH;
+    return GESTURE_UNKNOWN;
+  }
+
+  const char kWebPage[] = "RenderWidgetHostViewAura";
+  if (name == kWebPage) {
+    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
+      return GESTURE_WEBPAGE_PINCH;
+    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
+      return GESTURE_WEBPAGE_SCROLL;
+    if (event.type() == ui::ET_GESTURE_TAP)
+      return GESTURE_WEBPAGE_TAP;
+    return GESTURE_UNKNOWN;
+  }
+
+  views::Widget* widget = views::Widget::GetWidgetForNativeView(window);
+  if (!widget)
+    return GESTURE_UNKNOWN;
+
+  views::View* view = widget->GetRootView()->
+      GetEventHandlerForPoint(event.location());
+  if (!view)
+    return GESTURE_UNKNOWN;
+
+  name = view->GetClassName();
+
+  const char kTabStrip[] = "TabStrip";
+  const char kTab[] = "BrowserTab";
+  if (name == kTabStrip || name == kTab) {
+    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
+      return GESTURE_TABSTRIP_SCROLL;
+    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
+      return GESTURE_TABSTRIP_PINCH;
+    if (event.type() == ui::ET_GESTURE_TAP)
+      return GESTURE_TABSTRIP_TAP;
+    return GESTURE_UNKNOWN;
+  }
+
+  const char kOmnibox[] = "BrowserOmniboxViewViews";
+  if (name == kOmnibox) {
+    if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN)
+      return GESTURE_OMNIBOX_SCROLL;
+    if (event.type() == ui::ET_GESTURE_PINCH_BEGIN)
+      return GESTURE_OMNIBOX_PINCH;
+    return GESTURE_UNKNOWN;
+  }
+
+  return GESTURE_UNKNOWN;
+}
+
 }  // namespace ash
diff --git a/ash/touch/touch_uma.h b/ash/touch/touch_uma.h
index 46805b1..3e2c7fd 100644
--- a/ash/touch/touch_uma.h
+++ b/ash/touch/touch_uma.h
@@ -8,6 +8,7 @@
 #include <map>
 
 #include "ash/shell.h"
+#include "base/memory/singleton.h"
 #include "ui/gfx/point.h"
 #include "ui/views/widget/widget.h"
 
@@ -16,22 +17,57 @@
 }
 
 namespace ash {
-namespace internal {
 
 // Records some touch/gesture event specific details (e.g. what gestures are
 // targetted to which components etc.)
-class TouchUMA {
+class ASH_EXPORT TouchUMA {
  public:
-  TouchUMA();
-  ~TouchUMA();
+  enum GestureActionType {
+    GESTURE_UNKNOWN,
+    GESTURE_OMNIBOX_PINCH,
+    GESTURE_OMNIBOX_SCROLL,
+    GESTURE_TABSTRIP_PINCH,
+    GESTURE_TABSTRIP_SCROLL,
+    GESTURE_BEZEL_SCROLL,
+    GESTURE_DESKTOP_SCROLL,
+    GESTURE_DESKTOP_PINCH,
+    GESTURE_WEBPAGE_PINCH,
+    GESTURE_WEBPAGE_SCROLL,
+    GESTURE_WEBPAGE_TAP,
+    GESTURE_TABSTRIP_TAP,
+    GESTURE_BEZEL_DOWN,
+    GESTURE_TABSWITCH_TAP,
+    GESTURE_TABNOSWITCH_TAP,
+    GESTURE_TABCLOSE_TAP,
+    GESTURE_NEWTAB_TAP,
+    GESTURE_ROOTVIEWTOP_TAP,
+    GESTURE_FRAMEMAXIMIZE_TAP,
+    GESTURE_FRAMEVIEW_TAP,
+    GESTURE_MAXIMIZE_DOUBLETAP,
+    // NOTE: Add new action types only immediately above this line. Also,
+    // make sure the enum list in tools/histogram/histograms.xml is
+    // updated with any change in here.
+    GESTURE_ACTION_COUNT
+  };
+
+  // Returns the singleton instance.
+  static TouchUMA* GetInstance();
 
   void RecordGestureEvent(aura::Window* target,
                           const ui::GestureEvent& event);
+  void RecordGestureAction(GestureActionType action);
   void RecordTouchEvent(aura::Window* target,
                         const ui::TouchEvent& event);
 
  private:
+  friend struct DefaultSingletonTraits<TouchUMA>;
+
+  TouchUMA();
+  ~TouchUMA();
+
   void UpdateBurstData(const ui::TouchEvent& event);
+  GestureActionType FindGestureActionType(aura::Window* window,
+                                          const ui::GestureEvent& event);
 
   // These are used to measure the number of touch-start events we receive in a
   // quick succession, regardless of the target window.
@@ -42,7 +78,6 @@
   DISALLOW_COPY_AND_ASSIGN(TouchUMA);
 };
 
-}  // namespace internal
 }  // namespace ash
 
 #endif  // ASH_TOUCH_TOUCH_OBSERVER_UMA_H_
diff --git a/ash/wm/app_list_controller.cc b/ash/wm/app_list_controller.cc
index a13ba65..96b90f6 100644
--- a/ash/wm/app_list_controller.cc
+++ b/ash/wm/app_list_controller.cc
@@ -121,13 +121,31 @@
         Shell::GetInstance()->delegate()->CreateAppListViewDelegate());
     aura::Window* container = GetRootWindowController(window->GetRootWindow())->
         GetContainer(kShellWindowId_AppListContainer);
-    view->InitAsBubble(
-        container,
-        pagination_model_.get(),
-        Launcher::ForWindow(container)->GetAppListButtonView(),
-        gfx::Point(),
-        GetBubbleArrow(container),
-        true /* border_accepts_events */);
+    // TODO(harrym): find a better solution for this.
+    if (ash::switches::UseAlternateShelfLayout()) {
+      gfx::Rect applist_button_bounds = Launcher::ForWindow(container)->
+          GetAppListButtonView()->GetBoundsInScreen();
+      gfx::Point anchor = applist_button_bounds.origin();
+      if (anchor.x() == 0)
+        anchor.set_x(applist_button_bounds.right());
+      if (anchor.y() == 0)
+        anchor.set_y(applist_button_bounds.bottom());
+      view->InitAsBubble(
+          container,
+          pagination_model_.get(),
+          NULL,
+          anchor,
+          GetBubbleArrow(container),
+          true /* border_accepts_events */);
+    } else {
+      view->InitAsBubble(
+          container,
+          pagination_model_.get(),
+          Launcher::ForWindow(container)->GetAppListButtonView(),
+          gfx::Point(),
+          GetBubbleArrow(container),
+          true /* border_accepts_events */);
+    }
     if (ash::switches::UseAlternateShelfLayout())
       view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
     SetView(view);
diff --git a/ash/wm/app_list_controller.h b/ash/wm/app_list_controller.h
index ac163aa..922cbcc 100644
--- a/ash/wm/app_list_controller.h
+++ b/ash/wm/app_list_controller.h
@@ -9,7 +9,7 @@
 #include "ash/shell_observer.h"
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/app_list/pagination_model_observer.h"
 #include "ui/aura/client/focus_change_observer.h"
 #include "ui/aura/window_observer.h"
diff --git a/ash/wm/boot_splash_screen.h b/ash/wm/boot_splash_screen.h
index dc4538b..f56f49f 100644
--- a/ash/wm/boot_splash_screen.h
+++ b/ash/wm/boot_splash_screen.h
@@ -9,7 +9,7 @@
 
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
+#include "base/time/time.h"
 
 namespace aura {
 class RootWindow;
diff --git a/ash/wm/dock/docked_window_layout_manager_unittest.cc b/ash/wm/dock/docked_window_layout_manager_unittest.cc
index 1c8bad9..e52109f 100644
--- a/ash/wm/dock/docked_window_layout_manager_unittest.cc
+++ b/ash/wm/dock/docked_window_layout_manager_unittest.cc
@@ -215,6 +215,9 @@
 // Tests that a created window is successfully added to the dock
 // layout manager.
 TEST_P(DockedWindowLayoutManagerTest, AddOneWindow) {
+  if (!SupportsHostWindowResize())
+    return;
+
   gfx::Rect bounds(0, 0, 201, 201);
   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
diff --git a/ash/wm/dock/docked_window_resizer.cc b/ash/wm/dock/docked_window_resizer.cc
index 931ecb0..780e1bf 100644
--- a/ash/wm/dock/docked_window_resizer.cc
+++ b/ash/wm/dock/docked_window_resizer.cc
@@ -5,7 +5,6 @@
 #include "ash/wm/dock/docked_window_resizer.h"
 
 #include "ash/ash_switches.h"
-#include "ash/display/display_controller.h"
 #include "ash/launcher/launcher.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_ash.h"
diff --git a/ash/wm/dock/docked_window_resizer_unittest.cc b/ash/wm/dock/docked_window_resizer_unittest.cc
index bed2833..7f08981 100644
--- a/ash/wm/dock/docked_window_resizer_unittest.cc
+++ b/ash/wm/dock/docked_window_resizer_unittest.cc
@@ -223,6 +223,9 @@
 
 // Verifies a window can be dragged and attached to the dock.
 TEST_P(DockedWindowResizerTest, AttachRightPrecise) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
@@ -235,6 +238,9 @@
 // Verifies a window can be dragged and attached to the dock
 // even if we overshoot the screen edge by a few pixels (sticky edge)
 TEST_P(DockedWindowResizerTest, AttachRightOvershoot) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), +4);
 
@@ -247,6 +253,9 @@
 // Verifies a window can be dragged and then if not quite reaching the screen
 // edge it does not get docked to a screen edge and stays in the workspace.
 TEST_P(DockedWindowResizerTest, AttachRightUndershoot) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), -1);
 
@@ -259,6 +268,9 @@
 
 // Verifies a window can be dragged and attached to the dock.
 TEST_P(DockedWindowResizerTest, AttachLeftPrecise) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0);
 
@@ -271,6 +283,9 @@
 // Verifies a window can be dragged and attached to the dock
 // even if we overshoot the screen edge by a few pixels (sticky edge)
 TEST_P(DockedWindowResizerTest, AttachLeftOvershoot) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), -4);
 
@@ -283,6 +298,9 @@
 // Verifies a window can be dragged and then if not quite reaching the screen
 // edge it does not get docked to a screen edge and stays in the workspace.
 TEST_P(DockedWindowResizerTest, AttachLeftUndershoot) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 1);
 
@@ -296,6 +314,9 @@
 // Dock on the right side, change shelf alignment, check that windows move to
 // the opposite side.
 TEST_P(DockedWindowResizerTest, AttachRightChangeShelf) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
@@ -332,6 +353,9 @@
 
 // Dock on the right side, try to undock, then drag more to really undock
 TEST_P(DockedWindowResizerTest, AttachTryDetach) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
@@ -366,6 +390,9 @@
 
 // Minimize a docked window, then restore it and check that it is still docked.
 TEST_P(DockedWindowResizerTest, AttachMinimizeRestore) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
@@ -452,6 +479,9 @@
 
 // Reverting drag
 TEST_P(DockedWindowResizerTest, RevertDragRestoresAttachment) {
+  if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
 
diff --git a/ash/wm/drag_window_controller.cc b/ash/wm/drag_window_controller.cc
index 85e2cb4..01ae80f 100644
--- a/ash/wm/drag_window_controller.cc
+++ b/ash/wm/drag_window_controller.cc
@@ -67,7 +67,7 @@
   DCHECK(!drag_widget_);
   drag_widget_ = new views::Widget;
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.parent = window_->parent();
   params.can_activate = false;
   params.keep_on_top = true;
diff --git a/ash/wm/event_rewriter_event_filter.cc b/ash/wm/event_rewriter_event_filter.cc
index 2ef4853..840f498 100644
--- a/ash/wm/event_rewriter_event_filter.cc
+++ b/ash/wm/event_rewriter_event_filter.cc
@@ -8,6 +8,9 @@
 #include "base/logging.h"
 #include "ui/base/events/event.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/wm/sticky_keys.h"
+#endif  // OS_CHROMEOS
 namespace ash {
 namespace internal {
 
@@ -20,6 +23,13 @@
   delegate_ = delegate.Pass();
 }
 
+void EventRewriterEventFilter::EnableStickyKeys(bool enabled) {
+#if defined(OS_CHROMEOS)
+  if (enabled)
+    sticky_keys_.reset(new StickyKeys());
+#endif  // OS_CHROMEOS
+}
+
 void EventRewriterEventFilter::OnKeyEvent(ui::KeyEvent* event) {
   if (!delegate_)
     return;
@@ -37,6 +47,14 @@
       event->StopPropagation();
       break;
   }
+
+  if (event->stopped_propagation())
+    return;
+
+#if defined(OS_CHROMEOS)
+  if (sticky_keys_.get() && sticky_keys_->HandleKeyEvent(event))
+    event->StopPropagation();
+#endif  // OS_CHROMEOS
 }
 
 void EventRewriterEventFilter::OnMouseEvent(ui::MouseEvent* event) {
diff --git a/ash/wm/event_rewriter_event_filter.h b/ash/wm/event_rewriter_event_filter.h
index 6c267a0..87cf07f 100644
--- a/ash/wm/event_rewriter_event_filter.h
+++ b/ash/wm/event_rewriter_event_filter.h
@@ -14,6 +14,9 @@
 namespace ash {
 
 class EventRewriterDelegate;
+#if defined(OS_CHROMEOS)
+class StickyKeys;
+#endif  // OS_CHROMEOS
 
 namespace internal {
 
@@ -25,12 +28,18 @@
 
   void SetEventRewriterDelegate(scoped_ptr<EventRewriterDelegate> delegate);
 
+  // Enables or disables sticky keys.
+  void EnableStickyKeys(bool enabled);
+
  private:
   // Overridden from ui::EventHandler:
   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
 
   scoped_ptr<EventRewriterDelegate> delegate_;
+#if defined(OS_CHROMEOS)
+  scoped_ptr<StickyKeys> sticky_keys_;
+#endif  // OS_CHROMEOS
 
   DISALLOW_COPY_AND_ASSIGN(EventRewriterEventFilter);
 };
diff --git a/ash/wm/frame_painter.cc b/ash/wm/frame_painter.cc
index 712594f..79e7839 100644
--- a/ash/wm/frame_painter.cc
+++ b/ash/wm/frame_painter.cc
@@ -58,8 +58,10 @@
 const int kTitleIconOffsetX = 4;
 // Space between window edge and title text, when there is no icon.
 const int kTitleNoIconOffsetX = 8;
-// Color for the title text.
-const SkColor kTitleTextColor = SkColorSetRGB(40, 40, 40);
+// Color for the non-maximized window title text.
+const SkColor kNonMaximizedWindowTitleTextColor = SkColorSetRGB(40, 40, 40);
+// Color for the maximized window title text.
+const SkColor kMaximizedWindowTitleTextColor = SK_ColorWHITE;
 // Size of header/content separator line below the header image.
 const int kHeaderContentSeparatorSize = 1;
 // Color of header bottom edge line.
@@ -591,9 +593,11 @@
   views::WidgetDelegate* delegate = frame_->widget_delegate();
   if (delegate && delegate->ShouldShowWindowTitle()) {
     gfx::Rect title_bounds = GetTitleBounds(view, title_font);
+    SkColor title_color = frame_->IsMaximized() ?
+        kMaximizedWindowTitleTextColor : kNonMaximizedWindowTitleTextColor;
     canvas->DrawStringInt(delegate->GetWindowTitle(),
                           title_font,
-                          kTitleTextColor,
+                          title_color,
                           view->GetMirroredXForRect(title_bounds),
                           title_bounds.y(),
                           title_bounds.width(),
diff --git a/ash/wm/gestures/long_press_affordance_handler.cc b/ash/wm/gestures/long_press_affordance_handler.cc
index d35c3f0..5450e97 100644
--- a/ash/wm/gestures/long_press_affordance_handler.cc
+++ b/ash/wm/gestures/long_press_affordance_handler.cc
@@ -66,7 +66,7 @@
   params.accept_events = false;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.context = root_window;
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   widget->Init(params);
   widget->SetOpacity(0xFF);
   ash::GetRootWindowController(root_window)->GetContainer(
diff --git a/ash/wm/gestures/long_press_affordance_handler.h b/ash/wm/gestures/long_press_affordance_handler.h
index c21a587..14ff0f3 100644
--- a/ash/wm/gestures/long_press_affordance_handler.h
+++ b/ash/wm/gestures/long_press_affordance_handler.h
@@ -5,7 +5,7 @@
 #ifndef ASH_WM_GESTURES_LONG_PRESS_AFFORDANCE_HANDLER_H_
 #define ASH_WM_GESTURES_LONG_PRESS_AFFORDANCE_HANDLER_H_
 
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/base/animation/linear_animation.h"
 #include "ui/gfx/point.h"
diff --git a/ash/wm/lock_state_controller.h b/ash/wm/lock_state_controller.h
index a651b5f..10fce26 100644
--- a/ash/wm/lock_state_controller.h
+++ b/ash/wm/lock_state_controller.h
@@ -12,8 +12,8 @@
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/observer_list.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "ui/aura/root_window_observer.h"
 
 namespace gfx {
diff --git a/ash/wm/lock_state_controller_impl2.cc b/ash/wm/lock_state_controller_impl2.cc
index fe7152f..c5301b7 100644
--- a/ash/wm/lock_state_controller_impl2.cc
+++ b/ash/wm/lock_state_controller_impl2.cc
@@ -12,7 +12,7 @@
 #include "ash/wm/session_state_animator.h"
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/aura/root_window.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
diff --git a/ash/wm/lock_state_controller_impl2.h b/ash/wm/lock_state_controller_impl2.h
index 2ea1616..8908a7e 100644
--- a/ash/wm/lock_state_controller_impl2.h
+++ b/ash/wm/lock_state_controller_impl2.h
@@ -11,8 +11,8 @@
 #include "ash/wm/session_state_animator.h"
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "ui/aura/root_window_observer.h"
 
 namespace gfx {
diff --git a/ash/wm/lock_state_controller_impl2_unittest.cc b/ash/wm/lock_state_controller_impl2_unittest.cc
index 8177422..62bed7c 100644
--- a/ash/wm/lock_state_controller_impl2_unittest.cc
+++ b/ash/wm/lock_state_controller_impl2_unittest.cc
@@ -14,7 +14,7 @@
 #include "ash/wm/session_state_animator.h"
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/env.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/event_generator.h"
diff --git a/ash/wm/maximize_bubble_controller.cc b/ash/wm/maximize_bubble_controller.cc
index f2e3acb..ae108ca 100644
--- a/ash/wm/maximize_bubble_controller.cc
+++ b/ash/wm/maximize_bubble_controller.cc
@@ -9,7 +9,7 @@
 #include "ash/shell_window_ids.h"
 #include "ash/wm/window_animations.h"
 #include "ash/wm/workspace/frame_maximize_button.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "grit/ash_resources.h"
 #include "grit/ash_strings.h"
 #include "third_party/skia/include/core/SkPath.h"
diff --git a/ash/wm/panels/panel_layout_manager.cc b/ash/wm/panels/panel_layout_manager.cc
index c4ccd11..efb1826 100644
--- a/ash/wm/panels/panel_layout_manager.cc
+++ b/ash/wm/panels/panel_layout_manager.cc
@@ -13,9 +13,11 @@
 #include "ash/shelf/shelf_types.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
+#include "ash/shell_window_ids.h"
 #include "ash/wm/frame_painter.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_animations.h"
+#include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -221,7 +223,7 @@
   void InitWidget(aura::Window* parent) {
     views::Widget::InitParams params;
     params.type = views::Widget::InitParams::TYPE_POPUP;
-    params.transparent = true;
+    params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
     params.can_activate = false;
     params.keep_on_top = true;
     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -250,6 +252,7 @@
 // PanelLayoutManager public implementation:
 PanelLayoutManager::PanelLayoutManager(aura::Window* panel_container)
     : panel_container_(panel_container),
+      in_add_window_(false),
       in_layout_(false),
       dragged_panel_(NULL),
       launcher_(NULL),
@@ -328,6 +331,22 @@
 void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
   if (child->type() == aura::client::WINDOW_TYPE_POPUP)
     return;
+  if (in_add_window_)
+    return;
+  base::AutoReset<bool> auto_reset_in_add_window(&in_add_window_, true);
+  if (!child->GetProperty(kPanelAttachedKey)) {
+    // This should only happen when a window is added to panel container as a
+    // result of bounds change from within the application during a drag.
+    // If so we have already stopped the drag and should reparent the panel
+    // 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 .
+    child->SetDefaultParentByRootWindow(
+        child->GetRootWindow(),
+        child->GetRootWindow()->GetBoundsInScreen());
+    DCHECK(child->parent()->id() != kShellWindowId_PanelContainer);
+    return;
+  }
   PanelInfo panel_info;
   panel_info.window = child;
   panel_info.callout_widget = new PanelCalloutWidget(panel_container_);
diff --git a/ash/wm/panels/panel_layout_manager.h b/ash/wm/panels/panel_layout_manager.h
index 0a79824..04fa589 100644
--- a/ash/wm/panels/panel_layout_manager.h
+++ b/ash/wm/panels/panel_layout_manager.h
@@ -152,6 +152,8 @@
 
   // Parent window associated with this layout manager.
   aura::Window* panel_container_;
+  // Protect against recursive calls to OnWindowAddedToLayout().
+  bool in_add_window_;
   // Protect against recursive calls to Relayout().
   bool in_layout_;
   // Ordered list of unowned pointers to panel windows.
diff --git a/ash/wm/panels/panel_window_event_handler.cc b/ash/wm/panels/panel_window_event_handler.cc
new file mode 100644
index 0000000..2509196
--- /dev/null
+++ b/ash/wm/panels/panel_window_event_handler.cc
@@ -0,0 +1,49 @@
+// 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/panels/panel_window_event_handler.h"
+
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/events/event.h"
+#include "ui/base/hit_test.h"
+
+namespace ash {
+namespace internal {
+
+PanelWindowEventHandler::PanelWindowEventHandler(aura::Window* owner)
+    : ToplevelWindowEventHandler(owner) {
+}
+
+PanelWindowEventHandler::~PanelWindowEventHandler() {
+}
+
+void PanelWindowEventHandler::OnMouseEvent(ui::MouseEvent* event) {
+  aura::Window* target = static_cast<aura::Window*>(event->target());
+  if (event->type() == ui::ET_MOUSE_PRESSED &&
+      event->flags() & ui::EF_IS_DOUBLE_CLICK &&
+      target->delegate()->GetNonClientComponent(event->location()) ==
+          HTCAPTION) {
+    target->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+    return;
+  }
+  ToplevelWindowEventHandler::OnMouseEvent(event);
+}
+
+void PanelWindowEventHandler::OnGestureEvent(ui::GestureEvent* event) {
+  aura::Window* target = static_cast<aura::Window*>(event->target());
+  if (event->type() == ui::ET_GESTURE_TAP &&
+      event->details().tap_count() == 2 &&
+      target->delegate()->GetNonClientComponent(event->location()) ==
+          HTCAPTION) {
+    target->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+    event->StopPropagation();
+    return;
+  }
+  ToplevelWindowEventHandler::OnGestureEvent(event);
+}
+
+}  // namespace internal
+}  // namespace ash
diff --git a/ash/wm/panels/panel_window_event_handler.h b/ash/wm/panels/panel_window_event_handler.h
new file mode 100644
index 0000000..2823b88
--- /dev/null
+++ b/ash/wm/panels/panel_window_event_handler.h
@@ -0,0 +1,35 @@
+// 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_PANELS_PANEL_WINDOW_EVENT_HANDLER_H_
+#define ASH_WM_PANELS_PANEL_WINDOW_EVENT_HANDLER_H_
+
+#include "ash/wm/toplevel_window_event_handler.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+namespace internal {
+
+// PanelWindowEventHandler minimizes panels when the user double clicks or
+// double taps on the panel header.
+class PanelWindowEventHandler : public ToplevelWindowEventHandler {
+ public:
+  explicit PanelWindowEventHandler(aura::Window* owner);
+  virtual ~PanelWindowEventHandler();
+
+  // TopLevelWindowEventHandler:
+  virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PanelWindowEventHandler);
+};
+
+}  // namespace internal
+}  // namespace ash
+
+#endif  // ASH_WM_PANELS_PANEL_WINDOW_EVENT_HANDLER_H_
diff --git a/ash/wm/panels/panel_window_resizer.cc b/ash/wm/panels/panel_window_resizer.cc
index 1c054b4..7f14280 100644
--- a/ash/wm/panels/panel_window_resizer.cc
+++ b/ash/wm/panels/panel_window_resizer.cc
@@ -64,13 +64,11 @@
     did_move_or_resize_ = true;
     StartedDragging();
   }
-  gfx::Point location_in_screen = location;
-  wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen);
 
   // Check if the destination has changed displays.
   gfx::Screen* screen = Shell::GetScreen();
   const gfx::Display dst_display =
-      screen->GetDisplayNearestPoint(location_in_screen);
+      screen->GetDisplayNearestPoint(last_location_);
   if (dst_display.id() !=
       screen->GetDisplayNearestWindow(panel_container_->GetRootWindow()).id()) {
     // The panel is being dragged to a new display. If the previous container is
@@ -198,9 +196,11 @@
     // Attach the panel while dragging placing it in front of other panels.
     GetTarget()->SetProperty(internal::kContinueDragAfterReparent, true);
     GetTarget()->SetProperty(internal::kPanelAttachedKey, true);
+    // We use root window coordinates to ensure that during the drag the panel
+    // is reparented to a container in the root window that has that window.
     GetTarget()->SetDefaultParentByRootWindow(
         GetTarget()->GetRootWindow(),
-        GetTarget()->GetBoundsInScreen());
+        GetTarget()->GetRootWindow()->GetBoundsInScreen());
   }
 }
 
@@ -210,9 +210,11 @@
   if (GetTarget()->GetProperty(internal::kPanelAttachedKey) !=
       should_attach_) {
     GetTarget()->SetProperty(internal::kPanelAttachedKey, should_attach_);
-    gfx::Rect near_last_location(last_location_, gfx::Size());
-    GetTarget()->SetDefaultParentByRootWindow(GetTarget()->GetRootWindow(),
-                                              near_last_location);
+    // We use last known location to ensure that after the drag the panel
+    // is reparented to a container in the root window that has that location.
+    GetTarget()->SetDefaultParentByRootWindow(
+        GetTarget()->GetRootWindow(),
+        gfx::Rect(last_location_, gfx::Size()));
   }
   if (panel_container_)
     GetPanelLayoutManager(panel_container_)->FinishDragging();
diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc
index a0d493e..bc7465e 100644
--- a/ash/wm/panels/panel_window_resizer_unittest.cc
+++ b/ash/wm/panels/panel_window_resizer_unittest.cc
@@ -221,12 +221,18 @@
 // Verifies a window can be dragged from the panel and detached and then
 // reattached.
 TEST_F(PanelWindowResizerTest, PanelDetachReattachBottom) {
+ if (!SupportsHostWindowResize())
+    return;
+
   scoped_ptr<aura::Window> window(
       CreatePanelWindow(gfx::Rect(0, 0, 201, 201)));
   DetachReattachTest(window.get(), 0, -1);
 }
 
 TEST_F(PanelWindowResizerTest, PanelDetachReattachLeft) {
+ if (!SupportsHostWindowResize())
+    return;
+
   ash::Shell* shell = ash::Shell::GetInstance();
   shell->SetShelfAlignment(SHELF_ALIGNMENT_LEFT, shell->GetPrimaryRootWindow());
   scoped_ptr<aura::Window> window(
@@ -252,6 +258,9 @@
 }
 
 TEST_F(PanelWindowResizerTest, PanelDetachReattachTop) {
+ if (!SupportsHostWindowResize())
+    return;
+
   ash::Shell* shell = ash::Shell::GetInstance();
   shell->SetShelfAlignment(SHELF_ALIGNMENT_TOP, shell->GetPrimaryRootWindow());
   scoped_ptr<aura::Window> window(
@@ -432,6 +441,9 @@
 }
 
 TEST_P(PanelWindowResizerTextDirectionTest, DragReordersPanelsHorizontal) {
+  if (!SupportsHostWindowResize())
+    return;
+
   DragAlongShelfReorder(base::i18n::IsRTL() ? 1 : -1, 0);
 }
 
diff --git a/ash/wm/partial_screenshot_view.cc b/ash/wm/partial_screenshot_view.cc
index 032c91b..1b2fa66 100644
--- a/ash/wm/partial_screenshot_view.cc
+++ b/ash/wm/partial_screenshot_view.cc
@@ -112,7 +112,7 @@
   views::Widget* widget = new views::Widget;
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.delegate = this;
   // The partial screenshot rectangle has to be at the real top of
   // the screen.
diff --git a/ash/wm/power_button_controller_unittest.cc b/ash/wm/power_button_controller_unittest.cc
index 5ee1e27..e1ce71b 100644
--- a/ash/wm/power_button_controller_unittest.cc
+++ b/ash/wm/power_button_controller_unittest.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/wm/lock_state_controller.h"
-#include "ash/wm/power_button_controller.h"
-#include "ash/wm/session_state_animator.h"
-#include "ash/wm/session_state_controller_impl.h"
 #include "ash/ash_switches.h"
 #include "ash/session_state_delegate.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_shell_delegate.h"
+#include "ash/wm/lock_state_controller.h"
+#include "ash/wm/power_button_controller.h"
+#include "ash/wm/session_state_animator.h"
+#include "ash/wm/session_state_controller_impl.h"
 #include "base/command_line.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/env.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/event_generator.h"
diff --git a/ash/wm/resize_shadow.cc b/ash/wm/resize_shadow.cc
index 27ed2b7..cdf573d 100644
--- a/ash/wm/resize_shadow.cc
+++ b/ash/wm/resize_shadow.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/resize_shadow.h"
 
-#include "base/time.h"
+#include "base/time/time.h"
 #include "grit/ash_resources.h"
 #include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
diff --git a/ash/wm/root_window_layout_manager.cc b/ash/wm/root_window_layout_manager.cc
index 481fe3f..584bc06 100644
--- a/ash/wm/root_window_layout_manager.cc
+++ b/ash/wm/root_window_layout_manager.cc
@@ -5,7 +5,9 @@
 #include "ash/wm/root_window_layout_manager.h"
 
 #include "ash/desktop_background/desktop_background_widget_controller.h"
-#include "ui/aura/window.h"
+#include "ash/root_window_controller.h"
+#include "ash/wm/property_util.h"
+#include "ui/aura/root_window.h"
 #include "ui/compositor/layer.h"
 #include "ui/views/widget/widget.h"
 
@@ -39,10 +41,13 @@
     for (j = (*i)->children().begin(); j != (*i)->children().end(); ++j)
       (*j)->SetBounds(fullscreen_bounds);
   }
-  internal::DesktopBackgroundWidgetController* background =
-      owner_->GetProperty(kDesktopController);
-  if (!background && owner_->GetProperty(kAnimatingDesktopController)) {
-    background = owner_->GetProperty(kAnimatingDesktopController)->
+  RootWindowController* root_window_controller = GetRootWindowController(
+      static_cast<aura::RootWindow*>(owner_));
+  DesktopBackgroundWidgetController* background =
+      root_window_controller->wallpaper_controller();
+
+  if (!background && root_window_controller->animating_wallpaper_controller()) {
+    background = root_window_controller->animating_wallpaper_controller()->
         GetController(false);
   }
   if (background)
diff --git a/ash/wm/screen_dimmer.cc b/ash/wm/screen_dimmer.cc
index 814620a..101d69b 100644
--- a/ash/wm/screen_dimmer.cc
+++ b/ash/wm/screen_dimmer.cc
@@ -5,7 +5,7 @@
 #include "ash/wm/screen_dimmer.h"
 
 #include "ash/shell.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/root_window.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
diff --git a/ash/wm/session_state_animator.h b/ash/wm/session_state_animator.h
index cfd79d5..19ead2f 100644
--- a/ash/wm/session_state_animator.h
+++ b/ash/wm/session_state_animator.h
@@ -9,7 +9,7 @@
 #include "ash/wm/workspace/colored_window_controller.h"
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/aura/window.h"
 #include "ui/compositor/layer_animation_observer.h"
 
diff --git a/ash/wm/session_state_controller_impl.h b/ash/wm/session_state_controller_impl.h
index 79a5c2c..3ffc221 100644
--- a/ash/wm/session_state_controller_impl.h
+++ b/ash/wm/session_state_controller_impl.h
@@ -11,8 +11,8 @@
 #include "ash/wm/session_state_animator.h"
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "ui/aura/root_window_observer.h"
 
 namespace gfx {
diff --git a/ash/wm/stacking_controller.cc b/ash/wm/stacking_controller.cc
index 7b3337f..24f38d5 100644
--- a/ash/wm/stacking_controller.cc
+++ b/ash/wm/stacking_controller.cc
@@ -4,12 +4,13 @@
 
 #include "ash/wm/stacking_controller.h"
 
-#include "ash/display/display_controller.h"
+#include "ash/root_window_controller.h"
 #include "ash/session_state_delegate.h"
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"
 #include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/coordinate_conversion.h"
+#include "ash/wm/property_util.h"
 #include "ash/wm/window_properties.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/root_window.h"
@@ -53,6 +54,11 @@
   return window->GetProperty(internal::kPanelAttachedKey);
 }
 
+internal::AlwaysOnTopController*
+GetAlwaysOnTopController(aura::RootWindow* root_window) {
+  return GetRootWindowController(root_window)->always_on_top_controller();
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -145,20 +151,4 @@
   return container;
 }
 
-// TODO(oshima): Remove this once extended desktop is on by default.
-internal::AlwaysOnTopController*
-StackingController::GetAlwaysOnTopController(aura::RootWindow* root_window) {
-  internal::AlwaysOnTopController* controller =
-      root_window->GetProperty(internal::kAlwaysOnTopControllerKey);
-  if (!controller) {
-    controller = new internal::AlwaysOnTopController;
-    controller->SetAlwaysOnTopContainer(
-        root_window->GetChildById(
-            internal::kShellWindowId_AlwaysOnTopContainer));
-    // RootWindow owns the AlwaysOnTopController object.
-    root_window->SetProperty(internal::kAlwaysOnTopControllerKey, controller);
-  }
-  return controller;
-}
-
 }  // namespace ash
diff --git a/ash/wm/stacking_controller.h b/ash/wm/stacking_controller.h
index 21611ed..07003cb 100644
--- a/ash/wm/stacking_controller.h
+++ b/ash/wm/stacking_controller.h
@@ -40,10 +40,6 @@
   aura::Window* GetSystemModalContainer(aura::RootWindow* root,
                                         aura::Window* window) const;
 
-  // Returns the AlwaysOnTopController of the |root_window|.
-  internal::AlwaysOnTopController* GetAlwaysOnTopController(
-      aura::RootWindow* root_window);
-
   DISALLOW_COPY_AND_ASSIGN(StackingController);
 };
 
diff --git a/ash/wm/system_gesture_event_filter.cc b/ash/wm/system_gesture_event_filter.cc
index 029ab72..b368822 100644
--- a/ash/wm/system_gesture_event_filter.cc
+++ b/ash/wm/system_gesture_event_filter.cc
@@ -52,8 +52,7 @@
 }
 
 void SystemGestureEventFilter::OnMouseEvent(ui::MouseEvent* event) {
-// TODO(rjkroege): Remove USE_MESSAGEPUMP_LINUX guard once mice are supported.
-#if defined(OS_CHROMEOS) && !defined(USE_MESSAGEPUMP_LINUX)
+#if defined(OS_CHROMEOS) && !defined(USE_OZONE)
   if (event->type() == ui::ET_MOUSE_PRESSED && event->native_event() &&
       ui::TouchFactory::GetInstance()->IsTouchDevicePresent() &&
       Shell::GetInstance()->delegate()) {
@@ -65,13 +64,13 @@
 
 void SystemGestureEventFilter::OnTouchEvent(ui::TouchEvent* event) {
   aura::Window* target = static_cast<aura::Window*>(event->target());
-  touch_uma_.RecordTouchEvent(target, *event);
+  ash::TouchUMA::GetInstance()->RecordTouchEvent(target, *event);
   long_press_affordance_->ProcessEvent(target, event, event->touch_id());
 }
 
 void SystemGestureEventFilter::OnGestureEvent(ui::GestureEvent* event) {
   aura::Window* target = static_cast<aura::Window*>(event->target());
-  touch_uma_.RecordGestureEvent(target, *event);
+  ash::TouchUMA::GetInstance()->RecordGestureEvent(target, *event);
   long_press_affordance_->ProcessEvent(target, event,
       event->GetLowestTouchId());
 
diff --git a/ash/wm/system_gesture_event_filter.h b/ash/wm/system_gesture_event_filter.h
index 182ed5a..a2a5c73 100644
--- a/ash/wm/system_gesture_event_filter.h
+++ b/ash/wm/system_gesture_event_filter.h
@@ -7,7 +7,7 @@
 
 #include "ash/shell.h"
 #include "ash/touch/touch_uma.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/animation/animation_delegate.h"
 #include "ui/base/animation/linear_animation.h"
@@ -69,8 +69,6 @@
   scoped_ptr<LongPressAffordanceHandler> long_press_affordance_;
   scoped_ptr<TwoFingerDragHandler> two_finger_drag_;
 
-  TouchUMA touch_uma_;
-
   DISALLOW_COPY_AND_ASSIGN(SystemGestureEventFilter);
 };
 
diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc
index ce02fad..2238d47 100644
--- a/ash/wm/system_gesture_event_filter_unittest.cc
+++ b/ash/wm/system_gesture_event_filter_unittest.cc
@@ -20,8 +20,8 @@
 #include "ash/wm/gestures/long_press_affordance_handler.h"
 #include "ash/wm/window_util.h"
 #include "base/command_line.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "ui/aura/root_window.h"
 #include "ui/aura/test/event_generator.h"
 #include "ui/aura/test/test_windows.h"
diff --git a/ash/wm/user_activity_detector.h b/ash/wm/user_activity_detector.h
index a15b0d4..0a4ad48 100644
--- a/ash/wm/user_activity_detector.h
+++ b/ash/wm/user_activity_detector.h
@@ -9,7 +9,7 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/observer_list.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/base/events/event_handler.h"
 
 namespace ash {
diff --git a/ash/wm/user_activity_detector_unittest.cc b/ash/wm/user_activity_detector_unittest.cc
index 66306f7..75af6cc 100644
--- a/ash/wm/user_activity_detector_unittest.cc
+++ b/ash/wm/user_activity_detector_unittest.cc
@@ -9,7 +9,7 @@
 #include "ash/wm/user_activity_observer.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
 #include "ui/base/events/event.h"
diff --git a/ash/wm/video_detector.h b/ash/wm/video_detector.h
index 81fd2da..7a2b112 100644
--- a/ash/wm/video_detector.h
+++ b/ash/wm/video_detector.h
@@ -14,7 +14,7 @@
 #include "base/memory/linked_ptr.h"
 #include "base/observer_list.h"
 #include "base/scoped_observer.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/env_observer.h"
 #include "ui/aura/window_observer.h"
 
diff --git a/ash/wm/video_detector_unittest.cc b/ash/wm/video_detector_unittest.cc
index 187742c..96e0787 100644
--- a/ash/wm/video_detector_unittest.cc
+++ b/ash/wm/video_detector_unittest.cc
@@ -9,7 +9,7 @@
 #include "ash/wm/window_util.h"
 #include "base/compiler_specific.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_types.h"
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc
index f490a67..bd17770 100644
--- a/ash/wm/window_animations.cc
+++ b/ash/wm/window_animations.cc
@@ -13,14 +13,14 @@
 #include "ash/screen_ash.h"
 #include "ash/shell.h"
 #include "ash/wm/window_util.h"
-#include "ash/wm/workspace_controller.h"
 #include "ash/wm/workspace/workspace_animations.h"
+#include "ash/wm/workspace_controller.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/message_loop.h"
 #include "base/stl_util.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
@@ -396,13 +396,22 @@
 }
 
 void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) {
-  DCHECK(window->TargetVisibility());
+  // Some test results in invoking CrossFadeToBounds when window is not visible.
+  // No animation is necessary in that case, thus just change the bounds and
+  // quit.
+  if (!window->TargetVisibility()) {
+    window->SetBounds(new_bounds);
+    return;
+  }
+
   const gfx::Rect old_bounds = window->bounds();
 
   // Create fresh layers for the window and all its children to paint into.
   // Takes ownership of the old layer and all its children, which will be
   // cleaned up after the animation completes.
-  ui::Layer* old_layer = views::corewm::RecreateWindowLayers(window, false);
+  // Specify |set_bounds| to true here to keep the old bounds in the child
+  // windows of |window|.
+  ui::Layer* old_layer = views::corewm::RecreateWindowLayers(window, true);
   ui::Layer* new_layer = window->layer();
 
   // Resize the window to the new size, which will force a layout and paint.
diff --git a/ash/wm/window_animations_unittest.cc b/ash/wm/window_animations_unittest.cc
index a8646b6..d5680cc 100644
--- a/ash/wm/window_animations_unittest.cc
+++ b/ash/wm/window_animations_unittest.cc
@@ -7,7 +7,7 @@
 #include "ash/shell_window_ids.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/workspace_controller.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window.h"
 #include "ui/base/animation/animation_container_element.h"
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc
index e639258..8e74543 100644
--- a/ash/wm/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -6,8 +6,6 @@
 
 #include <algorithm>
 
-#include "ash/display/display_controller.h"
-#include "ash/display/display_manager.h"
 #include "ash/session_state_delegate.h"
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
index 7ea875b..fd39d0f 100644
--- a/ash/wm/window_cycle_list.cc
+++ b/ash/wm/window_cycle_list.cc
@@ -6,6 +6,7 @@
 
 #include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
+#include "ui/views/corewm/window_animations.h"
 
 namespace ash {
 
@@ -41,8 +42,12 @@
     // provided window list.  Just switch to the first (or last) one.
     current_index_ = (direction == FORWARD ? 0 : windows_.size() - 1);
   } else {
-    if (windows_.size() == 1)
+    // When there is only one window, we should give a feedback to user.
+    if (windows_.size() == 1) {
+      AnimateWindow(windows_[0],
+                    views::corewm::WINDOW_ANIMATION_TYPE_BOUNCE);
       return;
+    }
     // We're in a valid cycle, so step forward or backward.
     current_index_ += (direction == FORWARD ? 1 : -1);
   }
diff --git a/ash/wm/window_properties.cc b/ash/wm/window_properties.cc
index 64d55a7..9dda017 100644
--- a/ash/wm/window_properties.cc
+++ b/ash/wm/window_properties.cc
@@ -5,21 +5,16 @@
 #include "ash/wm/window_properties.h"
 
 #include "ash/root_window_controller.h"
-#include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/frame_painter.h"
 #include "ui/aura/window_property.h"
 #include "ui/gfx/rect.h"
 
-DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::AlwaysOnTopController*);
 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(ASH_EXPORT, ash::FramePainter*);
 DECLARE_WINDOW_PROPERTY_TYPE(ash::WindowPersistsAcrossAllWorkspacesType)
 DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::RootWindowController*);
 
 namespace ash {
 namespace internal {
-DEFINE_OWNED_WINDOW_PROPERTY_KEY(ash::internal::AlwaysOnTopController,
-                                 kAlwaysOnTopControllerKey,
-                                 NULL);
 DEFINE_WINDOW_PROPERTY_KEY(bool, kContinueDragAfterReparent, false);
 DEFINE_WINDOW_PROPERTY_KEY(bool, kCyclingThroughWorkspacesKey, false);
 DEFINE_WINDOW_PROPERTY_KEY(bool, kFullscreenUsesMinimalChromeKey, false);
diff --git a/ash/wm/window_properties.h b/ash/wm/window_properties.h
index ae30435..c5bba1f 100644
--- a/ash/wm/window_properties.h
+++ b/ash/wm/window_properties.h
@@ -17,18 +17,12 @@
 namespace ash {
 class FramePainter;
 namespace internal {
-class AlwaysOnTopController;
 class RootWindowController;
 
 // Shell-specific window property keys.
 
 // Alphabetical sort.
 
-// A Key to store AlwaysOnTopController per RootWindow. The value is
-// owned by the RootWindow.
-extern const aura::WindowProperty<internal::AlwaysOnTopController*>* const
-    kAlwaysOnTopControllerKey;
-
 // A property key to indicate that an in progress drag should be continued
 // after the window is reparented to another container.
 extern const aura::WindowProperty<bool>* const kContinueDragAfterReparent;
diff --git a/ash/wm/workspace/desktop_background_fade_controller.cc b/ash/wm/workspace/desktop_background_fade_controller.cc
index 68628b88..563d07b 100644
--- a/ash/wm/workspace/desktop_background_fade_controller.cc
+++ b/ash/wm/workspace/desktop_background_fade_controller.cc
@@ -6,7 +6,7 @@
 
 #include "ash/wm/window_animations.h"
 #include "ash/wm/workspace/colored_window_controller.h"
-#include "base/time.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"
diff --git a/ash/wm/workspace/frame_maximize_button.cc b/ash/wm/workspace/frame_maximize_button.cc
index 0329e3f..23ca750 100644
--- a/ash/wm/workspace/frame_maximize_button.cc
+++ b/ash/wm/workspace/frame_maximize_button.cc
@@ -9,6 +9,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/touch/touch_uma.h"
 #include "ash/wm/maximize_bubble_controller.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_properties.h"
@@ -279,8 +280,11 @@
     // for TAP and SCROLL_END). So it is necessary to update the snap-state for
     // the current event.
     ProcessUpdateEvent(*event);
-    if (event->type() == ui::ET_GESTURE_TAP)
+    if (event->type() == ui::ET_GESTURE_TAP) {
       snap_type_ = SnapTypeForLocation(event->location());
+      TouchUMA::GetInstance()->RecordGestureAction(
+          TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
+    }
     ProcessEndEvent(*event);
     event->SetHandled();
     return;
diff --git a/ash/wm/workspace/frame_maximize_button.h b/ash/wm/workspace/frame_maximize_button.h
index 6429aed..f01df6c 100644
--- a/ash/wm/workspace/frame_maximize_button.h
+++ b/ash/wm/workspace/frame_maximize_button.h
@@ -9,7 +9,7 @@
 #include "ash/wm/workspace/maximize_bubble_frame_state.h"
 #include "ash/wm/workspace/snap_types.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/timer.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"
diff --git a/ash/wm/workspace/multi_window_resize_controller.cc b/ash/wm/workspace/multi_window_resize_controller.cc
index 8880e33..c447b9c 100644
--- a/ash/wm/workspace/multi_window_resize_controller.cc
+++ b/ash/wm/workspace/multi_window_resize_controller.cc
@@ -384,7 +384,7 @@
   show_timer_.Stop();
   resize_widget_.reset(new views::Widget);
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.parent = Shell::GetContainer(
       Shell::GetActiveRootWindow(),
diff --git a/ash/wm/workspace/multi_window_resize_controller.h b/ash/wm/workspace/multi_window_resize_controller.h
index 85f7c27..02eeb25 100644
--- a/ash/wm/workspace/multi_window_resize_controller.h
+++ b/ash/wm/workspace/multi_window_resize_controller.h
@@ -10,7 +10,7 @@
 #include "ash/ash_export.h"
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/timer.h"
+#include "base/timer/timer.h"
 #include "ui/aura/window_observer.h"
 #include "ui/gfx/rect.h"
 #include "ui/views/mouse_watcher.h"
diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc
index 8cdbd0c..4287d5b 100644
--- a/ash/wm/workspace/phantom_window_controller.cc
+++ b/ash/wm/workspace/phantom_window_controller.cc
@@ -137,7 +137,7 @@
   DCHECK(!phantom_widget_);
   phantom_widget_ = new views::Widget;
   views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
-  params.transparent = true;
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
   // PhantomWindowController is used by FrameMaximizeButton to highlight the
   // launcher button. Put the phantom in the same window as the launcher so that
   // the phantom is visible.
diff --git a/ash/wm/workspace/snap_sizer.h b/ash/wm/workspace/snap_sizer.h
index b283258..bd5f719 100644
--- a/ash/wm/workspace/snap_sizer.h
+++ b/ash/wm/workspace/snap_sizer.h
@@ -9,7 +9,7 @@
 
 #include "ash/ash_export.h"
 #include "base/basictypes.h"
-#include "base/time.h"
+#include "base/time/time.h"
 #include "ui/gfx/rect.h"
 
 namespace aura {
diff --git a/ash/wm/workspace/workspace_animations.h b/ash/wm/workspace/workspace_animations.h
index 3e60c1b..39d84a2 100644
--- a/ash/wm/workspace/workspace_animations.h
+++ b/ash/wm/workspace/workspace_animations.h
@@ -6,7 +6,7 @@
 #define ASH_WM_WORKSPACE_WORKSPACE_ANIMATIONS_H_
 
 #include "ash/ash_export.h"
-#include "base/time.h"
+#include "base/time/time.h"
 
 namespace aura {
 class Window;
diff --git a/ash/wm/workspace/workspace_event_handler.cc b/ash/wm/workspace/workspace_event_handler.cc
index 828f024..6bf384c 100644
--- a/ash/wm/workspace/workspace_event_handler.cc
+++ b/ash/wm/workspace/workspace_event_handler.cc
@@ -7,6 +7,7 @@
 #include "ash/screen_ash.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
+#include "ash/touch/touch_uma.h"
 #include "ash/wm/coordinate_conversion.h"
 #include "ash/wm/property_util.h"
 #include "ash/wm/window_util.h"
@@ -119,14 +120,23 @@
 void WorkspaceEventHandler::OnGestureEvent(ui::GestureEvent* event) {
   aura::Window* target = static_cast<aura::Window*>(event->target());
   if (event->type() == ui::ET_GESTURE_TAP &&
-      event->details().tap_count() == 2 &&
       target->delegate()->GetNonClientComponent(event->location()) ==
       HTCAPTION) {
-    ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
-        ash::UMA_TOGGLE_MAXIMIZE_CAPTION_GESTURE);
-    ToggleMaximizedState(target);  // |this| may be destroyed from here.
-    event->StopPropagation();
-    return;
+    if (event->details().tap_count() == 2) {
+      ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
+          ash::UMA_TOGGLE_MAXIMIZE_CAPTION_GESTURE);
+      // Note: TouchUMA::GESTURE_FRAMEVIEW_TAP is counted twice each time
+      // TouchUMA::GESTURE_MAXIMIZE_DOUBLETAP is counted once.
+      TouchUMA::GetInstance()->RecordGestureAction(
+          TouchUMA::GESTURE_MAXIMIZE_DOUBLETAP);
+      ToggleMaximizedState(target);  // |this| may be destroyed from here.
+      event->StopPropagation();
+      return;
+    } else {
+      // Note: TouchUMA::GESTURE_FRAMEVIEW_TAP is counted twice for each tap.
+      TouchUMA::GetInstance()->RecordGestureAction(
+          TouchUMA::GESTURE_FRAMEVIEW_TAP);
+    }
   }
   ToplevelWindowEventHandler::OnGestureEvent(event);
 }
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index 6e9feca..de32a1d 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -5,11 +5,13 @@
 #include "ash/wm/workspace/workspace_layout_manager.h"
 
 #include "ash/ash_switches.h"
+#include "ash/root_window_controller.h"
 #include "ash/screen_ash.h"
 #include "ash/session_state_delegate.h"
 #include "ash/shell.h"
 #include "ash/wm/always_on_top_controller.h"
 #include "ash/wm/base_layout_manager.h"
+#include "ash/wm/property_util.h"
 #include "ash/wm/window_animations.h"
 #include "ash/wm/window_properties.h"
 #include "ash/wm/window_util.h"
@@ -230,18 +232,15 @@
   if (key == internal::kWindowTrackedByWorkspaceKey &&
       GetTrackedByWorkspace(window)) {
     workspace_manager()->OnTrackedByWorkspaceChanged(workspace_, window);
-    if (wm::IsWindowMaximized(window)) {
-      SetChildBoundsDirect(
-          window, ScreenAsh::GetMaximizedWindowBoundsInParent(
-              window->parent()->parent()));
-    }
+    if (wm::IsWindowMaximized(window))
+      SetMaximizedOrFullscreenBounds(window);
   }
 
   if (key == aura::client::kAlwaysOnTopKey &&
       window->GetProperty(aura::client::kAlwaysOnTopKey)) {
     internal::AlwaysOnTopController* controller =
-        window->GetRootWindow()->GetProperty(
-            internal::kAlwaysOnTopControllerKey);
+        GetRootWindowController(window->GetRootWindow())->
+            always_on_top_controller();
     controller->GetContainer(window)->AddChild(window);
   }
 }
@@ -355,7 +354,7 @@
         gfx::Rect bounds_in_parent =
             ScreenAsh::ConvertRectFromScreen(window->parent()->parent(),
                                              *restore);
-        SetChildBoundsDirect(
+        CrossFadeToBounds(
             window,
             BaseLayoutManager::BoundsWithScreenEdgeVisible(
                 window->parent()->parent(),
@@ -366,6 +365,9 @@
     }
 
     case ui::SHOW_STATE_MAXIMIZED:
+      CrossFadeToBounds(window, ScreenAsh::GetMaximizedWindowBoundsInParent(
+          window->parent()->parent()));
+      break;
     case ui::SHOW_STATE_FULLSCREEN:
       SetMaximizedOrFullscreenBounds(window);
       break;
diff --git a/ash/wm/workspace/workspace_manager.cc b/ash/wm/workspace/workspace_manager.cc
index b23f5c4..a060df2 100644
--- a/ash/wm/workspace/workspace_manager.cc
+++ b/ash/wm/workspace/workspace_manager.cc
@@ -368,12 +368,12 @@
     contents_window_->StackChildAtTop(last_active->window());
   }
 
-  UpdateShelfVisibility();
-
   // NOTE: duration supplied to this method is only used for desktop background.
   HideWorkspace(last_active, reason, is_unminimizing_fullscreen_window);
   ShowWorkspace(workspace, last_active, reason);
 
+  UpdateShelfVisibility();
+
   RootWindowController* root_controller = GetRootWindowController(
       contents_window_->GetRootWindow());
   if (root_controller) {
diff --git a/ash/wm/workspace/workspace_manager.h b/ash/wm/workspace/workspace_manager.h
index f070e62..b7ef571 100644
--- a/ash/wm/workspace/workspace_manager.h
+++ b/ash/wm/workspace/workspace_manager.h
@@ -15,8 +15,8 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
-#include "base/time.h"
-#include "base/timer.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "ui/base/ui_base_types.h"
 
 namespace aura {
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 4472815..cf2d8b0 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "ash/ash_switches.h"
-#include "ash/display/display_controller.h"
 #include "ash/screen_ash.h"
 #include "ash/shell.h"
 #include "ash/shell_window_ids.h"