accelerator_controller.cc revision 58537e28ecd584eab876aee8be7156509866d23a
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ash/accelerators/accelerator_controller.h"
6
7#include <algorithm>
8#include <cmath>
9#include <iostream>
10#include <string>
11
12#include "ash/accelerators/accelerator_commands.h"
13#include "ash/accelerators/accelerator_table.h"
14#include "ash/ash_switches.h"
15#include "ash/caps_lock_delegate.h"
16#include "ash/debug.h"
17#include "ash/desktop_background/desktop_background_controller.h"
18#include "ash/desktop_background/user_wallpaper_delegate.h"
19#include "ash/display/display_controller.h"
20#include "ash/display/display_manager.h"
21#include "ash/focus_cycler.h"
22#include "ash/ime_control_delegate.h"
23#include "ash/launcher/launcher.h"
24#include "ash/launcher/launcher_delegate.h"
25#include "ash/launcher/launcher_model.h"
26#include "ash/magnifier/magnification_controller.h"
27#include "ash/magnifier/partial_magnification_controller.h"
28#include "ash/root_window_controller.h"
29#include "ash/rotator/screen_rotation.h"
30#include "ash/screenshot_delegate.h"
31#include "ash/session_state_delegate.h"
32#include "ash/shelf/shelf_widget.h"
33#include "ash/shell.h"
34#include "ash/shell_delegate.h"
35#include "ash/shell_window_ids.h"
36#include "ash/system/brightness/brightness_control_delegate.h"
37#include "ash/system/keyboard_brightness/keyboard_brightness_control_delegate.h"
38#include "ash/system/status_area_widget.h"
39#include "ash/system/tray/system_tray.h"
40#include "ash/system/tray/system_tray_delegate.h"
41#include "ash/system/tray/system_tray_notifier.h"
42#include "ash/system/web_notification/web_notification_tray.h"
43#include "ash/touch/touch_hud_debug.h"
44#include "ash/volume_control_delegate.h"
45#include "ash/wm/overview/window_selector_controller.h"
46#include "ash/wm/partial_screenshot_view.h"
47#include "ash/wm/power_button_controller.h"
48#include "ash/wm/property_util.h"
49#include "ash/wm/window_cycle_controller.h"
50#include "ash/wm/window_util.h"
51#include "ash/wm/workspace/snap_sizer.h"
52#include "base/bind.h"
53#include "base/command_line.h"
54#include "content/public/browser/gpu_data_manager.h"
55#include "ui/aura/env.h"
56#include "ui/aura/root_window.h"
57#include "ui/base/accelerators/accelerator.h"
58#include "ui/base/accelerators/accelerator_manager.h"
59#include "ui/base/events/event.h"
60#include "ui/base/keycodes/keyboard_codes.h"
61#include "ui/compositor/debug_utils.h"
62#include "ui/compositor/layer.h"
63#include "ui/compositor/layer_animation_sequence.h"
64#include "ui/compositor/layer_animator.h"
65#include "ui/gfx/screen.h"
66#include "ui/oak/oak.h"
67#include "ui/views/controls/webview/webview.h"
68#include "ui/views/debug_utils.h"
69#include "ui/views/widget/widget.h"
70
71#if defined(OS_CHROMEOS)
72#include "ash/system/chromeos/keyboard_brightness_controller.h"
73#include "base/chromeos/chromeos_version.h"
74#endif  // defined(OS_CHROMEOS)
75
76namespace ash {
77namespace {
78
79using internal::DisplayInfo;
80
81bool DebugShortcutsEnabled() {
82#if defined(NDEBUG)
83  return CommandLine::ForCurrentProcess()->HasSwitch(
84          switches::kAshDebugShortcuts);
85#else
86  return true;
87#endif
88}
89
90bool OverviewEnabled() {
91  return !CommandLine::ForCurrentProcess()->HasSwitch(
92      switches::kAshDisableOverviewMode);
93}
94
95void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
96  Shell* shell = Shell::GetInstance();
97
98  if (accelerator.key_code() == ui::VKEY_TAB)
99    shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_PREVWINDOW_TAB);
100
101  if (OverviewEnabled()) {
102    shell->window_selector_controller()->HandleCycleWindow(
103        WindowSelector::BACKWARD);
104    return;
105  }
106  shell->window_cycle_controller()->HandleCycleWindow(
107      WindowCycleController::BACKWARD, accelerator.IsAltDown());
108}
109
110void HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
111  Shell* shell = Shell::GetInstance();
112
113  if (accelerator.key_code() == ui::VKEY_TAB)
114    shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEXTWINDOW_TAB);
115
116  if (OverviewEnabled()) {
117    shell->window_selector_controller()->HandleCycleWindow(
118        WindowSelector::FORWARD);
119    return;
120  }
121  shell->window_cycle_controller()->HandleCycleWindow(
122      WindowCycleController::FORWARD, accelerator.IsAltDown());
123}
124
125void HandleCycleLinear(const ui::Accelerator& accelerator) {
126  Shell* shell = Shell::GetInstance();
127
128  // TODO(jamescook): When overview becomes the default the AcceleratorAction
129  // should be renamed from CYCLE_LINEAR to TOGGLE_OVERVIEW.
130  if (OverviewEnabled()) {
131    shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_OVERVIEW_F5);
132    shell->window_selector_controller()->ToggleOverview();
133    return;
134  }
135  if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1)
136    shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEXTWINDOW_F5);
137  shell->window_cycle_controller()->HandleLinearCycleWindow();
138}
139
140bool HandleAccessibleFocusCycle(bool reverse) {
141  if (!Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled())
142    return false;
143  aura::Window* active_window = ash::wm::GetActiveWindow();
144  if (!active_window)
145    return false;
146  views::Widget* widget =
147      views::Widget::GetWidgetForNativeWindow(active_window);
148  if (!widget)
149    return false;
150  views::FocusManager* focus_manager = widget->GetFocusManager();
151  if (!focus_manager)
152    return false;
153  views::View* view = focus_manager->GetFocusedView();
154  if (!view)
155    return false;
156  if (!strcmp(view->GetClassName(), views::WebView::kViewClassName))
157    return false;
158
159  focus_manager->AdvanceFocus(reverse);
160  return true;
161}
162
163void HandleSilenceSpokenFeedback() {
164  if (!Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled())
165    return;
166
167  Shell::GetInstance()->delegate()->SilenceSpokenFeedback();
168}
169
170#if defined(OS_CHROMEOS)
171bool HandleLock() {
172  Shell::GetInstance()->session_state_delegate()->LockScreen();
173  return true;
174}
175
176bool HandleFileManager(bool as_dialog) {
177  Shell::GetInstance()->delegate()->OpenFileManager(as_dialog);
178  return true;
179}
180
181bool HandleCrosh() {
182  Shell::GetInstance()->delegate()->OpenCrosh();
183  return true;
184}
185
186bool HandleToggleSpokenFeedback() {
187  Shell::GetInstance()->delegate()->
188      ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
189  return true;
190}
191
192#endif  // defined(OS_CHROMEOS)
193
194bool HandleRotatePaneFocus(Shell::Direction direction) {
195  Shell* shell = Shell::GetInstance();
196  switch (direction) {
197    case Shell::FORWARD:
198      shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
199      break;
200    case Shell::BACKWARD:
201      shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
202      break;
203  }
204  return true;
205}
206
207// Rotate the active window.
208bool HandleRotateActiveWindow() {
209  aura::Window* active_window = wm::GetActiveWindow();
210  if (active_window) {
211    // The rotation animation bases its target transform on the current
212    // rotation and position. Since there could be an animation in progress
213    // right now, queue this animation so when it starts it picks up a neutral
214    // rotation and position. Use replace so we only enqueue one at a time.
215    active_window->layer()->GetAnimator()->
216        set_preemption_strategy(ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
217    active_window->layer()->GetAnimator()->StartAnimation(
218        new ui::LayerAnimationSequence(
219            new ash::ScreenRotation(360, active_window->layer())));
220  }
221  return true;
222}
223
224gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) {
225  switch (current) {
226    case gfx::Display::ROTATE_0:
227      return gfx::Display::ROTATE_90;
228    case gfx::Display::ROTATE_90:
229      return gfx::Display::ROTATE_180;
230    case gfx::Display::ROTATE_180:
231      return gfx::Display::ROTATE_270;
232    case gfx::Display::ROTATE_270:
233      return gfx::Display::ROTATE_0;
234  }
235  NOTREACHED() << "Unknown rotation:" << current;
236  return gfx::Display::ROTATE_0;
237}
238
239bool HandleScaleUI(bool up) {
240  internal::DisplayManager* display_manager =
241      Shell::GetInstance()->display_manager();
242  int64 display_id = display_manager->GetDisplayIdForUIScaling();
243  if (display_id == gfx::Display::kInvalidDisplayID)
244    return false;
245  const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id);
246  float next_scale =
247      internal::DisplayManager::GetNextUIScale(display_info, up);
248  display_manager->SetDisplayUIScale(display_id, next_scale);
249  return true;
250}
251
252bool HandleScaleReset() {
253  internal::DisplayManager* display_manager =
254      Shell::GetInstance()->display_manager();
255  int64 display_id = display_manager->GetDisplayIdForUIScaling();
256  if (display_id == gfx::Display::kInvalidDisplayID)
257    return false;
258  display_manager->SetDisplayUIScale(display_id, 1.0f);
259  return true;
260}
261
262// Rotates the screen.
263bool HandleRotateScreen() {
264  gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
265  gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point);
266  const DisplayInfo& display_info =
267      Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
268  Shell::GetInstance()->display_manager()->SetDisplayRotation(
269      display.id(), GetNextRotation(display_info.rotation()));
270  return true;
271}
272
273bool HandleToggleDesktopBackgroundMode() {
274  DesktopBackgroundController* desktop_background_controller =
275      Shell::GetInstance()->desktop_background_controller();
276  if (desktop_background_controller->desktop_background_mode() ==
277      DesktopBackgroundController::BACKGROUND_IMAGE) {
278    desktop_background_controller->SetDesktopBackgroundSolidColorMode(
279        SK_ColorBLACK);
280  } else {
281    ash::Shell::GetInstance()->user_wallpaper_delegate()->
282        InitializeWallpaper();
283  }
284  return true;
285}
286
287bool HandleToggleRootWindowFullScreen() {
288  Shell::GetPrimaryRootWindow()->ToggleFullScreen();
289  return true;
290}
291
292// Magnify the screen
293bool HandleMagnifyScreen(int delta_index) {
294  if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) {
295    // TODO(yoshiki): Move the following logic to MagnificationController.
296    float scale =
297        ash::Shell::GetInstance()->magnification_controller()->GetScale();
298    // Calculate rounded logarithm (base kMagnificationScaleFactor) of scale.
299    int scale_index =
300        std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5);
301
302    int new_scale_index = std::max(0, std::min(8, scale_index + delta_index));
303
304    ash::Shell::GetInstance()->magnification_controller()->
305        SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true);
306  } else if (ash::Shell::GetInstance()->
307             partial_magnification_controller()->is_enabled()) {
308    float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1;
309    ash::Shell::GetInstance()->partial_magnification_controller()->
310        SetScale(scale);
311  }
312
313  return true;
314}
315
316bool HandleMediaNextTrack() {
317  Shell::GetInstance()->delegate()->HandleMediaNextTrack();
318  return true;
319}
320
321bool HandleMediaPlayPause() {
322  Shell::GetInstance()->delegate()->HandleMediaPlayPause();
323  return true;
324}
325
326bool HandleMediaPrevTrack() {
327  Shell::GetInstance()->delegate()->HandleMediaPrevTrack();
328  return true;
329}
330
331bool HandlePrintLayerHierarchy() {
332  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
333  for (size_t i = 0; i < root_windows.size(); ++i) {
334    ui::PrintLayerHierarchy(root_windows[i]->layer(),
335                            root_windows[i]->GetLastMouseLocationInRoot());
336  }
337  return true;
338}
339
340bool HandlePrintViewHierarchy() {
341  aura::Window* active_window = ash::wm::GetActiveWindow();
342  if (!active_window)
343    return true;
344  views::Widget* browser_widget =
345      views::Widget::GetWidgetForNativeWindow(active_window);
346  if (!browser_widget)
347    return true;
348  views::PrintViewHierarchy(browser_widget->GetRootView());
349  return true;
350}
351
352void PrintWindowHierarchy(aura::Window* window,
353                          int indent,
354                          std::ostringstream* out) {
355  std::string indent_str(indent, ' ');
356  std::string name(window->name());
357  if (name.empty())
358    name = "\"\"";
359  *out << indent_str << name << " (" << window << ")"
360       << " type=" << window->type()
361       << (wm::IsActiveWindow(window) ? " [active] " : " ")
362       << (window->IsVisible() ? " visible " : " ")
363       << window->bounds().ToString()
364       << '\n';
365
366  for (size_t i = 0; i < window->children().size(); ++i)
367    PrintWindowHierarchy(window->children()[i], indent + 3, out);
368}
369
370bool HandlePrintWindowHierarchy() {
371  Shell::RootWindowControllerList controllers =
372      Shell::GetAllRootWindowControllers();
373  for (size_t i = 0; i < controllers.size(); ++i) {
374    std::ostringstream out;
375    out << "RootWindow " << i << ":\n";
376    PrintWindowHierarchy(controllers[i]->root_window(), 0, &out);
377    // Error so logs can be collected from end-users.
378    LOG(ERROR) << out.str();
379  }
380  return true;
381}
382
383bool HandlePrintUIHierarchies() {
384  // This is a separate command so the user only has to hit one key to generate
385  // all the logs. Developers use the individual dumps repeatedly, so keep
386  // those as separate commands to avoid spamming their logs.
387  HandlePrintLayerHierarchy();
388  HandlePrintWindowHierarchy();
389  HandlePrintViewHierarchy();
390  return true;
391}
392
393}  // namespace
394
395////////////////////////////////////////////////////////////////////////////////
396// AcceleratorControllerContext, public:
397
398AcceleratorControllerContext::AcceleratorControllerContext() {
399  current_accelerator_.set_type(ui::ET_UNKNOWN);
400  previous_accelerator_.set_type(ui::ET_UNKNOWN);
401}
402
403void AcceleratorControllerContext::UpdateContext(
404    const ui::Accelerator& accelerator) {
405  previous_accelerator_ = current_accelerator_;
406  current_accelerator_ = accelerator;
407}
408
409////////////////////////////////////////////////////////////////////////////////
410// AcceleratorController, public:
411
412AcceleratorController::AcceleratorController()
413    : accelerator_manager_(new ui::AcceleratorManager) {
414  Init();
415}
416
417AcceleratorController::~AcceleratorController() {
418}
419
420void AcceleratorController::Init() {
421  for (size_t i = 0; i < kActionsAllowedAtLoginOrLockScreenLength; ++i) {
422    actions_allowed_at_login_screen_.insert(
423        kActionsAllowedAtLoginOrLockScreen[i]);
424    actions_allowed_at_lock_screen_.insert(
425        kActionsAllowedAtLoginOrLockScreen[i]);
426  }
427  for (size_t i = 0; i < kActionsAllowedAtLockScreenLength; ++i)
428    actions_allowed_at_lock_screen_.insert(kActionsAllowedAtLockScreen[i]);
429  for (size_t i = 0; i < kActionsAllowedAtModalWindowLength; ++i)
430    actions_allowed_at_modal_window_.insert(kActionsAllowedAtModalWindow[i]);
431  for (size_t i = 0; i < kReservedActionsLength; ++i)
432    reserved_actions_.insert(kReservedActions[i]);
433  for (size_t i = 0; i < kNonrepeatableActionsLength; ++i)
434    nonrepeatable_actions_.insert(kNonrepeatableActions[i]);
435  for (size_t i = 0; i < kActionsAllowedInAppModeLength; ++i)
436    actions_allowed_in_app_mode_.insert(kActionsAllowedInAppMode[i]);
437
438  RegisterAccelerators(kAcceleratorData, kAcceleratorDataLength);
439
440#if !defined(NDEBUG)
441  RegisterAccelerators(kDesktopAcceleratorData, kDesktopAcceleratorDataLength);
442#endif
443
444  if (DebugShortcutsEnabled()) {
445    RegisterAccelerators(kDebugAcceleratorData, kDebugAcceleratorDataLength);
446    for (size_t i = 0; i < kReservedDebugActionsLength; ++i)
447      reserved_actions_.insert(kReservedDebugActions[i]);
448  }
449
450#if defined(OS_CHROMEOS)
451  keyboard_brightness_control_delegate_.reset(
452      new KeyboardBrightnessController());
453#endif
454}
455
456void AcceleratorController::Register(const ui::Accelerator& accelerator,
457                                     ui::AcceleratorTarget* target) {
458  accelerator_manager_->Register(accelerator,
459                                 ui::AcceleratorManager::kNormalPriority,
460                                 target);
461}
462
463void AcceleratorController::Unregister(const ui::Accelerator& accelerator,
464                                       ui::AcceleratorTarget* target) {
465  accelerator_manager_->Unregister(accelerator, target);
466}
467
468void AcceleratorController::UnregisterAll(ui::AcceleratorTarget* target) {
469  accelerator_manager_->UnregisterAll(target);
470}
471
472bool AcceleratorController::Process(const ui::Accelerator& accelerator) {
473  if (ime_control_delegate_) {
474    return accelerator_manager_->Process(
475        ime_control_delegate_->RemapAccelerator(accelerator));
476  }
477  return accelerator_manager_->Process(accelerator);
478}
479
480bool AcceleratorController::IsRegistered(
481    const ui::Accelerator& accelerator) const {
482  return accelerator_manager_->GetCurrentTarget(accelerator) != NULL;
483}
484
485bool AcceleratorController::IsReservedAccelerator(
486    const ui::Accelerator& accelerator) const {
487  const ui::Accelerator remapped_accelerator = ime_control_delegate_.get() ?
488      ime_control_delegate_->RemapAccelerator(accelerator) : accelerator;
489
490  std::map<ui::Accelerator, int>::const_iterator iter =
491      accelerators_.find(remapped_accelerator);
492  if (iter == accelerators_.end())
493    return false;  // not an accelerator.
494
495  return reserved_actions_.find(iter->second) != reserved_actions_.end();
496}
497
498bool AcceleratorController::PerformAction(int action,
499                                          const ui::Accelerator& accelerator) {
500  ash::Shell* shell = ash::Shell::GetInstance();
501  if (!shell->session_state_delegate()->IsActiveUserSessionStarted() &&
502      actions_allowed_at_login_screen_.find(action) ==
503      actions_allowed_at_login_screen_.end()) {
504    return false;
505  }
506  if (shell->session_state_delegate()->IsScreenLocked() &&
507      actions_allowed_at_lock_screen_.find(action) ==
508      actions_allowed_at_lock_screen_.end()) {
509    return false;
510  }
511  if (shell->IsSystemModalWindowOpen() &&
512      actions_allowed_at_modal_window_.find(action) ==
513      actions_allowed_at_modal_window_.end()) {
514    // Note: we return true. This indicates the shortcut is handled
515    // and will not be passed to the modal window. This is important
516    // for things like Alt+Tab that would cause an undesired effect
517    // in the modal window by cycling through its window elements.
518    return true;
519  }
520  if (shell->delegate()->IsRunningInForcedAppMode() &&
521      actions_allowed_in_app_mode_.find(action) ==
522      actions_allowed_in_app_mode_.end()) {
523    return false;
524  }
525
526  const ui::KeyboardCode key_code = accelerator.key_code();
527  // PerformAction() is performed from gesture controllers and passes
528  // empty Accelerator() instance as the second argument. Such events
529  // should never be suspended.
530  const bool gesture_event = key_code == ui::VKEY_UNKNOWN;
531
532  // Ignore accelerators invoked as repeated (while holding a key for a long
533  // time, if their handling is nonrepeatable.
534  if (nonrepeatable_actions_.find(action) != nonrepeatable_actions_.end() &&
535      context_.repeated() && !gesture_event) {
536    return true;
537  }
538  // Type of the previous accelerator. Used by NEXT_IME and DISABLE_CAPS_LOCK.
539  const ui::EventType previous_event_type =
540    context_.previous_accelerator().type();
541  const ui::KeyboardCode previous_key_code =
542    context_.previous_accelerator().key_code();
543
544  // You *MUST* return true when some action is performed. Otherwise, this
545  // function might be called *twice*, via BrowserView::PreHandleKeyboardEvent
546  // and BrowserView::HandleKeyboardEvent, for a single accelerator press.
547  //
548  // If your accelerator invokes more than one line of code, please either
549  // implement it in your module's controller code (like TOGGLE_MIRROR_MODE
550  // below) or pull it into a HandleFoo() function above.
551  switch (action) {
552    case ACCESSIBLE_FOCUS_NEXT:
553      return HandleAccessibleFocusCycle(false);
554    case ACCESSIBLE_FOCUS_PREVIOUS:
555      return HandleAccessibleFocusCycle(true);
556    case CYCLE_BACKWARD_MRU:
557      HandleCycleBackwardMRU(accelerator);
558      return true;
559    case CYCLE_FORWARD_MRU:
560      HandleCycleForwardMRU(accelerator);
561      return true;
562    case CYCLE_LINEAR:
563      HandleCycleLinear(accelerator);
564      return true;
565#if defined(OS_CHROMEOS)
566    case ADD_REMOVE_DISPLAY:
567      Shell::GetInstance()->display_manager()->AddRemoveDisplay();
568      return true;
569    case TOGGLE_MIRROR_MODE:
570      Shell::GetInstance()->display_controller()->ToggleMirrorMode();
571      return true;
572    case LOCK_SCREEN:
573      if (key_code == ui::VKEY_L)
574        shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_LOCK_SCREEN_L);
575      return HandleLock();
576    case OPEN_FILE_DIALOG:
577      return HandleFileManager(true /* as_dialog */);
578    case OPEN_FILE_MANAGER:
579      return HandleFileManager(false /* as_dialog */);
580    case OPEN_CROSH:
581      return HandleCrosh();
582    case SILENCE_SPOKEN_FEEDBACK:
583      HandleSilenceSpokenFeedback();
584      break;
585    case SWAP_PRIMARY_DISPLAY:
586      Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
587      return true;
588    case TOGGLE_SPOKEN_FEEDBACK:
589      return HandleToggleSpokenFeedback();
590    case TOGGLE_WIFI:
591      Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi();
592      return true;
593    case TOUCH_HUD_CLEAR: {
594      internal::RootWindowController* controller =
595          internal::RootWindowController::ForActiveRootWindow();
596      if (controller->touch_hud_debug()) {
597        controller->touch_hud_debug()->Clear();
598        return true;
599      }
600      return false;
601    }
602    case TOUCH_HUD_MODE_CHANGE: {
603      internal::RootWindowController* controller =
604          internal::RootWindowController::ForActiveRootWindow();
605      if (controller->touch_hud_debug()) {
606        controller->touch_hud_debug()->ChangeToNextMode();
607        return true;
608      }
609      return false;
610    }
611    case TOUCH_HUD_PROJECTION_TOGGLE: {
612      bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
613      Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
614      return true;
615    }
616    case DISABLE_GPU_WATCHDOG:
617      content::GpuDataManager::GetInstance()->DisableGpuWatchdog();
618      return true;
619#endif
620    case OPEN_FEEDBACK_PAGE:
621      ash::Shell::GetInstance()->delegate()->OpenFeedbackPage();
622      return true;
623    case EXIT:
624      // UMA metrics are recorded in the handler.
625      exit_warning_handler_.HandleAccelerator();
626      return true;
627    case NEW_INCOGNITO_WINDOW:
628      Shell::GetInstance()->delegate()->NewWindow(true /* is_incognito */);
629      return true;
630    case NEW_TAB:
631      if (key_code == ui::VKEY_T)
632        shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEWTAB_T);
633      Shell::GetInstance()->delegate()->NewTab();
634      return true;
635    case NEW_WINDOW:
636      Shell::GetInstance()->delegate()->NewWindow(false /* is_incognito */);
637      return true;
638    case RESTORE_TAB:
639      Shell::GetInstance()->delegate()->RestoreTab();
640      return true;
641    case TAKE_SCREENSHOT:
642      if (screenshot_delegate_.get() &&
643          screenshot_delegate_->CanTakeScreenshot()) {
644        screenshot_delegate_->HandleTakeScreenshotForAllRootWindows();
645      }
646      // Return true to prevent propagation of the key event.
647      return true;
648    case TAKE_PARTIAL_SCREENSHOT:
649      if (screenshot_delegate_) {
650        ash::PartialScreenshotView::StartPartialScreenshot(
651            screenshot_delegate_.get());
652      }
653      // Return true to prevent propagation of the key event because
654      // this key combination is reserved for partial screenshot.
655      return true;
656    case TOGGLE_APP_LIST:
657      // If something else was pressed between the Search key (LWIN)
658      // being pressed and released, then ignore the release of the
659      // Search key.
660      if (key_code == ui::VKEY_LWIN &&
661          (previous_event_type == ui::ET_KEY_RELEASED ||
662           previous_key_code != ui::VKEY_LWIN))
663        return false;
664      if (key_code == ui::VKEY_LWIN)
665        shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_SEARCH_LWIN);
666      // When spoken feedback is enabled, we should neither toggle the list nor
667      // consume the key since Search+Shift is one of the shortcuts the a11y
668      // feature uses. crbug.com/132296
669      DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
670      if (Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled())
671        return false;
672      ash::Shell::GetInstance()->ToggleAppList(NULL);
673      return true;
674    case DISABLE_CAPS_LOCK:
675      if (previous_event_type == ui::ET_KEY_RELEASED ||
676          (previous_key_code != ui::VKEY_LSHIFT &&
677           previous_key_code != ui::VKEY_SHIFT &&
678           previous_key_code != ui::VKEY_RSHIFT)) {
679        // If something else was pressed between the Shift key being pressed
680        // and released, then ignore the release of the Shift key.
681        return false;
682      }
683      if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
684        shell->caps_lock_delegate()->SetCapsLockEnabled(false);
685        return true;
686      }
687      return false;
688    case TOGGLE_CAPS_LOCK:
689      if (key_code == ui::VKEY_LWIN) {
690        // If something else was pressed between the Search key (LWIN)
691        // being pressed and released, then ignore the release of the
692        // Search key.
693        // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
694        if (previous_event_type == ui::ET_KEY_RELEASED ||
695            previous_key_code != ui::VKEY_LWIN)
696          return false;
697      }
698      shell->caps_lock_delegate()->ToggleCapsLock();
699      return true;
700    case BRIGHTNESS_DOWN:
701      if (brightness_control_delegate_)
702        return brightness_control_delegate_->HandleBrightnessDown(accelerator);
703      break;
704    case BRIGHTNESS_UP:
705      if (brightness_control_delegate_)
706        return brightness_control_delegate_->HandleBrightnessUp(accelerator);
707      break;
708    case KEYBOARD_BRIGHTNESS_DOWN:
709      if (keyboard_brightness_control_delegate_)
710        return keyboard_brightness_control_delegate_->
711            HandleKeyboardBrightnessDown(accelerator);
712      break;
713    case KEYBOARD_BRIGHTNESS_UP:
714      if (keyboard_brightness_control_delegate_)
715        return keyboard_brightness_control_delegate_->
716            HandleKeyboardBrightnessUp(accelerator);
717      break;
718    case VOLUME_MUTE:
719      return shell->system_tray_delegate()->GetVolumeControlDelegate()->
720          HandleVolumeMute(accelerator);
721    case VOLUME_DOWN:
722      return shell->system_tray_delegate()->GetVolumeControlDelegate()->
723          HandleVolumeDown(accelerator);
724    case VOLUME_UP:
725      return shell->system_tray_delegate()->GetVolumeControlDelegate()->
726          HandleVolumeUp(accelerator);
727    case FOCUS_LAUNCHER:
728      return shell->focus_cycler()->FocusWidget(
729          Launcher::ForPrimaryDisplay()->shelf_widget());
730    case FOCUS_NEXT_PANE:
731      return HandleRotatePaneFocus(Shell::FORWARD);
732    case FOCUS_PREVIOUS_PANE:
733      return HandleRotatePaneFocus(Shell::BACKWARD);
734    case SHOW_KEYBOARD_OVERLAY:
735      ash::Shell::GetInstance()->delegate()->ShowKeyboardOverlay();
736      return true;
737    case SHOW_OAK:
738      if (CommandLine::ForCurrentProcess()->HasSwitch(
739              switches::kAshEnableOak)) {
740        oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow());
741        return true;
742      }
743      break;
744    case SHOW_SYSTEM_TRAY_BUBBLE: {
745      internal::RootWindowController* controller =
746          internal::RootWindowController::ForActiveRootWindow();
747      if (!controller->GetSystemTray()->HasSystemBubble()) {
748        controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
749        return true;
750      }
751      break;
752    }
753    case SHOW_MESSAGE_CENTER_BUBBLE: {
754      internal::RootWindowController* controller =
755          internal::RootWindowController::ForActiveRootWindow();
756      internal::StatusAreaWidget* status_area_widget =
757          controller->shelf()->status_area_widget();
758      if (status_area_widget) {
759        WebNotificationTray* notification_tray =
760            status_area_widget->web_notification_tray();
761        if (notification_tray->visible())
762          notification_tray->ShowMessageCenterBubble();
763      }
764      break;
765    }
766    case SHOW_TASK_MANAGER:
767      Shell::GetInstance()->delegate()->ShowTaskManager();
768      return true;
769    case NEXT_IME:
770      // This check is necessary e.g. not to process the Shift+Alt+
771      // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/
772      // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
773      // is released.
774      if (previous_event_type == ui::ET_KEY_RELEASED &&
775          // Workaround for crbug.com/139556. CJK IME users tend to press
776          // Enter (or Space) and Shift+Alt almost at the same time to commit
777          // an IME string and then switch from the IME to the English layout.
778          // This workaround allows the user to trigger NEXT_IME even if the
779          // user presses Shift+Alt before releasing Enter.
780          // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
781          previous_key_code != ui::VKEY_RETURN &&
782          previous_key_code != ui::VKEY_SPACE) {
783        // We totally ignore this accelerator.
784        // TODO(mazda): Fix crbug.com/158217
785        return false;
786      }
787      if (ime_control_delegate_)
788        return ime_control_delegate_->HandleNextIme();
789      break;
790    case PREVIOUS_IME:
791      if (ime_control_delegate_)
792        return ime_control_delegate_->HandlePreviousIme(accelerator);
793      break;
794    case PRINT_UI_HIERARCHIES:
795      return HandlePrintUIHierarchies();
796    case SWITCH_IME:
797      if (ime_control_delegate_)
798        return ime_control_delegate_->HandleSwitchIme(accelerator);
799      break;
800    case LAUNCH_APP_0:
801      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0);
802      return true;
803    case LAUNCH_APP_1:
804      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1);
805      return true;
806    case LAUNCH_APP_2:
807      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2);
808      return true;
809    case LAUNCH_APP_3:
810      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3);
811      return true;
812    case LAUNCH_APP_4:
813      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4);
814      return true;
815    case LAUNCH_APP_5:
816      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5);
817      return true;
818    case LAUNCH_APP_6:
819      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6);
820      return true;
821    case LAUNCH_APP_7:
822      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7);
823      return true;
824    case LAUNCH_LAST_APP:
825      Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
826      return true;
827    case WINDOW_SNAP_LEFT:
828    case WINDOW_SNAP_RIGHT: {
829      aura::Window* window = wm::GetActiveWindow();
830      // Disable window docking shortcut key for full screen window due to
831      // http://crbug.com/135487.
832      if (!window ||
833          window->type() != aura::client::WINDOW_TYPE_NORMAL ||
834          wm::IsWindowFullscreen(window)) {
835        break;
836      }
837
838      internal::SnapSizer::SnapWindow(window,
839          action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
840                                       internal::SnapSizer::RIGHT_EDGE);
841      return true;
842    }
843    case WINDOW_MINIMIZE:
844      return accelerators::ToggleMinimized();
845    case TOGGLE_FULLSCREEN: {
846      if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
847        shell->delegate()->RecordUserMetricsAction(
848            UMA_ACCEL_FULLSCREEN_F4);
849      }
850      shell->delegate()->ToggleFullscreen();
851      return true;
852    }
853    case TOGGLE_MAXIMIZED: {
854      shell->delegate()->ToggleMaximized();
855      return true;
856    }
857    case WINDOW_POSITION_CENTER: {
858      aura::Window* window = wm::GetActiveWindow();
859      if (window) {
860        wm::CenterWindow(window);
861        return true;
862      }
863      break;
864    }
865    case SCALE_UI_UP:
866      return HandleScaleUI(true /* up */);
867    case SCALE_UI_DOWN:
868      return HandleScaleUI(false /* down */);
869    case SCALE_UI_RESET:
870      return HandleScaleReset();
871    case ROTATE_WINDOW:
872      return HandleRotateActiveWindow();
873    case ROTATE_SCREEN:
874      return HandleRotateScreen();
875    case TOGGLE_DESKTOP_BACKGROUND_MODE:
876      return HandleToggleDesktopBackgroundMode();
877    case TOGGLE_ROOT_WINDOW_FULL_SCREEN:
878      return HandleToggleRootWindowFullScreen();
879    case DEBUG_TOGGLE_DEVICE_SCALE_FACTOR:
880      Shell::GetInstance()->display_manager()->ToggleDisplayScaleFactor();
881      return true;
882    case DEBUG_TOGGLE_SHOW_DEBUG_BORDERS:
883      ash::debug::ToggleShowDebugBorders();
884      return true;
885    case DEBUG_TOGGLE_SHOW_FPS_COUNTER:
886      ash::debug::ToggleShowFpsCounter();
887      return true;
888    case DEBUG_TOGGLE_SHOW_PAINT_RECTS:
889      ash::debug::ToggleShowPaintRects();
890      return true;
891    case MAGNIFY_SCREEN_ZOOM_IN:
892      return HandleMagnifyScreen(1);
893    case MAGNIFY_SCREEN_ZOOM_OUT:
894      return HandleMagnifyScreen(-1);
895    case MEDIA_NEXT_TRACK:
896      return HandleMediaNextTrack();
897    case MEDIA_PLAY_PAUSE:
898       return HandleMediaPlayPause();
899    case MEDIA_PREV_TRACK:
900       return HandleMediaPrevTrack();
901    case POWER_PRESSED:  // fallthrough
902    case POWER_RELEASED:
903#if defined(OS_CHROMEOS)
904      if (!base::chromeos::IsRunningOnChromeOS()) {
905        // There is no powerd in linux desktop, so call the
906        // PowerButtonController here.
907        Shell::GetInstance()->power_button_controller()->
908            OnPowerButtonEvent(action == POWER_PRESSED, base::TimeTicks());
909      }
910#endif
911      // We don't do anything with these at present on the device,
912      // (power button events are reported to us from powerm via
913      // D-BUS), but we consume them to prevent them from getting
914      // passed to apps -- see http://crbug.com/146609.
915      return true;
916    case LOCK_PRESSED:
917    case LOCK_RELEASED:
918      Shell::GetInstance()->power_button_controller()->
919          OnLockButtonEvent(action == LOCK_PRESSED, base::TimeTicks());
920      return true;
921    case PRINT_LAYER_HIERARCHY:
922      return HandlePrintLayerHierarchy();
923    case PRINT_VIEW_HIERARCHY:
924      return HandlePrintViewHierarchy();
925    case PRINT_WINDOW_HIERARCHY:
926      return HandlePrintWindowHierarchy();
927    default:
928      NOTREACHED() << "Unhandled action " << action;
929  }
930  return false;
931}
932
933void AcceleratorController::SetBrightnessControlDelegate(
934    scoped_ptr<BrightnessControlDelegate> brightness_control_delegate) {
935  // Install brightness control delegate only when internal
936  // display exists.
937  if (Shell::GetInstance()->display_manager()->HasInternalDisplay() ||
938      CommandLine::ForCurrentProcess()->HasSwitch(
939          switches::kAshEnableBrightnessControl)) {
940    brightness_control_delegate_ = brightness_control_delegate.Pass();
941  }
942}
943
944void AcceleratorController::SetImeControlDelegate(
945    scoped_ptr<ImeControlDelegate> ime_control_delegate) {
946  ime_control_delegate_ = ime_control_delegate.Pass();
947}
948
949void AcceleratorController::SetScreenshotDelegate(
950    scoped_ptr<ScreenshotDelegate> screenshot_delegate) {
951  screenshot_delegate_ = screenshot_delegate.Pass();
952}
953
954////////////////////////////////////////////////////////////////////////////////
955// AcceleratorController, ui::AcceleratorTarget implementation:
956
957bool AcceleratorController::AcceleratorPressed(
958    const ui::Accelerator& accelerator) {
959  std::map<ui::Accelerator, int>::const_iterator it =
960      accelerators_.find(accelerator);
961  DCHECK(it != accelerators_.end());
962  return PerformAction(static_cast<AcceleratorAction>(it->second), accelerator);
963}
964
965void AcceleratorController::RegisterAccelerators(
966    const AcceleratorData accelerators[],
967    size_t accelerators_length) {
968  for (size_t i = 0; i < accelerators_length; ++i) {
969    ui::Accelerator accelerator(accelerators[i].keycode,
970                                accelerators[i].modifiers);
971    accelerator.set_type(accelerators[i].trigger_on_press ?
972                         ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED);
973    Register(accelerator, this);
974    accelerators_.insert(
975        std::make_pair(accelerator, accelerators[i].action));
976  }
977}
978
979void AcceleratorController::SetKeyboardBrightnessControlDelegate(
980    scoped_ptr<KeyboardBrightnessControlDelegate>
981    keyboard_brightness_control_delegate) {
982  keyboard_brightness_control_delegate_ =
983      keyboard_brightness_control_delegate.Pass();
984}
985
986bool AcceleratorController::CanHandleAccelerators() const {
987  return true;
988}
989
990}  // namespace ash
991