Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: content/renderer/media/midi_message_filter.cc

Issue 662853003: Manage MIDI related objects and sessions' lifecycles correctly (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: unittest Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/midi_message_filter.h" 5 #include "content/renderer/media/midi_message_filter.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/debug/trace_event.h" 8 #include "base/debug/trace_event.h"
9 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
11 #include "content/common/media/midi_messages.h" 11 #include "content/common/media/midi_messages.h"
12 #include "content/renderer/render_thread_impl.h" 12 #include "content/renderer/render_thread_impl.h"
13 #include "ipc/ipc_logging.h" 13 #include "ipc/ipc_logging.h"
14 14
15 using media::MidiPortInfoList; 15 using media::MidiPortInfoList;
16 using base::AutoLock; 16 using base::AutoLock;
17 17
18 // The maximum number of bytes which we're allowed to send to the browser 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 19 // before getting acknowledgement back from the browser that they've been
20 // successfully sent. 20 // successfully sent.
21 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB. 21 static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB.
22 22
23 namespace content { 23 namespace content {
24 24
25 MidiMessageFilter::MidiMessageFilter( 25 MidiMessageFilter::MidiMessageFilter(
26 const scoped_refptr<base::MessageLoopProxy>& io_message_loop) 26 const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
27 : sender_(NULL), 27 : sender_(NULL),
28 io_message_loop_(io_message_loop), 28 io_message_loop_(io_message_loop),
29 main_message_loop_(base::MessageLoopProxy::current()), 29 main_message_loop_(base::MessageLoopProxy::current()),
30 next_available_id_(0), 30 session_result_(media::MIDI_NOT_INITIALIZED),
31 unacknowledged_bytes_sent_(0) { 31 unacknowledged_bytes_sent_(0) {
32 } 32 }
33 33
34 MidiMessageFilter::~MidiMessageFilter() {} 34 MidiMessageFilter::~MidiMessageFilter() {}
35 35
36 void MidiMessageFilter::AddClient(blink::WebMIDIAccessorClient* client) {
37 TRACE_EVENT0("midi", "MidiMessageFilter::AddClient");
38 base::AutoLock auto_lock(lock_);
39 if (session_result_ != media::MIDI_NOT_INITIALIZED) {
yhirano 2014/10/20 12:15:14 Is it OK to behave as if there is no error when se
Takashi Toyoshima 2014/10/20 17:14:44 Yes, I think it's ok to cache the error results he
40 main_message_loop_->PostTask(FROM_HERE,
41 base::Bind(&MidiMessageFilter::HandleClientAdded, this, client));
42 } else if (!clients_waiting_session_queue_.empty()) {
43 clients_waiting_session_queue_.push_back(client);
44 } else {
45 clients_waiting_session_queue_.push_back(client);
46 io_message_loop_->PostTask(FROM_HERE,
47 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this));
48 }
49 }
50
51 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
52 base::AutoLock auto_lock(lock_);
53 ClientsSet::iterator set_it = clients_.find(client);
yhirano 2014/10/20 12:15:14 Isn't |client_.erase(client)| enough?
Takashi Toyoshima 2014/10/20 17:14:44 Done.
54 if (set_it != clients_.end())
55 clients_.erase(set_it);
56 for (ClientsQueue::iterator queue_it = clients_waiting_session_queue_.begin();
yhirano 2014/10/20 12:15:14 How about using std::find?
Takashi Toyoshima 2014/10/20 17:14:43 Done.
57 queue_it != clients_waiting_session_queue_.end(); ++queue_it) {
58 if (*queue_it == client) {
59 clients_waiting_session_queue_.erase(queue_it);
60 break;
61 }
62 }
63 if (clients_.empty() && clients_waiting_session_queue_.empty()) {
64 session_result_ = media::MIDI_NOT_INITIALIZED;
65 io_message_loop_->PostTask(FROM_HERE,
66 base::Bind(&MidiMessageFilter::EndSessionOnIOThread, this));
67 }
68 }
69
70 void MidiMessageFilter::SendMidiData(uint32 port,
71 const uint8* data,
72 size_t length,
73 double timestamp) {
74 if (length > kMaxUnacknowledgedBytesSent) {
75 // TODO(toyoshim): buffer up the data to send at a later time.
76 // For now we're just dropping these bytes on the floor.
77 return;
78 }
79
80 std::vector<uint8> v(data, data + length);
81 io_message_loop_->PostTask(FROM_HERE,
82 base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
83 port, v, timestamp));
84 }
85
86 void MidiMessageFilter::StartSessionOnIOThread() {
87 TRACE_EVENT0("midi", "MidiMessageFilter::StartSessionOnIOThread");
88 DCHECK(io_message_loop_->BelongsToCurrentThread());
89 Send(new MidiHostMsg_StartSession());
90 }
91
92 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
93 const std::vector<uint8>& data,
94 double timestamp) {
95 DCHECK(io_message_loop_->BelongsToCurrentThread());
96 size_t n = data.size();
97 if (n > kMaxUnacknowledgedBytesSent ||
98 unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
99 n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
100 // TODO(toyoshim): buffer up the data to send at a later time.
101 // For now we're just dropping these bytes on the floor.
102 return;
103 }
104
105 unacknowledged_bytes_sent_ += n;
106
107 // Send to the browser.
108 Send(new MidiHostMsg_SendData(port, data, timestamp));
109 }
110
111 void MidiMessageFilter::EndSessionOnIOThread() {
112 DCHECK(io_message_loop_->BelongsToCurrentThread());
113 Send(new MidiHostMsg_EndSession());
114 }
115
36 void MidiMessageFilter::Send(IPC::Message* message) { 116 void MidiMessageFilter::Send(IPC::Message* message) {
37 DCHECK(io_message_loop_->BelongsToCurrentThread()); 117 DCHECK(io_message_loop_->BelongsToCurrentThread());
38 if (!sender_) { 118 if (!sender_) {
39 delete message; 119 delete message;
40 } else { 120 } else {
41 sender_->Send(message); 121 sender_->Send(message);
42 } 122 }
43 } 123 }
44 124
45 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) { 125 bool MidiMessageFilter::OnMessageReceived(const IPC::Message& message) {
46 DCHECK(io_message_loop_->BelongsToCurrentThread()); 126 DCHECK(io_message_loop_->BelongsToCurrentThread());
47 bool handled = true; 127 bool handled = true;
48 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message) 128 IPC_BEGIN_MESSAGE_MAP(MidiMessageFilter, message)
49 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted) 129 IPC_MESSAGE_HANDLER(MidiMsg_SessionStarted, OnSessionStarted)
50 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived) 130 IPC_MESSAGE_HANDLER(MidiMsg_DataReceived, OnDataReceived)
51 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData) 131 IPC_MESSAGE_HANDLER(MidiMsg_AcknowledgeSentData, OnAcknowledgeSentData)
52 IPC_MESSAGE_UNHANDLED(handled = false) 132 IPC_MESSAGE_UNHANDLED(handled = false)
53 IPC_END_MESSAGE_MAP() 133 IPC_END_MESSAGE_MAP()
54 return handled; 134 return handled;
55 } 135 }
56 136
57 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) { 137 void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
58 DCHECK(io_message_loop_->BelongsToCurrentThread()); 138 DCHECK(io_message_loop_->BelongsToCurrentThread());
59 sender_ = sender; 139 sender_ = sender;
60 } 140 }
61 141
62 void MidiMessageFilter::OnFilterRemoved() { 142 void MidiMessageFilter::OnFilterRemoved() {
63 DCHECK(io_message_loop_->BelongsToCurrentThread()); 143 DCHECK(io_message_loop_->BelongsToCurrentThread());
64
65 // Once removed, a filter will not be used again. At this time all 144 // Once removed, a filter will not be used again. At this time all
66 // delegates must be notified so they release their reference. 145 // delegates must be notified so they release their reference.
67 OnChannelClosing(); 146 OnChannelClosing();
68 } 147 }
69 148
70 void MidiMessageFilter::OnChannelClosing() { 149 void MidiMessageFilter::OnChannelClosing() {
71 DCHECK(io_message_loop_->BelongsToCurrentThread()); 150 DCHECK(io_message_loop_->BelongsToCurrentThread());
72 sender_ = NULL; 151 sender_ = NULL;
73 } 152 }
74 153
75 void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) { 154 void MidiMessageFilter::OnSessionStarted(media::MidiResult result,
76 // Generate and keep track of a "client id" which is sent to the browser 155 MidiPortInfoList inputs,
77 // to ask permission to talk to MIDI hardware. 156 MidiPortInfoList outputs) {
78 // This id is handed back when we receive the answer in OnAccessApproved(). 157 TRACE_EVENT0("midi", "MidiMessageFilter::OnSessionStarted");
79 if (clients_.find(client) == clients_.end()) { 158 DCHECK(io_message_loop_->BelongsToCurrentThread());
80 int client_id = next_available_id_++; 159 base::AutoLock auto_lock(lock_);
81 clients_[client] = client_id; 160 session_result_ = result;
82 161 inputs_ = inputs;
83 io_message_loop_->PostTask(FROM_HERE, 162 outputs_ = outputs;
84 base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this, 163 // Handle on the main JS thread.
85 client_id)); 164 for (ClientsQueue::iterator it = clients_waiting_session_queue_.begin();
yhirano 2014/10/20 12:15:14 You can use the range-based for.
Takashi Toyoshima 2014/10/20 17:14:44 Done.
165 it != clients_waiting_session_queue_.end(); ++it) {
166 main_message_loop_->PostTask(FROM_HERE,
167 base::Bind(&MidiMessageFilter::HandleClientAdded, this, *it));
86 } 168 }
169 clients_waiting_session_queue_.clear();
87 } 170 }
88 171
89 void MidiMessageFilter::StartSessionOnIOThread(int client_id) { 172 void MidiMessageFilter::OnDataReceived(uint32 port,
90 Send(new MidiHostMsg_StartSession(client_id)); 173 const std::vector<uint8>& data,
174 double timestamp) {
175 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
176 DCHECK(io_message_loop_->BelongsToCurrentThread());
177 main_message_loop_->PostTask(
178 FROM_HERE,
179 base::Bind(&MidiMessageFilter::HandleDataReceived, this,
180 port, data, timestamp));
91 } 181 }
92 182
93 void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) { 183 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
94 ClientsMap::iterator i = clients_.find(client); 184 DCHECK(io_message_loop_->BelongsToCurrentThread());
95 if (i != clients_.end()) 185 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
96 clients_.erase(i); 186 if (unacknowledged_bytes_sent_ >= bytes_sent)
187 unacknowledged_bytes_sent_ -= bytes_sent;
97 } 188 }
98 189
99 // Received from browser. 190 void MidiMessageFilter::HandleClientAdded(
191 blink::WebMIDIAccessorClient* client) {
192 TRACE_EVENT0("midi", "MidiMessageFilter::HandleClientAdded");
193 DCHECK(main_message_loop_->BelongsToCurrentThread());
194 media::MidiResult result;
195 {
196 base::AutoLock auto_lock(lock_);
197 result = session_result_;
198 if (result == media::MIDI_OK) {
yhirano 2014/10/20 12:15:14 You can bring this block out of auto_lock's scope.
Takashi Toyoshima 2014/10/20 17:14:43 Done.
199 // Add the client's input and output ports.
200 for (size_t i = 0; i < inputs_.size(); ++i) {
yhirano 2014/10/20 12:15:14 You can use the range-based for.
Takashi Toyoshima 2014/10/20 17:14:44 Done.
201 client->didAddInputPort(
202 base::UTF8ToUTF16(inputs_[i].id),
203 base::UTF8ToUTF16(inputs_[i].manufacturer),
204 base::UTF8ToUTF16(inputs_[i].name),
205 base::UTF8ToUTF16(inputs_[i].version));
206 }
100 207
101 void MidiMessageFilter::OnSessionStarted( 208 for (size_t i = 0; i < outputs_.size(); ++i) {
yhirano 2014/10/20 12:15:14 ditto
Takashi Toyoshima 2014/10/20 17:14:44 Done.
102 int client_id, 209 client->didAddOutputPort(
103 media::MidiResult result, 210 base::UTF8ToUTF16(outputs_[i].id),
104 MidiPortInfoList inputs, 211 base::UTF8ToUTF16(outputs_[i].manufacturer),
105 MidiPortInfoList outputs) { 212 base::UTF8ToUTF16(outputs_[i].name),
106 // Handle on the main JS thread. 213 base::UTF8ToUTF16(outputs_[i].version));
107 main_message_loop_->PostTask( 214 }
108 FROM_HERE,
109 base::Bind(&MidiMessageFilter::HandleSessionStarted, this,
110 client_id, result, inputs, outputs));
111 }
112
113 void MidiMessageFilter::HandleSessionStarted(
114 int client_id,
115 media::MidiResult result,
116 MidiPortInfoList inputs,
117 MidiPortInfoList outputs) {
118 blink::WebMIDIAccessorClient* client = GetClientFromId(client_id);
119 if (!client)
120 return;
121
122 if (result == media::MIDI_OK) {
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 } 215 }
131 216 clients_.insert(client);
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 } 217 }
140 std::string error; 218 std::string error;
141 std::string message; 219 std::string message;
142 switch (result) { 220 switch (result) {
143 case media::MIDI_OK: 221 case media::MIDI_OK:
144 break; 222 break;
145 case media::MIDI_NOT_SUPPORTED: 223 case media::MIDI_NOT_SUPPORTED:
146 error = "NotSupportedError"; 224 error = "NotSupportedError";
147 break; 225 break;
148 case media::MIDI_INITIALIZATION_ERROR: 226 case media::MIDI_INITIALIZATION_ERROR:
149 error = "InvalidStateError"; 227 error = "InvalidStateError";
150 message = "Platform dependent initialization failed."; 228 message = "Platform dependent initialization failed.";
151 break; 229 break;
152 default: 230 default:
153 NOTREACHED(); 231 NOTREACHED();
154 error = "InvalidStateError"; 232 error = "InvalidStateError";
155 message = "Unknown internal error occurred."; 233 message = "Unknown internal error occurred.";
156 break; 234 break;
157 } 235 }
158 client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error), 236 client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error),
159 base::UTF8ToUTF16(message)); 237 base::UTF8ToUTF16(message));
160 } 238 }
161 239
162 blink::WebMIDIAccessorClient*
163 MidiMessageFilter::GetClientFromId(int client_id) {
164 // Iterating like this seems inefficient, but in practice there generally
165 // will be very few clients (usually one). Additionally, this lookup
166 // usually happens one time during page load. So the performance hit is
167 // negligible.
168 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) {
169 if ((*i).second == client_id)
170 return (*i).first;
171 }
172 return NULL;
173 }
174
175 void MidiMessageFilter::OnDataReceived(uint32 port,
176 const std::vector<uint8>& data,
177 double timestamp) {
178 TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
179
180 main_message_loop_->PostTask(
181 FROM_HERE,
182 base::Bind(&MidiMessageFilter::HandleDataReceived, this,
183 port, data, timestamp));
184 }
185
186 void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
187 DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
188 if (unacknowledged_bytes_sent_ >= bytes_sent)
189 unacknowledged_bytes_sent_ -= bytes_sent;
190 }
191
192 void MidiMessageFilter::HandleDataReceived(uint32 port, 240 void MidiMessageFilter::HandleDataReceived(uint32 port,
193 const std::vector<uint8>& data, 241 const std::vector<uint8>& data,
194 double timestamp) { 242 double timestamp) {
243 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
244 DCHECK(main_message_loop_->BelongsToCurrentThread());
195 DCHECK(!data.empty()); 245 DCHECK(!data.empty());
196 TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
197 246
198 for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) 247 for (ClientsSet::iterator it = clients_.begin(); it != clients_.end(); ++it)
yhirano 2014/10/20 12:15:14 You can use the range-based for.
Takashi Toyoshima 2014/10/20 17:14:44 Done.
199 (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp); 248 (*it)->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
200 }
201
202 void MidiMessageFilter::SendMidiData(uint32 port,
203 const uint8* data,
204 size_t length,
205 double timestamp) {
206 if (length > kMaxUnacknowledgedBytesSent) {
207 // TODO(toyoshim): buffer up the data to send at a later time.
208 // For now we're just dropping these bytes on the floor.
209 return;
210 }
211
212 std::vector<uint8> v(data, data + length);
213 io_message_loop_->PostTask(FROM_HERE,
214 base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
215 port, v, timestamp));
216 }
217
218 void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
219 const std::vector<uint8>& data,
220 double timestamp) {
221 size_t n = data.size();
222 if (n > kMaxUnacknowledgedBytesSent ||
223 unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
224 n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
225 // TODO(toyoshim): buffer up the data to send at a later time.
226 // For now we're just dropping these bytes on the floor.
227 return;
228 }
229
230 unacknowledged_bytes_sent_ += n;
231
232 // Send to the browser.
233 Send(new MidiHostMsg_SendData(port, data, timestamp));
234 } 249 }
235 250
236 } // namespace content 251 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698