Home | History | Annotate | Download | only in input
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/renderer/input/input_event_filter.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/bind.h"
      9 #include "base/command_line.h"
     10 #include "base/debug/trace_event.h"
     11 #include "base/location.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/single_thread_task_runner.h"
     14 #include "cc/input/input_handler.h"
     15 #include "content/common/input/did_overscroll_params.h"
     16 #include "content/common/input/web_input_event_traits.h"
     17 #include "content/common/input_messages.h"
     18 #include "content/common/view_messages.h"
     19 #include "content/public/common/content_switches.h"
     20 #include "ipc/ipc_listener.h"
     21 #include "ipc/ipc_sender.h"
     22 #include "ui/gfx/vector2d_f.h"
     23 
     24 using blink::WebInputEvent;
     25 
     26 #include "ipc/ipc_message_null_macros.h"
     27 #undef IPC_MESSAGE_DECL
     28 #define IPC_MESSAGE_DECL(kind, type, name, in, out, ilist, olist) \
     29   case name::ID: return #name;
     30 
     31 const char* GetInputMessageTypeName(const IPC::Message& message) {
     32   switch (message.type()) {
     33 #include "content/common/input_messages.h"
     34     default:
     35       NOTREACHED() << "Invalid message type: " << message.type();
     36       break;
     37   };
     38   return "NonInputMsgType";
     39 }
     40 
     41 namespace content {
     42 
     43 InputEventFilter::InputEventFilter(
     44     IPC::Listener* main_listener,
     45     const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
     46     const scoped_refptr<base::MessageLoopProxy>& target_loop)
     47     : main_task_runner_(main_task_runner),
     48       main_listener_(main_listener),
     49       sender_(NULL),
     50       target_loop_(target_loop),
     51       overscroll_notifications_enabled_(false),
     52       current_overscroll_params_(NULL) {
     53   DCHECK(target_loop_.get());
     54   overscroll_notifications_enabled_ =
     55       CommandLine::ForCurrentProcess()->HasSwitch(
     56           switches::kEnableOverscrollNotifications);
     57 }
     58 
     59 void InputEventFilter::SetBoundHandler(const Handler& handler) {
     60   DCHECK(main_task_runner_->BelongsToCurrentThread());
     61   handler_ = handler;
     62 }
     63 
     64 void InputEventFilter::DidAddInputHandler(int routing_id,
     65                                           cc::InputHandler* input_handler) {
     66   base::AutoLock locked(routes_lock_);
     67   routes_.insert(routing_id);
     68 }
     69 
     70 void InputEventFilter::DidRemoveInputHandler(int routing_id) {
     71   base::AutoLock locked(routes_lock_);
     72   routes_.erase(routing_id);
     73 }
     74 
     75 void InputEventFilter::DidOverscroll(int routing_id,
     76                                      const DidOverscrollParams& params) {
     77   if (!overscroll_notifications_enabled_)
     78     return;
     79 
     80   if (current_overscroll_params_) {
     81     current_overscroll_params_->reset(new DidOverscrollParams(params));
     82     return;
     83   }
     84 
     85   SendMessage(scoped_ptr<IPC::Message>(
     86       new InputHostMsg_DidOverscroll(routing_id, params)));
     87 }
     88 
     89 void InputEventFilter::DidStopFlinging(int routing_id) {
     90   SendMessage(
     91       scoped_ptr<IPC::Message>(new ViewHostMsg_DidStopFlinging(routing_id)));
     92 }
     93 
     94 void InputEventFilter::OnFilterAdded(IPC::Sender* sender) {
     95   io_loop_ = base::MessageLoopProxy::current();
     96   sender_ = sender;
     97 }
     98 
     99 void InputEventFilter::OnFilterRemoved() {
    100   sender_ = NULL;
    101 }
    102 
    103 void InputEventFilter::OnChannelClosing() {
    104   sender_ = NULL;
    105 }
    106 
    107 // This function returns true if the IPC message is one that the compositor
    108 // thread can handle *or* one that needs to preserve relative order with
    109 // messages that the compositor thread can handle. Returning true for a message
    110 // type means that the message will go through an extra copy and thread hop, so
    111 // use with care.
    112 static bool RequiresThreadBounce(const IPC::Message& message) {
    113   return IPC_MESSAGE_ID_CLASS(message.type()) == InputMsgStart;
    114 }
    115 
    116 bool InputEventFilter::OnMessageReceived(const IPC::Message& message) {
    117   if (!RequiresThreadBounce(message))
    118     return false;
    119 
    120   TRACE_EVENT0("input", "InputEventFilter::OnMessageReceived::InputMessage");
    121 
    122   {
    123     base::AutoLock locked(routes_lock_);
    124     if (routes_.find(message.routing_id()) == routes_.end())
    125       return false;
    126   }
    127 
    128   target_loop_->PostTask(
    129       FROM_HERE,
    130       base::Bind(&InputEventFilter::ForwardToHandler, this, message));
    131   return true;
    132 }
    133 
    134 InputEventFilter::~InputEventFilter() {
    135   DCHECK(!current_overscroll_params_);
    136 }
    137 
    138 void InputEventFilter::ForwardToMainListener(const IPC::Message& message) {
    139   main_listener_->OnMessageReceived(message);
    140 }
    141 
    142 void InputEventFilter::ForwardToHandler(const IPC::Message& message) {
    143   DCHECK(!handler_.is_null());
    144   DCHECK(target_loop_->BelongsToCurrentThread());
    145   TRACE_EVENT1("input", "InputEventFilter::ForwardToHandler",
    146                "message_type", GetInputMessageTypeName(message));
    147 
    148   if (message.type() != InputMsg_HandleInputEvent::ID) {
    149     TRACE_EVENT_INSTANT0(
    150         "input",
    151         "InputEventFilter::ForwardToHandler::ForwardToMainListener",
    152         TRACE_EVENT_SCOPE_THREAD);
    153     main_task_runner_->PostTask(
    154         FROM_HERE,
    155         base::Bind(&InputEventFilter::ForwardToMainListener, this, message));
    156     return;
    157   }
    158 
    159   int routing_id = message.routing_id();
    160   InputMsg_HandleInputEvent::Param params;
    161   if (!InputMsg_HandleInputEvent::Read(&message, &params))
    162     return;
    163   const WebInputEvent* event = params.a;
    164   ui::LatencyInfo latency_info = params.b;
    165   bool is_keyboard_shortcut = params.c;
    166   DCHECK(event);
    167 
    168   const bool send_ack = !WebInputEventTraits::IgnoresAckDisposition(*event);
    169 
    170   // Intercept |DidOverscroll| notifications, bundling any triggered overscroll
    171   // response with the input event ack.
    172   scoped_ptr<DidOverscrollParams> overscroll_params;
    173   base::AutoReset<scoped_ptr<DidOverscrollParams>*>
    174       auto_reset_current_overscroll_params(
    175           &current_overscroll_params_, send_ack ? &overscroll_params : NULL);
    176 
    177   InputEventAckState ack_state = handler_.Run(routing_id, event, &latency_info);
    178 
    179   if (ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) {
    180     DCHECK(!overscroll_params);
    181     TRACE_EVENT_INSTANT0(
    182         "input",
    183         "InputEventFilter::ForwardToHandler::ForwardToMainListener",
    184         TRACE_EVENT_SCOPE_THREAD);
    185     IPC::Message new_msg = InputMsg_HandleInputEvent(
    186         routing_id, event, latency_info, is_keyboard_shortcut);
    187     main_task_runner_->PostTask(
    188         FROM_HERE,
    189         base::Bind(&InputEventFilter::ForwardToMainListener, this, new_msg));
    190     return;
    191   }
    192 
    193   if (!send_ack)
    194     return;
    195 
    196   InputHostMsg_HandleInputEvent_ACK_Params ack;
    197   ack.type = event->type;
    198   ack.state = ack_state;
    199   ack.latency = latency_info;
    200   ack.overscroll = overscroll_params.Pass();
    201   SendMessage(scoped_ptr<IPC::Message>(
    202       new InputHostMsg_HandleInputEvent_ACK(routing_id, ack)));
    203 }
    204 
    205 void InputEventFilter::SendMessage(scoped_ptr<IPC::Message> message) {
    206   DCHECK(target_loop_->BelongsToCurrentThread());
    207 
    208   io_loop_->PostTask(FROM_HERE,
    209                      base::Bind(&InputEventFilter::SendMessageOnIOThread,
    210                                 this,
    211                                 base::Passed(&message)));
    212 }
    213 
    214 void InputEventFilter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
    215   DCHECK(io_loop_->BelongsToCurrentThread());
    216 
    217   if (!sender_)
    218     return;  // Filter was removed.
    219 
    220   sender_->Send(message.release());
    221 }
    222 
    223 }  // namespace content
    224