Chromium Code Reviews| Index: content/browser/renderer_host/input/input_queue.cc |
| diff --git a/content/browser/renderer_host/input/input_queue.cc b/content/browser/renderer_host/input/input_queue.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bf3d020fc46471a49309d40e874737dc611d520b |
| --- /dev/null |
| +++ b/content/browser/renderer_host/input/input_queue.cc |
| @@ -0,0 +1,183 @@ |
| +// Copyright (c) 2013 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 "content/browser/renderer_host/input/input_queue.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/debug/trace_event.h" |
| +#include "content/browser/renderer_host/input/browser_input_event.h" |
| +#include "content/browser/renderer_host/input/input_queue_client.h" |
| +#include "content/common/input/event_packet.h" |
| +#include "content/common/input/input_event.h" |
| + |
| +namespace content { |
| + |
| +// A specialized EventPacket with utility methods for dispatched event handling. |
| +class InputQueue::BrowserEventPacket : public EventPacket { |
| + public: |
| + typedef ScopedVector<BrowserInputEvent> BrowserInputEvents; |
| + |
| + BrowserEventPacket() : weak_factory_(this) {} |
| + virtual ~BrowserEventPacket() {} |
| + |
| + // Validate the response and signal dispatch to the processed events. |
| + // Undelivered events will be re-enqueued, and any generated followup events |
| + // will be inserted at the same relative order as their generating event. |
| + AckResult ValidateAndDispatchAck(int64 packet_id, |
| + const InputEventDispositions& dispositions) { |
| + if (!Validate(packet_id, dispositions)) |
| + return ACK_INVALID; |
| + |
| + // Empty the packet; events will be re-enqueued as necessary. |
| + InputEvents dispatched_events; |
| + events_.swap(dispatched_events); |
| + |
| + // The packet could be deleted as a result of event dispatch; use a local |
| + // weak ref to ensure proper shutdown. |
| + base::WeakPtr<EventPacket> weak_ref_this = weak_factory_.GetWeakPtr(); |
| + |
| + BrowserInputEvents followup_events; |
| + for (size_t i = 0; i < dispatched_events.size(); ++i) { |
| + // Take ownership of the event. |
| + scoped_ptr<BrowserInputEvent> event( |
| + static_cast<BrowserInputEvent*>(dispatched_events[i])); |
| + dispatched_events[i] = NULL; |
| + |
| + // Re-enqueue undelivered events. |
| + InputEventDisposition disposition = dispositions[i]; |
| + if (disposition == INPUT_EVENT_COULD_NOT_DELIVER) { |
| + Add(event.PassAs<InputEvent>()); |
| + continue; |
| + } |
| + |
| + event->OnDispatched(disposition, &followup_events); |
| + |
| + if (!weak_ref_this.get()) |
|
aelias_OOO_until_Jul13
2013/09/09 22:31:14
Please add a comment // TODO(jdduke): http://crbug
jdduke (slow)
2013/09/10 19:41:16
Done.
|
| + return ACK_SHUTDOWN; |
| + |
| + AddAll(&followup_events); |
| + } |
| + return ACK_OK; |
| + }; |
| + |
| +protected: |
| + // Add and take ownership of events in |followup_events|. |
| + void AddAll(BrowserInputEvents* followup_events) { |
| + for (BrowserInputEvents::iterator iter = followup_events->begin(); |
| + iter != followup_events->end(); |
| + ++iter) { |
| + Add(scoped_ptr<InputEvent>(*iter)); |
| + } |
| + followup_events->weak_clear(); |
| + } |
| + |
| + // Perform a sanity check of the ack against the current packet. |
| + // |packet_id| should match that of this packet, and |dispositions| should |
| + // be of size equal to the number of events in this packet. |
| + bool Validate(int64 packet_id, |
| + const InputEventDispositions& dispositions) const { |
| + if (packet_id != id()) |
| + return false; |
| + |
| + if (dispositions.size() != size()) |
| + return false; |
| + |
| + return true; |
| + } |
| + |
| +private: |
| + base::WeakPtrFactory<EventPacket> weak_factory_; |
| +}; |
| + |
| +InputQueue::InputQueue(InputQueueClient* client) |
| + : client_(client), |
| + next_packet_id_(1), |
| + flush_requested_(false), |
| + in_flush_packet_(new BrowserEventPacket()), |
| + pending_flush_packet_(new BrowserEventPacket()) { |
| + DCHECK(client_); |
| +} |
| + |
| +InputQueue::~InputQueue() {} |
| + |
| +void InputQueue::QueueEvent(scoped_ptr<BrowserInputEvent> event) { |
| + DCHECK(event); |
| + DCHECK(event->valid()); |
| + pending_flush_packet_->Add(event.PassAs<InputEvent>()); |
| + RequestFlushIfNecessary(); |
| +} |
| + |
| +void InputQueue::BeginFlush() { |
| + // Ignore repeated flush attempts. |
| + if (!flush_requested_) |
| + return; |
| + |
| + DCHECK(!FlushInProgress()); |
| + DCHECK(!pending_flush_packet_->empty()); |
| + |
| + flush_requested_ = false; |
| + in_flush_packet_.swap(pending_flush_packet_); |
| + DeliverInFlushPacket(); |
| +} |
| + |
| +InputQueue::AckResult InputQueue::OnEventPacketAck( |
| + int64 packet_id, const InputEventDispositions& dispositions) { |
| + if (!FlushInProgress()) |
| + return ACK_UNEXPECTED; |
| + |
| + TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "AckPacket", |
| + "id", packet_id); |
| + |
| + AckResult ack_result = |
| + in_flush_packet_->ValidateAndDispatchAck(packet_id, dispositions); |
| + |
| + if (ack_result != ACK_OK) |
| + return ack_result; |
| + |
| + if (FlushInProgress()) { |
| + DeliverInFlushPacket(); |
| + } else { |
| + TRACE_EVENT_ASYNC_END0("input", "InputQueueFlush", this); |
| + client_->DidFinishFlush(); |
| + RequestFlushIfNecessary(); |
| + } |
| + |
| + return ACK_OK; |
| +} |
| + |
| +size_t InputQueue::QueuedEventCount() const { |
| + return in_flush_packet_->size() + pending_flush_packet_->size(); |
| +} |
| + |
| +void InputQueue::DeliverInFlushPacket() { |
| + DCHECK(FlushInProgress()); |
| + TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "DeliverPacket", |
| + "id", next_packet_id_); |
| + in_flush_packet_->set_id(next_packet_id_++); |
| + client_->Deliver(*in_flush_packet_); |
| +} |
| + |
| +void InputQueue::RequestFlushIfNecessary() { |
| + if (flush_requested_) |
| + return; |
| + |
| + // Defer flush requests until the current flush has finished. |
| + if (FlushInProgress()) |
| + return; |
| + |
| + // No additional events to flush. |
| + if (pending_flush_packet_->empty()) |
| + return; |
| + |
| + TRACE_EVENT_ASYNC_BEGIN0("input", "InputQueueFlush", this); |
| + TRACE_EVENT_ASYNC_STEP0("input", "InputQueueFlush", this, "Request"); |
| + flush_requested_ = true; |
| + client_->SetNeedsFlush(); |
| +} |
| + |
| +bool InputQueue::FlushInProgress() const { |
| + return !in_flush_packet_->empty(); |
| +} |
| + |
| +} // namespace content |