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 #ifndef ASH_ACCELERATORS_ACCELERATOR_CONTROLLER_IMPL_H_ 6 #define ASH_ACCELERATORS_ACCELERATOR_CONTROLLER_IMPL_H_ 7 8 #include <stddef.h> 9 10 #include <map> 11 #include <memory> 12 #include <set> 13 #include <vector> 14 15 #include "ash/accelerators/accelerator_confirmation_dialog.h" 16 #include "ash/accelerators/accelerator_table.h" 17 #include "ash/accelerators/exit_warning_handler.h" 18 #include "ash/ash_export.h" 19 #include "ash/public/cpp/accelerators.h" 20 #include "base/compiler_specific.h" 21 #include "base/gtest_prod_util.h" 22 #include "base/macros.h" 23 #include "ui/base/accelerators/accelerator.h" 24 #include "ui/base/accelerators/accelerator_history.h" 25 26 namespace ui { 27 class AcceleratorManager; 28 } 29 30 namespace ash { 31 32 struct AcceleratorData; 33 class ExitWarningHandler; 34 35 // See TabletModeVolumeAdjustType at tools/metrics/histograms/enums.xml. 36 enum class TabletModeVolumeAdjustType { 37 kAccidentalAdjustWithSwapEnabled = 0, 38 kNormalAdjustWithSwapEnabled = 1, 39 kAccidentalAdjustWithSwapDisabled = 2, 40 kNormalAdjustWithSwapDisabled = 3, 41 kMaxValue = kNormalAdjustWithSwapDisabled, 42 }; 43 44 // These values are persisted to logs. Entries should not be renumbered and 45 // numeric values should never be reused. 46 // Captures usage of Alt+[ and Alt+]. 47 enum class WindowSnapAcceleratorAction { 48 kCycleLeftSnapInClamshellNoOverview = 0, 49 kCycleLeftSnapInClamshellOverview = 1, 50 kCycleLeftSnapInTablet = 2, 51 kCycleRightSnapInClamshellNoOverview = 3, 52 kCycleRightSnapInClamshellOverview = 4, 53 kCycleRightSnapInTablet = 5, 54 kMaxValue = kCycleRightSnapInTablet, 55 }; 56 57 // Histogram for volume adjustment in tablet mode. 58 ASH_EXPORT extern const char kTabletCountOfVolumeAdjustType[]; 59 60 // Identifiers for toggling accelerator notifications. 61 ASH_EXPORT extern const char kHighContrastToggleAccelNotificationId[]; 62 ASH_EXPORT extern const char kDockedMagnifierToggleAccelNotificationId[]; 63 ASH_EXPORT extern const char kFullscreenMagnifierToggleAccelNotificationId[]; 64 65 // UMA accessibility histogram names. 66 ASH_EXPORT extern const char kAccessibilityHighContrastShortcut[]; 67 ASH_EXPORT extern const char kAccessibilitySpokenFeedbackShortcut[]; 68 ASH_EXPORT extern const char kAccessibilityScreenMagnifierShortcut[]; 69 ASH_EXPORT extern const char kAccessibilityDockedMagnifierShortcut[]; 70 71 // Name of histogram corresponding to |WindowSnapAcceleratorAction|. 72 ASH_EXPORT extern const char kAccelWindowSnap[]; 73 74 // AcceleratorControllerImpl provides functions for registering or unregistering 75 // global keyboard accelerators, which are handled earlier than any windows. It 76 // also implements several handlers as an accelerator target. 77 class ASH_EXPORT AcceleratorControllerImpl : public ui::AcceleratorTarget, 78 public AcceleratorController { 79 public: 80 // Some Chrome OS devices have volume up and volume down buttons on their 81 // side. We want the button that's closer to the top/right to increase the 82 // volume and the button that's closer to the bottom/left to decrease the 83 // volume, so we use the buttons' location and the device orientation to 84 // determine whether the buttons should be swapped. 85 struct SideVolumeButtonLocation { 86 // The button can be at the side of the keyboard or the display. Then value 87 // of the region could be kVolumeButtonRegionKeyboard or 88 // kVolumeButtonRegionScreen. 89 std::string region; 90 // Side info of region. The value could be kVolumeButtonSideLeft, 91 // kVolumeButtonSideRight, kVolumeButtonSideTop or kVolumeButtonSideBottom. 92 std::string side; 93 }; 94 95 // TestApi is used for tests to get internal implementation details. 96 class TestApi { 97 public: 98 explicit TestApi(AcceleratorControllerImpl* controller); 99 ~TestApi() = default; 100 101 // If |controller_->tablet_mode_volume_adjust_timer_| is running, stops it, 102 // runs its task, and returns true. Otherwise returns false. 103 bool TriggerTabletModeVolumeAdjustTimer() WARN_UNUSED_RESULT; 104 105 // Registers the specified accelerators. 106 void RegisterAccelerators(const AcceleratorData accelerators[], 107 size_t accelerators_length); 108 109 // Returns the corresponding accelerator data if |action| maps to a 110 // deprecated accelerator, otherwise return nullptr. 111 const DeprecatedAcceleratorData* GetDeprecatedAcceleratorData( 112 AcceleratorAction action); 113 114 // Accessor to accelerator confirmation dialog. 115 AcceleratorConfirmationDialog* GetConfirmationDialog(); 116 117 AcceleratorControllerImpl::SideVolumeButtonLocation side_volume_button_location()118 side_volume_button_location() { 119 return controller_->side_volume_button_location_; 120 } 121 void SetSideVolumeButtonFilePath(base::FilePath path); 122 void SetSideVolumeButtonLocation(const std::string& region, 123 const std::string& side); 124 125 private: 126 AcceleratorControllerImpl* controller_; // Not owned. 127 128 DISALLOW_COPY_AND_ASSIGN(TestApi); 129 }; 130 131 // Fields of the side volume button location info. 132 static constexpr const char* kVolumeButtonRegion = "region"; 133 static constexpr const char* kVolumeButtonSide = "side"; 134 135 // Values of kVolumeButtonRegion. 136 static constexpr const char* kVolumeButtonRegionKeyboard = "keyboard"; 137 static constexpr const char* kVolumeButtonRegionScreen = "screen"; 138 // Values of kVolumeButtonSide. 139 static constexpr const char* kVolumeButtonSideLeft = "left"; 140 static constexpr const char* kVolumeButtonSideRight = "right"; 141 static constexpr const char* kVolumeButtonSideTop = "top"; 142 static constexpr const char* kVolumeButtonSideBottom = "bottom"; 143 144 AcceleratorControllerImpl(); 145 ~AcceleratorControllerImpl() override; 146 147 // A list of possible ways in which an accelerator should be restricted before 148 // processing. Any target registered with this controller should respect 149 // restrictions by calling |GetCurrentAcceleratorRestriction| during 150 // processing. 151 enum AcceleratorProcessingRestriction { 152 // Process the accelerator normally. 153 RESTRICTION_NONE, 154 155 // Don't process the accelerator. 156 RESTRICTION_PREVENT_PROCESSING, 157 158 // Don't process the accelerator and prevent propagation to other targets. 159 RESTRICTION_PREVENT_PROCESSING_AND_PROPAGATION 160 }; 161 162 // Registers global keyboard accelerators for the specified target. If 163 // multiple targets are registered for any given accelerator, a target 164 // registered later has higher priority. 165 void Register(const std::vector<ui::Accelerator>& accelerators, 166 ui::AcceleratorTarget* target); 167 168 // Unregisters the specified keyboard accelerator for the specified target. 169 void Unregister(const ui::Accelerator& accelerator, 170 ui::AcceleratorTarget* target); 171 172 // Unregisters all keyboard accelerators for the specified target. 173 void UnregisterAll(ui::AcceleratorTarget* target); 174 175 // Returns true if there is an action for |accelerator| and it is enabled. 176 bool IsActionForAcceleratorEnabled(const ui::Accelerator& accelerator) const; 177 178 // AcceleratorControllerImpl: 179 bool Process(const ui::Accelerator& accelerator) override; 180 bool IsDeprecated(const ui::Accelerator& accelerator) const override; 181 bool PerformActionIfEnabled(AcceleratorAction action, 182 const ui::Accelerator& accelerator) override; 183 bool OnMenuAccelerator(const ui::Accelerator& accelerator) override; 184 bool IsRegistered(const ui::Accelerator& accelerator) const override; 185 ui::AcceleratorHistory* GetAcceleratorHistory() override; 186 187 // Returns true if the |accelerator| is preferred. A preferred accelerator 188 // is handled before being passed to an window/web contents, unless 189 // the window is in fullscreen state. 190 bool IsPreferred(const ui::Accelerator& accelerator) const; 191 192 // Returns true if the |accelerator| is reserved. A reserved accelerator 193 // is always handled and will never be passed to an window/web contents. 194 bool IsReserved(const ui::Accelerator& accelerator) const; 195 196 // Returns the restriction for the current context. 197 AcceleratorProcessingRestriction GetCurrentAcceleratorRestriction(); 198 199 // Provides access to the ExitWarningHandler for testing. GetExitWarningHandlerForTest()200 ExitWarningHandler* GetExitWarningHandlerForTest() { 201 return &exit_warning_handler_; 202 } 203 accelerator_history()204 ui::AcceleratorHistory* accelerator_history() { 205 return accelerator_history_.get(); 206 } 207 208 // Overridden from ui::AcceleratorTarget: 209 bool AcceleratorPressed(const ui::Accelerator& accelerator) override; 210 bool CanHandleAccelerators() const override; 211 212 // A confirmation dialog will be shown the first time an accessibility feature 213 // is enabled using the specified accelerator key sequence. Only one dialog 214 // will be shown at a time, and will not be shown again if the user has 215 // selected "accept" on a given dialog. The dialog was added to ensure that 216 // users would be aware of the shortcut they have just enabled, and to prevent 217 // users from accidentally triggering the feature. The dialog is currently 218 // shown when enabling the following features: high contrast, full screen 219 // magnifier, docked magnifier and screen rotation. The shown dialog is stored 220 // as a weak pointer in the variable |confirmation_dialog_| below. 221 void MaybeShowConfirmationDialog(int window_title_text_id, 222 int dialog_text_id, 223 base::OnceClosure on_accept_callback, 224 base::OnceClosure on_cancel_callback); 225 226 // Read the side volume button location info from local file under 227 // kSideVolumeButtonLocationFilePath, parse and write it into 228 // |side_volume_button_location_|. 229 void ParseSideVolumeButtonLocationInfo(); 230 231 private: 232 // Initializes the accelerators this class handles as a target. 233 void Init(); 234 235 // Registers the specified accelerators. 236 void RegisterAccelerators(const AcceleratorData accelerators[], 237 size_t accelerators_length); 238 239 // Registers the deprecated accelerators and their replacing new ones. 240 void RegisterDeprecatedAccelerators(); 241 242 // Returns whether |action| can be performed. The |accelerator| may provide 243 // additional data the action needs. 244 bool CanPerformAction(AcceleratorAction action, 245 const ui::Accelerator& accelerator) const; 246 247 // Performs the specified action. The |accelerator| may provide additional 248 // data the action needs. 249 void PerformAction(AcceleratorAction action, 250 const ui::Accelerator& accelerator); 251 252 // Returns whether performing |action| should consume the key event. 253 bool ShouldActionConsumeKeyEvent(AcceleratorAction action); 254 255 // Get the accelerator restriction for the given action. Supply an |action| 256 // of -1 to get restrictions that apply for the current context. 257 AcceleratorProcessingRestriction GetAcceleratorProcessingRestriction( 258 int action) const; 259 260 // If |accelerator| is a deprecated accelerator, it performs the appropriate 261 // deprecated accelerator pre-handling. 262 // Returns PROCEED if the accelerator's action should be performed (i.e. if 263 // |accelerator| is not a deprecated accelerator, or it's an enabled 264 // deprecated accelerator), and STOP otherwise (if the accelerator is a 265 // disabled deprecated accelerator). 266 enum class AcceleratorProcessingStatus { PROCEED, STOP }; 267 AcceleratorProcessingStatus MaybeDeprecatedAcceleratorPressed( 268 AcceleratorAction action, 269 const ui::Accelerator& accelerator) const; 270 271 // Returns true if |source_device_id| corresponds to the internal keyboard or 272 // an internal uncategorized input device. 273 bool IsInternalKeyboardOrUncategorizedDevice(int source_device_id) const; 274 275 // Returns true if |side_volume_button_location_| is in agreed format and 276 // values. 277 bool IsValidSideVolumeButtonLocation() const; 278 279 // Returns true if the side volume buttons should be swapped. See 280 // SideVolumeButonLocation for the details. 281 bool ShouldSwapSideVolumeButtons(int source_device_id) const; 282 283 // The metrics recorded include accidental volume adjustments (defined as a 284 // sequence of volume button events in close succession starting with a 285 // volume-up event but ending with an overall-decreased volume, or vice versa) 286 // or normal volume adjustments w/o SwapSideVolumeButtonsForOrientation 287 // feature enabled. 288 void UpdateTabletModeVolumeAdjustHistogram(); 289 290 // Starts |tablet_mode_volume_adjust_timer_| while see VOLUME_UP or 291 // VOLUME_DOWN acceleration action when in tablet mode. 292 void StartTabletModeVolumeAdjustTimer(AcceleratorAction action); 293 294 std::unique_ptr<ui::AcceleratorManager> accelerator_manager_; 295 296 // A tracker for the current and previous accelerators. 297 std::unique_ptr<ui::AcceleratorHistory> accelerator_history_; 298 299 // Handles the exit accelerator which requires a double press to exit and 300 // shows a popup with an explanation. 301 ExitWarningHandler exit_warning_handler_; 302 303 // A map from accelerators to the AcceleratorAction values, which are used in 304 // the implementation. 305 std::map<ui::Accelerator, AcceleratorAction> accelerators_; 306 307 std::map<AcceleratorAction, const DeprecatedAcceleratorData*> 308 actions_with_deprecations_; 309 std::set<ui::Accelerator> deprecated_accelerators_; 310 311 // Actions allowed when the user is not signed in. 312 std::set<int> actions_allowed_at_login_screen_; 313 // Actions allowed when the screen is locked. 314 std::set<int> actions_allowed_at_lock_screen_; 315 // Actions allowed when the power menu is opened. 316 std::set<int> actions_allowed_at_power_menu_; 317 // Actions allowed when a modal window is up. 318 std::set<int> actions_allowed_at_modal_window_; 319 // Preferred actions. See accelerator_table.h for details. 320 std::set<int> preferred_actions_; 321 // Reserved actions. See accelerator_table.h for details. 322 std::set<int> reserved_actions_; 323 // Actions which will be repeated while holding the accelerator key. 324 std::set<int> repeatable_actions_; 325 // Actions allowed in app mode. 326 std::set<int> actions_allowed_in_app_mode_; 327 // Actions allowed in pinned mode. 328 std::set<int> actions_allowed_in_pinned_mode_; 329 // Actions disallowed if there are no windows. 330 std::set<int> actions_needing_window_; 331 // Actions that can be performed without closing the menu (if one is present). 332 std::set<int> actions_keeping_menu_open_; 333 334 // Holds a weak pointer to the accelerator confirmation dialog. 335 base::WeakPtr<AcceleratorConfirmationDialog> confirmation_dialog_; 336 337 // Path of the file that contains the side volume button location info. It 338 // should always be kSideVolumeButtonLocationFilePath. But it is allowed to be 339 // set to different paths in test. 340 base::FilePath side_volume_button_location_file_path_; 341 342 // Stores the location info of side volume buttons. 343 SideVolumeButtonLocation side_volume_button_location_; 344 345 // Started when VOLUME_DOWN or VOLUME_UP accelerator action is seen while in 346 // tablet mode. Runs UpdateTabletModeVolumeAdjustHistogram() to record 347 // metrics. 348 base::OneShotTimer tablet_mode_volume_adjust_timer_; 349 350 // True if volume adjust starts with VOLUME_UP action. 351 bool volume_adjust_starts_with_up_ = false; 352 353 // The initial volume percentage when volume adjust starts. 354 int initial_volume_percent_ = 0; 355 356 DISALLOW_COPY_AND_ASSIGN(AcceleratorControllerImpl); 357 }; 358 359 } // namespace ash 360 361 #endif // ASH_ACCELERATORS_ACCELERATOR_CONTROLLER_IMPL_H_ 362