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

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

Powered by Google App Engine
This is Rietveld 408576698