blob: bbdc6a509ed0805ee8ee8bbb8031160b5f35be13 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/accelerators/accelerator_history_impl.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "ui/events/event.h"
#include "ui/events/event_target.h"
namespace ash {
namespace {
bool ShouldFilter(ui::KeyEvent* event) {
const ui::EventType type = event->type();
if (!event->target() ||
(type != ui::ET_KEY_PRESSED && type != ui::ET_KEY_RELEASED) ||
event->is_char() || !event->target() ||
// Key events with key code of VKEY_PROCESSKEY, usually created by virtual
// keyboard (like handwriting input), have no effect on accelerator and
// they may disturb the accelerator history. So filter them out. (see
// https://crbug.com/918317)
event->key_code() == ui::VKEY_PROCESSKEY) {
return true;
}
return false;
}
} // namespace
AcceleratorHistoryImpl::AcceleratorHistoryImpl() = default;
AcceleratorHistoryImpl::~AcceleratorHistoryImpl() = default;
void AcceleratorHistoryImpl::OnKeyEvent(ui::KeyEvent* event) {
DCHECK(event->target());
if (!ShouldFilter(event))
StoreCurrentAccelerator(ui::Accelerator(*event));
}
void AcceleratorHistoryImpl::OnMouseEvent(ui::MouseEvent* event) {
if (event->type() == ui::ET_MOUSE_PRESSED ||
event->type() == ui::ET_MOUSE_RELEASED) {
InterruptCurrentAccelerator();
}
}
void AcceleratorHistoryImpl::StoreCurrentAccelerator(
const ui::Accelerator& accelerator) {
// Track the currently pressed keys so that we don't mistakenly store an
// already pressed key as a new keypress after another key has been released.
// As an example, when the user presses and holds Alt+Search, then releases
// Alt but keeps holding the Search key down, at this point no new Search
// presses should be stored in the history after the Alt release, since Search
// was never released in the first place. crbug.com/704280.
if (accelerator.key_state() == ui::Accelerator::KeyState::PRESSED) {
if (!currently_pressed_keys_.emplace(accelerator.key_code()).second)
return;
} else {
if (!currently_pressed_keys_.erase(accelerator.key_code())) {
// If the released accelerator doesn't have a corresponding press stored,
// likely the language was changed between press and release. Clear
// `currently_pressed_keys_` to prevent keys being left pressed.
std::string pressed_keys;
for (auto key_code : currently_pressed_keys_)
pressed_keys.append(base::NumberToString(key_code).append(" "));
LOG(WARNING) << "Key Release (" << accelerator.key_code()
<< ") delivered with no corresponding Press. "
"Clearing all pressed keys: "
<< pressed_keys;
currently_pressed_keys_.clear();
}
}
if (accelerator != current_accelerator_) {
previous_accelerator_ = current_accelerator_;
current_accelerator_ = accelerator;
}
}
void AcceleratorHistoryImpl::InterruptCurrentAccelerator() {
if (current_accelerator_.key_state() == ui::Accelerator::KeyState::PRESSED) {
// Only interrupts pressed keys.
current_accelerator_.set_interrupted_by_mouse_event(true);
}
}
} // namespace ash