blob: f6b480c8fa2a044c1d55cc53dd1bced279bf33cb [file] [log] [blame]
// Copyright 2018 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 "services/ws/event_queue.h"
#include "services/ws/window_service.h"
#include "services/ws/window_tree.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tree_host.h"
#include "ui/events/event.h"
namespace ws {
namespace {
constexpr base::TimeDelta kEventAckTimeout =
#if defined(NDEBUG)
base::TimeDelta::FromMilliseconds(100);
#else
base::TimeDelta::FromMilliseconds(1000);
#endif
bool IsQueableEvent(const ui::Event& event) {
return event.IsKeyEvent();
}
} // namespace
struct EventQueue::QueuedEvent {
base::WeakPtr<aura::WindowTreeHost> host; // May be destroyed while queued.
std::unique_ptr<ui::Event> event;
base::OnceClosure callback;
};
EventQueue::EventQueue(WindowService* service) : window_service_(service) {
window_service_->AddObserver(this);
}
EventQueue::~EventQueue() {
window_service_->RemoveObserver(this);
}
base::Optional<ui::EventDispatchDetails> EventQueue::DeliverOrQueueEvent(
aura::WindowTreeHost* host,
ui::Event* event) {
if (!ShouldQueueEvent(host, *event))
return host->GetEventSink()->OnEventFromSource(event);
QueueEvent(host, *event);
return base::nullopt;
}
void EventQueue::NotifyWhenReadyToDispatch(base::OnceClosure closure) {
if (!in_flight_event_.has_value()) {
std::move(closure).Run();
} else {
std::unique_ptr<QueuedEvent> queued_event = std::make_unique<QueuedEvent>();
queued_event->callback = std::move(closure);
queued_events_.push_back(std::move(queued_event));
}
}
bool EventQueue::ShouldQueueEvent(aura::WindowTreeHost* host,
const ui::Event& event) {
if (!in_flight_event_ || !IsQueableEvent(event))
return false;
aura::WindowTargeter* targeter = host->window()->targeter();
if (!targeter)
targeter = host->dispatcher()->event_targeter();
DCHECK(targeter);
aura::Window* target = targeter->FindTargetForKeyEvent(host->window());
return target && WindowService::IsProxyWindow(target);
}
void EventQueue::QueueEvent(aura::WindowTreeHost* host,
const ui::Event& event) {
DCHECK(ShouldQueueEvent(host, event));
std::unique_ptr<QueuedEvent> queued_event = std::make_unique<QueuedEvent>();
queued_event->host = host->GetWeakPtr();
queued_event->event = ui::Event::Clone(event);
queued_events_.push_back(std::move(queued_event));
}
void EventQueue::DispatchNextQueuedEvent() {
while (!queued_events_.empty() && !in_flight_event_) {
std::unique_ptr<QueuedEvent> queued_event =
std::move(*queued_events_.begin());
queued_events_.pop_front();
if (queued_event->callback) {
DCHECK(!queued_event->event) << "Running callback and ignoring event";
std::move(queued_event->callback).Run();
} else if (queued_event->host) {
ignore_result(queued_event->host->GetEventSink()->OnEventFromSource(
queued_event->event.get()));
}
}
}
void EventQueue::OnClientTookTooLongToAckEvent() {
DVLOG(1) << "Client took too long to respond to event";
DCHECK(in_flight_event_);
OnClientAckedEvent(in_flight_event_->client_id, in_flight_event_->event_id);
}
void EventQueue::OnWillSendEventToClient(ClientSpecificId client_id,
uint32_t event_id,
const ui::Event& event) {
if (IsQueableEvent(event)) {
DCHECK(!in_flight_event_);
in_flight_event_ = InFlightEvent();
in_flight_event_->client_id = client_id;
in_flight_event_->event_id = event_id;
ack_timer_.Start(FROM_HERE, kEventAckTimeout, this,
&EventQueue::OnClientTookTooLongToAckEvent);
}
}
void EventQueue::OnClientAckedEvent(ClientSpecificId client_id,
uint32_t event_id) {
if (!in_flight_event_ || in_flight_event_->client_id != client_id ||
in_flight_event_->event_id != event_id) {
return;
}
in_flight_event_.reset();
ack_timer_.Stop();
DispatchNextQueuedEvent();
}
void EventQueue::OnWillDestroyClient(ClientSpecificId client_id) {
if (in_flight_event_ && in_flight_event_->client_id == client_id)
OnClientAckedEvent(client_id, in_flight_event_->event_id);
}
} // namespace ws