| 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..9e7bf654df5671d736cdf907cf78c2d25524a76f
|
| --- /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);
|
| +
|
| + // TODO(jdduke): http://crbug.com/274029
|
| + if (!weak_ref_this.get())
|
| + 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
|
|
|