File: event_rewriter_delegate_impl.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (369 lines) | stat: -rw-r--r-- 14,371 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/events/event_rewriter_delegate_impl.h"

#include <optional>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/input_device_settings_controller.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/public/mojom/input_device_settings.mojom.h"
#include "ash/system/input_device_settings/input_device_settings_notification_controller.h"
#include "base/containers/fixed_flat_map.h"
#include "base/notreached.h"
#include "chrome/browser/ash/notifications/deprecation_notification_controller.h"
#include "chrome/browser/extensions/extension_commands_global_registry.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/login/login_display_host.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/events/ash/mojom/modifier_key.mojom-shared.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/message_center/message_center.h"

namespace ash {

EventRewriterDelegateImpl::EventRewriterDelegateImpl(
    wm::ActivationClient* activation_client)
    : EventRewriterDelegateImpl(
          activation_client,
          std::make_unique<DeprecationNotificationController>(
              message_center::MessageCenter::Get()),
          std::make_unique<InputDeviceSettingsNotificationController>(
              message_center::MessageCenter::Get()),
          InputDeviceSettingsController::Get()) {}

EventRewriterDelegateImpl::EventRewriterDelegateImpl(
    wm::ActivationClient* activation_client,
    std::unique_ptr<DeprecationNotificationController> deprecation_controller,
    std::unique_ptr<InputDeviceSettingsNotificationController>
        input_device_settings_notification_controller,
    InputDeviceSettingsController* input_device_settings_controller)
    : pref_service_for_testing_(nullptr),
      activation_client_(activation_client),
      deprecation_controller_(std::move(deprecation_controller)),
      input_device_settings_notification_controller_(
          std::move(input_device_settings_notification_controller)),
      input_device_settings_controller_(input_device_settings_controller) {}

EventRewriterDelegateImpl::~EventRewriterDelegateImpl() = default;

bool EventRewriterDelegateImpl::RewriteModifierKeys() {
  // Do nothing if we have just logged in as guest but have not restarted chrome
  // process yet (so we are still on the login screen). In this situations we
  // have no user profile so can not do anything useful.
  // Note that currently, unlike other accounts, when user logs in as guest, we
  // restart chrome process. In future this is to be changed.
  // TODO(glotov): remove the following condition when we do not restart chrome
  // when user logs in as guest.
  // TODO(kpschoedel): check whether this is still necessary.
  if (user_manager::UserManager::Get()->IsLoggedInAsGuest() &&
      LoginDisplayHost::default_host())
    return false;
  return !suppress_modifier_key_rewrites_;
}

std::optional<ui::mojom::ModifierKey>
EventRewriterDelegateImpl::GetKeyboardRemappedModifierValue(
    int device_id,
    ui::mojom::ModifierKey modifier_key,
    const std::string& pref_name) const {
  // `modifier_key` and `device_id` are unused when the flag is disabled.
  if (!ash::features::IsInputDeviceSettingsSplitEnabled()) {
    if (pref_name.empty()) {
      return std::nullopt;
    }

    // If we're at the login screen, try to get the pref from the global prefs
    // dictionary.
    int value;
    if (LoginDisplayHost::default_host() &&
        LoginDisplayHost::default_host()->GetKeyboardRemappedPrefValue(
            pref_name, &value)) {
      return static_cast<ui::mojom::ModifierKey>(value);
    }
    const PrefService* pref_service = GetPrefService();
    if (!pref_service) {
      return std::nullopt;
    }
    const PrefService::Preference* preference =
        pref_service->FindPreference(pref_name);
    if (!preference) {
      return std::nullopt;
    }

    DCHECK_EQ(preference->GetType(), base::Value::Type::INTEGER);
    return static_cast<ui::mojom::ModifierKey>(
        preference->GetValue()->GetInt());
  }

  // `pref_name` is unused when the flag is enabled.
  const mojom::KeyboardSettings* settings =
      input_device_settings_controller_->GetKeyboardSettings(device_id);
  if (!settings) {
    return std::nullopt;
  }

  auto iter = settings->modifier_remappings.find(modifier_key);
  if (iter == settings->modifier_remappings.end()) {
    return modifier_key;
  }

  return iter->second;
}

bool EventRewriterDelegateImpl::TopRowKeysAreFunctionKeys(int device_id) const {
  if (ash::features::IsInputDeviceSettingsSplitEnabled()) {
    const mojom::KeyboardSettings* settings =
        input_device_settings_controller_->GetKeyboardSettings(device_id);
    if (settings) {
      return settings->top_row_are_fkeys;
    }
  }

  if (ash::features::IsPeripheralCustomizationEnabled()) {
    bool is_mouse_or_tablet =
        input_device_settings_controller_->GetMouseSettings(device_id) ||
        input_device_settings_controller_->GetGraphicsTabletSettings(device_id);
    if (is_mouse_or_tablet) {
      // If it is a mouse or graphics tablet, do not rewrite function keys.
      return true;
    }
  }

  // If we really don't know what device this is, fall back to respecting the
  // global preference.
  const PrefService* pref_service = GetPrefService();
  if (!pref_service) {
    return false;
  }
  return pref_service->GetBoolean(prefs::kSendFunctionKeys);
}

bool EventRewriterDelegateImpl::IsExtensionCommandRegistered(
    ui::KeyboardCode key_code,
    int flags) const {
  if (extension_commands_override_for_testing_.has_value()) {
    return extension_commands_override_for_testing_->count({key_code, flags}) >
           0;
  }

  // Some keyboard events for ChromeOS get rewritten, such as:
  // Search+Shift+Left gets converted to Shift+Home (BeginDocument).
  // This doesn't make sense if the user has assigned that shortcut
  // to an extension. Because:
  // 1) The extension would, upon seeing a request for Ctrl+Shift+Home have
  //    to register for Shift+Home, instead.
  // 2) The conversion is unnecessary, because Shift+Home (BeginDocument) isn't
  //    going to be executed.
  // Therefore, we skip converting the accelerator if an extension has
  // registered for this shortcut.
  Profile* profile = ProfileManager::GetActiveUserProfile();
  if (!profile || !extensions::ExtensionCommandsGlobalRegistry::Get(profile))
    return false;

  constexpr int kModifierMasks = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN |
                                 ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN;
  ui::Accelerator accelerator(key_code, flags & kModifierMasks);
  return extensions::ExtensionCommandsGlobalRegistry::Get(profile)
      ->IsRegistered(accelerator);
}

bool EventRewriterDelegateImpl::IsSearchKeyAcceleratorReserved() const {
  // |activation_client_| can be null in test.
  if (!activation_client_)
    return false;

  aura::Window* active_window = activation_client_->GetActiveWindow();
  return active_window &&
         active_window->GetProperty(kSearchKeyAcceleratorReservedKey);
}

bool EventRewriterDelegateImpl::RewriteMetaTopRowKeyComboEvents(
    int device_id) const {
  // When the flag is disabled, `device_id` is unused.
  if (!ash::features::IsInputDeviceSettingsSplitEnabled()) {
    return !suppress_meta_top_row_key_rewrites_;
  }

  const mojom::KeyboardSettings* settings =
      input_device_settings_controller_->GetKeyboardSettings(device_id);
  if (settings) {
    return !settings->suppress_meta_fkey_rewrites;
  }

  if (ash::features::IsPeripheralCustomizationEnabled()) {
    // If it is a mouse or graphics tablet, do not rewrite function keys.
    return !(input_device_settings_controller_->GetMouseSettings(device_id) ||
             input_device_settings_controller_->GetGraphicsTabletSettings(
                 device_id));
  }

  return true;
}

void EventRewriterDelegateImpl::SuppressMetaTopRowKeyComboRewrites(
    bool should_suppress) {
  suppress_meta_top_row_key_rewrites_ = should_suppress;
}

void EventRewriterDelegateImpl::RecordEventRemappedToRightClick(
    bool alt_based_right_click) {
  PrefService* const pref_service = GetPrefService();
  if (!pref_service) {
    return;
  }
  const auto* pref_name = alt_based_right_click
                              ? prefs::kAltEventRemappedToRightClick
                              : prefs::kSearchEventRemappedToRightClick;
  int count = pref_service->GetInteger(pref_name);
  pref_service->SetInteger(pref_name, ++count);
}

void EventRewriterDelegateImpl::RecordSixPackEventRewrite(
    ui::KeyboardCode key_code,
    bool alt_based) {
  PrefService* const pref_service = GetPrefService();
  if (!pref_service) {
    return;
  }
  // A map between "six pack" keys to prefs which track how often a user uses
  // either the alt or search based shortcut variant to emit a "six pack" event.
  // The "Insert" key is omitted since the (Search+Shift+Backspace) rewrite is
  // the only way to emit an "Insert" key event.
  static constexpr auto kSixPackKeyToPrefMap =
      base::MakeFixedFlatMap<ui::KeyboardCode, const char*>({
          {ui::KeyboardCode::VKEY_DELETE,
           prefs::kKeyEventRemappedToSixPackDelete},
          {ui::KeyboardCode::VKEY_HOME, prefs::kKeyEventRemappedToSixPackHome},
          {ui::KeyboardCode::VKEY_PRIOR,
           prefs::kKeyEventRemappedToSixPackPageDown},
          {ui::KeyboardCode::VKEY_END, prefs::kKeyEventRemappedToSixPackEnd},
          {ui::KeyboardCode::VKEY_NEXT,
           prefs::kKeyEventRemappedToSixPackPageUp},
      });
  auto it = kSixPackKeyToPrefMap.find(key_code);
  CHECK(it != kSixPackKeyToPrefMap.end());
  int count = pref_service->GetInteger(it->second);
  // `alt_based` tells us whether this "six pack" event was produced by an
  // Alt or Search/Launcher based keyboard shortcut. Update our pref to track
  // which method the user uses more frequently.
  count += alt_based ? 1 : -1;
  pref_service->SetInteger(it->second, count);
}

std::optional<ui::mojom::SimulateRightClickModifier>
EventRewriterDelegateImpl::GetRemapRightClickModifier(int device_id) {
  const mojom::TouchpadSettings* settings =
      input_device_settings_controller_->GetTouchpadSettings(device_id);
  if (!settings) {
    return std::nullopt;
  }
  return settings->simulate_right_click;
}

std::optional<ui::mojom::SixPackShortcutModifier>
EventRewriterDelegateImpl::GetShortcutModifierForSixPackKey(
    int device_id,
    ui::KeyboardCode key_code) {
  const mojom::KeyboardSettings* settings =
      input_device_settings_controller_->GetKeyboardSettings(device_id);
  if (!settings) {
    return std::nullopt;
  }
  switch (key_code) {
    case ui::KeyboardCode::VKEY_DELETE:
      return settings->six_pack_key_remappings->del;
    case ui::KeyboardCode::VKEY_HOME:
      return settings->six_pack_key_remappings->home;
    case ui::KeyboardCode::VKEY_PRIOR:
      return settings->six_pack_key_remappings->page_up;
    case ui::KeyboardCode::VKEY_END:
      return settings->six_pack_key_remappings->end;
    case ui::KeyboardCode::VKEY_NEXT:
      return settings->six_pack_key_remappings->page_down;
    case ui::KeyboardCode::VKEY_INSERT:
      return settings->six_pack_key_remappings->insert;
    default:
      NOTREACHED();
  }
}

bool EventRewriterDelegateImpl::NotifyDeprecatedRightClickRewrite() {
  return deprecation_controller_->NotifyDeprecatedRightClickRewrite();
}

bool EventRewriterDelegateImpl::NotifyDeprecatedSixPackKeyRewrite(
    ui::KeyboardCode key_code) {
  return deprecation_controller_->NotifyDeprecatedSixPackKeyRewrite(key_code);
}

PrefService* EventRewriterDelegateImpl::GetPrefService() const {
  if (pref_service_for_testing_)
    return pref_service_for_testing_;
  Profile* profile = ProfileManager::GetActiveUserProfile();
  return profile ? profile->GetPrefs() : nullptr;
}

void EventRewriterDelegateImpl::SuppressModifierKeyRewrites(
    bool should_suppress) {
  suppress_modifier_key_rewrites_ = should_suppress;
}

void EventRewriterDelegateImpl::NotifyRightClickRewriteBlockedBySetting(
    ui::mojom::SimulateRightClickModifier blocked_modifier,
    ui::mojom::SimulateRightClickModifier active_modifier) {
  DCHECK(features::IsAltClickAndSixPackCustomizationEnabled());
  input_device_settings_notification_controller_
      ->NotifyRightClickRewriteBlockedBySetting(blocked_modifier,
                                                active_modifier);
}

void EventRewriterDelegateImpl::NotifySixPackRewriteBlockedBySetting(
    ui::KeyboardCode key_code,
    ui::mojom::SixPackShortcutModifier blocked_modifier,
    ui::mojom::SixPackShortcutModifier active_modifier,
    int device_id) {
  DCHECK(ash::features::IsAltClickAndSixPackCustomizationEnabled());
  input_device_settings_notification_controller_
      ->NotifySixPackRewriteBlockedBySetting(key_code, blocked_modifier,
                                             active_modifier, device_id);
}

std::optional<ui::mojom::ExtendedFkeysModifier>
EventRewriterDelegateImpl::GetExtendedFkeySetting(int device_id,
                                                  ui::KeyboardCode key_code) {
  CHECK(key_code == ui::KeyboardCode::VKEY_F11 ||
        key_code == ui::KeyboardCode::VKEY_F12);

  const mojom::KeyboardSettings* settings =
      input_device_settings_controller_->GetKeyboardSettings(device_id);

  if (!settings) {
    return std::nullopt;
  }

  CHECK(settings->f11.has_value() && settings->f12.has_value());
  if (key_code == ui::KeyboardCode::VKEY_F11) {
    return settings->f11;
  }
  return settings->f12;
}

void EventRewriterDelegateImpl::NotifySixPackRewriteBlockedByFnKey(
    ui::KeyboardCode key_code,
    ui::mojom::SixPackShortcutModifier modifier) {
  input_device_settings_notification_controller_->ShowSixPackKeyRewritingNudge(
      key_code, modifier);
}

void EventRewriterDelegateImpl::NotifyTopRowRewriteBlockedByFnKey() {
  input_device_settings_notification_controller_->ShowTopRowRewritingNudge();
}

}  // namespace ash