blob: c27f584b389668335cfca6c1ea48e1aa0bf7ace7 [file] [log] [blame]
// Copyright 2015 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/events/spoken_feedback_event_rewriter.h"
#include <string>
#include <utility>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/shell.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event.h"
#include "ui/events/event_sink.h"
namespace ash {
SpokenFeedbackEventRewriter::SpokenFeedbackEventRewriter()
: continuation_(nullptr) {}
SpokenFeedbackEventRewriter::~SpokenFeedbackEventRewriter() = default;
void SpokenFeedbackEventRewriter::SetDelegate(
mojom::SpokenFeedbackEventRewriterDelegatePtr delegate) {
delegate_ = std::move(delegate);
}
void SpokenFeedbackEventRewriter::OnUnhandledSpokenFeedbackEvent(
std::unique_ptr<ui::Event> event) const {
DCHECK(event->IsKeyEvent()) << "Unexpected unhandled event type";
// Send the event to the most recently rewritten event's continuation,
// (that is, through its EventSource). Under the assumption that a single
// SpokenFeedbackEventRewriter is not registered to multiple EventSources,
// this will be the same as this event's original source.
const char* failure_reason = nullptr;
if (continuation_) {
ui::EventDispatchDetails details = SendEvent(continuation_, event.get());
if (details.dispatcher_destroyed)
failure_reason = "destroyed dispatcher";
else if (details.target_destroyed)
failure_reason = "destroyed target";
} else if (continuation_.WasInvalidated()) {
failure_reason = "destroyed source";
} else {
failure_reason = "no prior rewrite";
}
if (failure_reason) {
VLOG(0) << "Undispatched key " << event->AsKeyEvent()->key_code()
<< " due to " << failure_reason << ".";
}
}
ui::EventDispatchDetails SpokenFeedbackEventRewriter::RewriteEvent(
const ui::Event& event,
const Continuation continuation) {
// Save continuation for |OnUnhandledSpokenFeedbackEvent()|.
continuation_ = continuation;
if (!delegate_.is_bound() ||
!Shell::Get()->accessibility_controller()->spoken_feedback_enabled())
return SendEvent(continuation, &event);
if (event.IsKeyEvent()) {
const ui::KeyEvent* key_event = event.AsKeyEvent();
bool capture = capture_all_keys_;
// Always capture the Search key.
capture |= key_event->IsCommandDown();
// Don't capture tab as it gets consumed by Blink so never comes back
// unhandled. In third_party/WebKit/Source/core/input/EventHandler.cpp, a
// default tab handler consumes tab even when no focusable nodes are found;
// it sets focus to Chrome and eats the event.
if (key_event->GetDomKey() == ui::DomKey::TAB)
capture = false;
delegate_->DispatchKeyEventToChromeVox(ui::Event::Clone(event), capture);
return capture ? DiscardEvent(continuation)
: SendEvent(continuation, &event);
}
if (send_mouse_events_ && event.IsMouseEvent()) {
delegate_->DispatchMouseEventToChromeVox(ui::Event::Clone(event));
}
return SendEvent(continuation, &event);
}
} // namespace ash