Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / midi_message_filter.cc
1 // Copyright (c) 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/media/midi_message_filter.h"
6
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/common/media/midi_messages.h"
12 #include "content/renderer/render_thread_impl.h"
13 #include "ipc/ipc_logging.h"
14
15 using media::MidiPortInfoList;
16 using base::AutoLock;
17
18 // The maximum number of bytes which we're allowed to send to the browser
19 // before getting acknowledgement back from the browser that they've been
20 // successfully sent.
21 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024;  // 10 MB.
22
23 namespace content {
24
25 MidiMessageFilter::MidiMessageFilter(
26     const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
27     : channel_(NULL),
28       io_message_loop_(io_message_loop),
29       main_message_loop_(base::MessageLoopProxy::current()),
30       next_available_id_(0),
31       unacknowledged_bytes_sent_(0) {
32 }
33
34 MidiMessageFilter::~MidiMessageFilter() {}
35
36 void MidiMessageFilter::Send(IPC::Message* message) {
37   DCHECK(io_message_loop_->BelongsToCurrentThread());
38   if (!channel_) {
39     delete message;
40   } else {
41     channel_->Send(message);
42   }
43 }
44
45 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
46   DCHECK(io_message_loop_->BelongsToCurrentThread());
47   bool handled = true;
48   IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
49     IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
50     IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
51     IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
52     IPC_MESSAGE_UNHANDLED(handled = false)
53   IPC_END_MESSAGE_MAP()
54   return handled;
55 }
56
57 void MidiMessageFilter::OnFilterAdded(IPC::Channel* channel) {
58   DCHECK(io_message_loop_->BelongsToCurrentThread());
59   channel_ = channel;
60 }
61
62 void MidiMessageFilter::OnFilterRemoved() {
63   DCHECK(io_message_loop_->BelongsToCurrentThread());
64
65   // Once removed, a filter will not be used again.  At this time all
66   // delegates must be notified so they release their reference.
67   OnChannelClosing();
68 }
69
70 void MidiMessageFilter::OnChannelClosing() {
71   DCHECK(io_message_loop_->BelongsToCurrentThread());
72   channel_ = NULL;
73 }
74
75 void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) {
76   // Generate and keep track of a "client id" which is sent to the browser
77   // to ask permission to talk to MIDI hardware.
78   // This id is handed back when we receive the answer in OnAccessApproved().
79   if (clients_.find(client) == clients_.end()) {
80     int client_id = next_available_id_++;
81     clients_[client] = client_id;
82
83     io_message_loop_->PostTask(FROM_HERE,
84         base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this,
85                    client_id));
86   }
87 }
88
89 void MidiMessageFilter::StartSessionOnIOThread(int client_id) {
90   Send(new MidiHostMsg_StartSession(client_id));
91 }
92
93 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
94   ClientsMap::iterator i = clients_.find(client);
95   if (i != clients_.end())
96     clients_.erase(i);
97 }
98
99 // Received from browser.
100
101 void MidiMessageFilter::OnSessionStarted(
102     int client_id,
103     bool success,
104     MidiPortInfoList inputs,
105     MidiPortInfoList outputs) {
106   // Handle on the main JS thread.
107   main_message_loop_->PostTask(
108       FROM_HERE,
109       base::Bind(&MidiMessageFilter::HandleSessionStarted, this,
110                  client_id, success, inputs, outputs));
111 }
112
113 void MidiMessageFilter::HandleSessionStarted(
114     int client_id,
115     bool success,
116     MidiPortInfoList inputs,
117     MidiPortInfoList outputs) {
118   blink::WebMIDIAccessorClient* client = GetClientFromId(client_id);
119   if (!client)
120     return;
121
122   if (success) {
123     // Add the client's input and output ports.
124     for (size_t i = 0; i < inputs.size(); ++i) {
125       client->didAddInputPort(
126           base::UTF8ToUTF16(inputs[i].id),
127           base::UTF8ToUTF16(inputs[i].manufacturer),
128           base::UTF8ToUTF16(inputs[i].name),
129           base::UTF8ToUTF16(inputs[i].version));
130     }
131
132     for (size_t i = 0; i < outputs.size(); ++i) {
133       client->didAddOutputPort(
134           base::UTF8ToUTF16(outputs[i].id),
135           base::UTF8ToUTF16(outputs[i].manufacturer),
136           base::UTF8ToUTF16(outputs[i].name),
137           base::UTF8ToUTF16(outputs[i].version));
138     }
139   }
140   client->didStartSession(success);
141 }
142
143 blink::WebMIDIAccessorClient*
144 MidiMessageFilter::GetClientFromId(int client_id) {
145   // Iterating like this seems inefficient, but in practice there generally
146   // will be very few clients (usually one).  Additionally, this lookup
147   // usually happens one time during page load. So the performance hit is
148   // negligible.
149   for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) {
150     if ((*i).second == client_id)
151       return (*i).first;
152   }
153   return NULL;
154 }
155
156 void MidiMessageFilter::OnDataReceived(uint32 port,
157                                        const std::vector<uint8>& data,
158                                        double timestamp) {
159   TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
160
161   main_message_loop_->PostTask(
162       FROM_HERE,
163       base::Bind(&MidiMessageFilter::HandleDataReceived, this,
164                  port, data, timestamp));
165 }
166
167 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
168   DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
169   if (unacknowledged_bytes_sent_ >= bytes_sent)
170     unacknowledged_bytes_sent_ -= bytes_sent;
171 }
172
173 void MidiMessageFilter::HandleDataReceived(uint32 port,
174                                            const std::vector<uint8>& data,
175                                            double timestamp) {
176   DCHECK(!data.empty());
177   TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
178
179   for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i)
180     (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
181 }
182
183 void MidiMessageFilter::SendMidiData(uint32 port,
184                                      const uint8* data,
185                                      size_t length,
186                                      double timestamp) {
187   if (length > kMaxUnacknowledgedBytesSent) {
188     // TODO(toyoshim): buffer up the data to send at a later time.
189     // For now we're just dropping these bytes on the floor.
190     return;
191   }
192
193   std::vector<uint8> v(data, data + length);
194   io_message_loop_->PostTask(FROM_HERE,
195       base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
196                  port, v, timestamp));
197 }
198
199 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
200                                                const std::vector<uint8>& data,
201                                                double timestamp) {
202   size_t n = data.size();
203   if (n > kMaxUnacknowledgedBytesSent ||
204       unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
205       n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
206     // TODO(toyoshim): buffer up the data to send at a later time.
207     // For now we're just dropping these bytes on the floor.
208     return;
209   }
210
211   unacknowledged_bytes_sent_ += n;
212
213   // Send to the browser.
214   Send(new MidiHostMsg_SendData(port, data, timestamp));
215 }
216
217 }  // namespace content