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