Update To 11.40.268.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 <algorithm>
8
9 #include "base/bind.h"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/common/media/midi_messages.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "ipc/ipc_logging.h"
16
17 using media::MidiPortInfoList;
18 using base::AutoLock;
19
20 // The maximum number of bytes which we're allowed to send to the browser
21 // before getting acknowledgement back from the browser that they've been
22 // successfully sent.
23 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024;  // 10 MB.
24
25 namespace content {
26
27 // TODO(crbug.com/425389): Rewrite this class as a RenderFrameObserver.
28 MidiMessageFilter::MidiMessageFilter(
29     const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
30     : sender_(nullptr),
31       io_message_loop_(io_message_loop),
32       main_message_loop_(base::MessageLoopProxy::current()),
33       session_result_(media::MIDI_NOT_INITIALIZED),
34       unacknowledged_bytes_sent_(0u) {
35 }
36
37 MidiMessageFilter::~MidiMessageFilter() {}
38
39 void MidiMessageFilter::AddClient(blink::WebMIDIAccessorClient* client) {
40   DCHECK(main_message_loop_->BelongsToCurrentThread());
41   TRACE_EVENT0("midi", "MidiMessageFilter::AddClient");
42   clients_waiting_session_queue_.push_back(client);
43   if (session_result_ != media::MIDI_NOT_INITIALIZED) {
44     HandleClientAdded(session_result_);
45   } else if (clients_waiting_session_queue_.size() == 1u) {
46     io_message_loop_->PostTask(FROM_HERE,
47         base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this));
48   }
49 }
50
51 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
52   DCHECK(main_message_loop_->BelongsToCurrentThread());
53   clients_.erase(client);
54   ClientsQueue::iterator it = std::find(clients_waiting_session_queue_.begin(),
55                                         clients_waiting_session_queue_.end(),
56                                         client);
57   if (it != clients_waiting_session_queue_.end())
58     clients_waiting_session_queue_.erase(it);
59   if (clients_.empty() && clients_waiting_session_queue_.empty()) {
60     session_result_ = media::MIDI_NOT_INITIALIZED;
61     inputs_.clear();
62     outputs_.clear();
63     io_message_loop_->PostTask(FROM_HERE,
64         base::Bind(&MidiMessageFilter::EndSessionOnIOThread, this));
65   }
66 }
67
68 void MidiMessageFilter::SendMidiData(uint32 port,
69                                      const uint8* data,
70                                      size_t length,
71                                      double timestamp) {
72   DCHECK(main_message_loop_->BelongsToCurrentThread());
73   if ((kMaxUnacknowledgedBytesSent - unacknowledged_bytes_sent_) < length) {
74     // TODO(toyoshim): buffer up the data to send at a later time.
75     // For now we're just dropping these bytes on the floor.
76     return;
77   }
78
79   unacknowledged_bytes_sent_ += length;
80   std::vector<uint8> v(data, data + length);
81   io_message_loop_->PostTask(FROM_HERE, base::Bind(
82         &MidiMessageFilter::SendMidiDataOnIOThread, this, port, v, timestamp));
83 }
84
85 void MidiMessageFilter::StartSessionOnIOThread() {
86   TRACE_EVENT0("midi", "MidiMessageFilter::StartSessionOnIOThread");
87   DCHECK(io_message_loop_->BelongsToCurrentThread());
88   Send(new MidiHostMsg_StartSession());
89 }
90
91 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
92                                                const std::vector<uint8>& data,
93                                                double timestamp) {
94   DCHECK(io_message_loop_->BelongsToCurrentThread());
95   Send(new MidiHostMsg_SendData(port, data, timestamp));
96 }
97
98 void MidiMessageFilter::EndSessionOnIOThread() {
99   DCHECK(io_message_loop_->BelongsToCurrentThread());
100   Send(new MidiHostMsg_EndSession());
101 }
102
103 void MidiMessageFilter::Send(IPC::Message* message) {
104   DCHECK(io_message_loop_->BelongsToCurrentThread());
105   if (!sender_) {
106     delete message;
107   } else {
108     sender_->Send(message);
109   }
110 }
111
112 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
113   DCHECK(io_message_loop_->BelongsToCurrentThread());
114   bool handled = true;
115   IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
116     IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
117     IPC_MESSAGE_HANDLER(MidiMsg_AddInputPort, OnAddInputPort)
118     IPC_MESSAGE_HANDLER(MidiMsg_AddOutputPort, OnAddOutputPort)
119     IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
120     IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
121     IPC_MESSAGE_UNHANDLED(handled = false)
122   IPC_END_MESSAGE_MAP()
123   return handled;
124 }
125
126 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
127   DCHECK(io_message_loop_->BelongsToCurrentThread());
128   sender_ = sender;
129 }
130
131 void MidiMessageFilter::OnFilterRemoved() {
132   DCHECK(io_message_loop_->BelongsToCurrentThread());
133   // Once removed, a filter will not be used again.  At this time all
134   // delegates must be notified so they release their reference.
135   OnChannelClosing();
136 }
137
138 void MidiMessageFilter::OnChannelClosing() {
139   DCHECK(io_message_loop_->BelongsToCurrentThread());
140   sender_ = nullptr;
141 }
142
143 void MidiMessageFilter::OnSessionStarted(media::MidiResult result) {
144   TRACE_EVENT0("midi", "MidiMessageFilter::OnSessionStarted");
145   DCHECK(io_message_loop_->BelongsToCurrentThread());
146   // Handle on the main JS thread.
147   main_message_loop_->PostTask(
148       FROM_HERE,
149       base::Bind(&MidiMessageFilter::HandleClientAdded, this, result));
150 }
151
152 void MidiMessageFilter::OnAddInputPort(media::MidiPortInfo info) {
153   DCHECK(io_message_loop_->BelongsToCurrentThread());
154   main_message_loop_->PostTask(
155       FROM_HERE,
156       base::Bind(&MidiMessageFilter::HandleAddInputPort, this, info));
157 }
158
159 void MidiMessageFilter::OnAddOutputPort(media::MidiPortInfo info) {
160   DCHECK(io_message_loop_->BelongsToCurrentThread());
161   main_message_loop_->PostTask(
162       FROM_HERE,
163       base::Bind(&MidiMessageFilter::HandleAddOutputPort, this, info));
164 }
165
166 void MidiMessageFilter::OnDataReceived(uint32 port,
167                                        const std::vector<uint8>& data,
168                                        double timestamp) {
169   TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
170   DCHECK(io_message_loop_->BelongsToCurrentThread());
171   // Handle on the main JS thread.
172   main_message_loop_->PostTask(
173       FROM_HERE,
174       base::Bind(&MidiMessageFilter::HandleDataReceived, this, port, data,
175                  timestamp));
176 }
177
178 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
179   DCHECK(io_message_loop_->BelongsToCurrentThread());
180   main_message_loop_->PostTask(
181       FROM_HERE,
182       base::Bind(&MidiMessageFilter::HandleAckknowledgeSentData, this,
183                  bytes_sent));
184 }
185
186 void MidiMessageFilter::HandleClientAdded(media::MidiResult result) {
187   TRACE_EVENT0("midi", "MidiMessageFilter::HandleClientAdded");
188   DCHECK(main_message_loop_->BelongsToCurrentThread());
189   session_result_ = result;
190   std::string error;
191   std::string message;
192   switch (result) {
193     case media::MIDI_OK:
194       break;
195     case media::MIDI_NOT_SUPPORTED:
196       error = "NotSupportedError";
197       break;
198     case media::MIDI_INITIALIZATION_ERROR:
199       error = "InvalidStateError";
200       message = "Platform dependent initialization failed.";
201       break;
202     default:
203       NOTREACHED();
204       error = "InvalidStateError";
205       message = "Unknown internal error occurred.";
206       break;
207   }
208   base::string16 error16 = base::UTF8ToUTF16(error);
209   base::string16 message16 = base::UTF8ToUTF16(message);
210   for (blink::WebMIDIAccessorClient* client : clients_waiting_session_queue_) {
211     if (result == media::MIDI_OK) {
212       // Add the client's input and output ports.
213       const bool active = true;
214       for (const auto& info : inputs_) {
215         client->didAddInputPort(
216             base::UTF8ToUTF16(info.id),
217             base::UTF8ToUTF16(info.manufacturer),
218             base::UTF8ToUTF16(info.name),
219             base::UTF8ToUTF16(info.version),
220             active);
221       }
222
223       for (const auto& info : outputs_) {
224         client->didAddOutputPort(
225             base::UTF8ToUTF16(info.id),
226             base::UTF8ToUTF16(info.manufacturer),
227             base::UTF8ToUTF16(info.name),
228             base::UTF8ToUTF16(info.version),
229             active);
230       }
231     }
232     client->didStartSession(result == media::MIDI_OK, error16, message16);
233     clients_.insert(client);
234   }
235   clients_waiting_session_queue_.clear();
236 }
237
238 void MidiMessageFilter::HandleAddInputPort(media::MidiPortInfo info) {
239   DCHECK(main_message_loop_->BelongsToCurrentThread());
240   inputs_.push_back(info);
241   // TODO(toyoshim): Notify to clients that were already added.
242 }
243
244 void MidiMessageFilter::HandleAddOutputPort(media::MidiPortInfo info) {
245   DCHECK(main_message_loop_->BelongsToCurrentThread());
246   outputs_.push_back(info);
247   // TODO(toyoshim): Notify to clients that were already added.
248 }
249
250 void MidiMessageFilter::HandleDataReceived(uint32 port,
251                                            const std::vector<uint8>& data,
252                                            double timestamp) {
253   TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
254   DCHECK(main_message_loop_->BelongsToCurrentThread());
255   DCHECK(!data.empty());
256
257   for (blink::WebMIDIAccessorClient* client : clients_)
258     client->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
259 }
260
261 void MidiMessageFilter::HandleAckknowledgeSentData(size_t bytes_sent) {
262   DCHECK(main_message_loop_->BelongsToCurrentThread());
263   DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
264   if (unacknowledged_bytes_sent_ >= bytes_sent)
265     unacknowledged_bytes_sent_ -= bytes_sent;
266 }
267
268 }  // namespace content