summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortoyoshim <toyoshim@chromium.org>2014-10-23 00:17:59 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-23 07:18:17 +0000
commit71fcb150d9e2cb5e2f05069177ac611ac9c189c0 (patch)
tree5067da853f0f8b7d7b52a0fb632ad93e33701fe7
parent7a0ddbb2ced7ed2117ae177773714968f66a66b5 (diff)
downloadchromium_src-71fcb150d9e2cb5e2f05069177ac611ac9c189c0.zip
chromium_src-71fcb150d9e2cb5e2f05069177ac611ac9c189c0.tar.gz
chromium_src-71fcb150d9e2cb5e2f05069177ac611ac9c189c0.tar.bz2
Manage MIDI related objects and sessions' lifecycles correctly
Following changes are needed to maintain MIDIPortInfoList contents consistent in each MidiMessageFilter instances when MidiManager update the list asynchronously. Essential changes: - Call EndSession iff a session is open. - Do not use client_id to communicate between browser and renderer. Multiple clients are managed in a MidiMessageFilter existing in each renderer. Minor changes: - Remove midi_manager_ checks in MIDIHost, and add CHECK insteads. - Rename MidiManagerFilter::StartSession to MidiManagerFilter::AddClient to be aligned with RemoveClient. A session can be shared with multiple clients. After this change, MidiManager::EndSession is called from each Renderer and RendererHost at the right timing, e.g., GC collects the last MIDIAccess reference in JavaScript. See, also http://crbug.com/424859. BUG=424859 Review URL: https://codereview.chromium.org/662853003 Cr-Commit-Position: refs/heads/master@{#300851}
-rw-r--r--content/browser/media/midi_host.cc26
-rw-r--r--content/browser/media/midi_host.h10
-rw-r--r--content/common/media/midi_messages.h8
-rw-r--r--content/renderer/media/midi_message_filter.cc265
-rw-r--r--content/renderer/media/midi_message_filter.h58
-rw-r--r--content/renderer/media/renderer_webmidiaccessor_impl.cc2
-rw-r--r--media/midi/midi_manager.cc28
-rw-r--r--media/midi/midi_manager.h12
-rw-r--r--media/midi/midi_manager_unittest.cc37
-rw-r--r--media/midi/midi_manager_usb_unittest.cc4
-rw-r--r--media/midi/midi_result.h3
11 files changed, 240 insertions, 213 deletions
diff --git a/content/browser/media/midi_host.cc b/content/browser/media/midi_host.cc
index 950cf48..fb4dcce 100644
--- a/content/browser/media/midi_host.cc
+++ b/content/browser/media/midi_host.cc
@@ -52,13 +52,16 @@ MidiHost::MidiHost(int renderer_process_id, media::MidiManager* midi_manager)
: BrowserMessageFilter(MidiMsgStart),
renderer_process_id_(renderer_process_id),
has_sys_ex_permission_(false),
+ is_session_requested_(false),
midi_manager_(midi_manager),
sent_bytes_in_flight_(0),
bytes_sent_since_last_acknowledgement_(0) {
+ CHECK(midi_manager_);
}
MidiHost::~MidiHost() {
- if (midi_manager_)
+ // Close an open session, or abort opening a session.
+ if (is_session_requested_)
midi_manager_->EndSession(this);
}
@@ -72,23 +75,21 @@ bool MidiHost::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(MidiHost, message)
IPC_MESSAGE_HANDLER(MidiHostMsg_StartSession, OnStartSession)
IPC_MESSAGE_HANDLER(MidiHostMsg_SendData, OnSendData)
+ IPC_MESSAGE_HANDLER(MidiHostMsg_EndSession, OnEndSession)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
-void MidiHost::OnStartSession(int client_id) {
- if (midi_manager_)
- midi_manager_->StartSession(this, client_id);
+void MidiHost::OnStartSession() {
+ is_session_requested_ = true;
+ midi_manager_->StartSession(this);
}
void MidiHost::OnSendData(uint32 port,
const std::vector<uint8>& data,
double timestamp) {
- if (!midi_manager_)
- return;
-
if (data.empty())
return;
@@ -117,7 +118,13 @@ void MidiHost::OnSendData(uint32 port,
midi_manager_->DispatchSendMidiData(this, port, data, timestamp);
}
-void MidiHost::CompleteStartSession(int client_id, media::MidiResult result) {
+void MidiHost::OnEndSession() {
+ is_session_requested_ = false;
+ midi_manager_->EndSession(this);
+}
+
+void MidiHost::CompleteStartSession(media::MidiResult result) {
+ DCHECK(is_session_requested_);
MidiPortInfoList input_ports;
MidiPortInfoList output_ports;
@@ -132,8 +139,7 @@ void MidiHost::CompleteStartSession(int client_id, media::MidiResult result) {
CanSendMidiSysExMessage(renderer_process_id_);
}
- Send(new MidiMsg_SessionStarted(client_id,
- result,
+ Send(new MidiMsg_SessionStarted(result,
input_ports,
output_ports));
}
diff --git a/content/browser/media/midi_host.h b/content/browser/media/midi_host.h
index 87be305..acaee52 100644
--- a/content/browser/media/midi_host.h
+++ b/content/browser/media/midi_host.h
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
+#include "base/synchronization/lock.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
@@ -35,7 +36,7 @@ class CONTENT_EXPORT MidiHost
bool OnMessageReceived(const IPC::Message& message) override;
// MidiManagerClient implementation.
- void CompleteStartSession(int client_id, media::MidiResult result) override;
+ void CompleteStartSession(media::MidiResult result) override;
void ReceiveMidiData(uint32 port,
const uint8* data,
size_t length,
@@ -43,13 +44,15 @@ class CONTENT_EXPORT MidiHost
void AccumulateMidiBytesSent(size_t n) override;
// Start session to access MIDI hardware.
- void OnStartSession(int client_id);
+ void OnStartSession();
// Data to be sent to a MIDI output port.
void OnSendData(uint32 port,
const std::vector<uint8>& data,
double timestamp);
+ void OnEndSession();
+
private:
FRIEND_TEST_ALL_PREFIXES(MidiHostTest, IsValidWebMIDIData);
friend class base::DeleteHelper<MidiHost>;
@@ -70,6 +73,9 @@ class CONTENT_EXPORT MidiHost
// messages.
bool has_sys_ex_permission_;
+ // Represents if a session is requested to start.
+ bool is_session_requested_;
+
// |midi_manager_| talks to the platform-specific MIDI APIs.
// It can be NULL if the platform (or our current implementation)
// does not support MIDI. If not supported then a call to
diff --git a/content/common/media/midi_messages.h b/content/common/media/midi_messages.h
index da131397..c1375c3 100644
--- a/content/common/media/midi_messages.h
+++ b/content/common/media/midi_messages.h
@@ -43,18 +43,18 @@ IPC_MESSAGE_ROUTED2(MidiMsg_SysExPermissionApproved,
// Messages for IPC between MidiMessageFilter and MidiHost.
// Renderer request to browser for access to MIDI services.
-IPC_MESSAGE_CONTROL1(MidiHostMsg_StartSession,
- int /* client id */)
+IPC_MESSAGE_CONTROL0(MidiHostMsg_StartSession)
IPC_MESSAGE_CONTROL3(MidiHostMsg_SendData,
uint32 /* port */,
std::vector<uint8> /* data */,
double /* timestamp */)
+IPC_MESSAGE_CONTROL0(MidiHostMsg_EndSession)
+
// Messages sent from the browser to the renderer.
-IPC_MESSAGE_CONTROL4(MidiMsg_SessionStarted,
- int /* client id */,
+IPC_MESSAGE_CONTROL3(MidiMsg_SessionStarted,
media::MidiResult /* result */,
media::MidiPortInfoList /* input ports */,
media::MidiPortInfoList /* output ports */)
diff --git a/content/renderer/media/midi_message_filter.cc b/content/renderer/media/midi_message_filter.cc
index 165451a..8f08ff4 100644
--- a/content/renderer/media/midi_message_filter.cc
+++ b/content/renderer/media/midi_message_filter.cc
@@ -4,6 +4,8 @@
#include "content/renderer/media/midi_message_filter.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/message_loop/message_loop_proxy.h"
@@ -22,17 +24,80 @@ static const size_t kMaxUnacknowledgedBytesSent = 10 * 1024 * 1024; // 10 MB.
namespace content {
+// TODO(crbug.com/425389): Rewrite this class as a RenderFrameObserver.
MidiMessageFilter::MidiMessageFilter(
const scoped_refptr<base::MessageLoopProxy>& io_message_loop)
: sender_(NULL),
io_message_loop_(io_message_loop),
main_message_loop_(base::MessageLoopProxy::current()),
- next_available_id_(0),
- unacknowledged_bytes_sent_(0) {
+ session_result_(media::MIDI_NOT_INITIALIZED),
+ unacknowledged_bytes_sent_(0u) {
}
MidiMessageFilter::~MidiMessageFilter() {}
+void MidiMessageFilter::AddClient(blink::WebMIDIAccessorClient* client) {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ TRACE_EVENT0("midi", "MidiMessageFilter::AddClient");
+ clients_waiting_session_queue_.push_back(client);
+ if (session_result_ != media::MIDI_NOT_INITIALIZED) {
+ HandleClientAdded(session_result_);
+ } else if (clients_waiting_session_queue_.size() == 1u) {
+ io_message_loop_->PostTask(FROM_HERE,
+ base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this));
+ }
+}
+
+void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ clients_.erase(client);
+ ClientsQueue::iterator it = std::find(clients_waiting_session_queue_.begin(),
+ clients_waiting_session_queue_.end(),
+ client);
+ if (it != clients_waiting_session_queue_.end())
+ clients_waiting_session_queue_.erase(it);
+ if (clients_.empty() && clients_waiting_session_queue_.empty()) {
+ session_result_ = media::MIDI_NOT_INITIALIZED;
+ io_message_loop_->PostTask(FROM_HERE,
+ base::Bind(&MidiMessageFilter::EndSessionOnIOThread, this));
+ }
+}
+
+void MidiMessageFilter::SendMidiData(uint32 port,
+ const uint8* data,
+ size_t length,
+ double timestamp) {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ if ((kMaxUnacknowledgedBytesSent - unacknowledged_bytes_sent_) < length) {
+ // TODO(toyoshim): buffer up the data to send at a later time.
+ // For now we're just dropping these bytes on the floor.
+ return;
+ }
+
+ unacknowledged_bytes_sent_ += length;
+ std::vector<uint8> v(data, data + length);
+ io_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &MidiMessageFilter::SendMidiDataOnIOThread, this, port, v, timestamp));
+}
+
+void MidiMessageFilter::StartSessionOnIOThread() {
+ TRACE_EVENT0("midi", "MidiMessageFilter::StartSessionOnIOThread");
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ Send(new MidiHostMsg_StartSession());
+}
+
+void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
+ const std::vector<uint8>& data,
+ double timestamp) {
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ Send(new MidiHostMsg_SendData(port, data, timestamp));
+}
+
+void MidiMessageFilter::EndSessionOnIOThread() {
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ Send(new MidiHostMsg_EndSession());
+}
+
void MidiMessageFilter::Send(IPC::Message* message) {
DCHECK(io_message_loop_->BelongsToCurrentThread());
if (!sender_) {
@@ -61,7 +126,6 @@ void MidiMessageFilter::OnFilterAdded(IPC::Sender* sender) {
void MidiMessageFilter::OnFilterRemoved() {
DCHECK(io_message_loop_->BelongsToCurrentThread());
-
// Once removed, a filter will not be used again. At this time all
// delegates must be notified so they release their reference.
OnChannelClosing();
@@ -72,74 +136,47 @@ void MidiMessageFilter::OnChannelClosing() {
sender_ = NULL;
}
-void MidiMessageFilter::StartSession(blink::WebMIDIAccessorClient* client) {
- // Generate and keep track of a "client id" which is sent to the browser
- // to ask permission to talk to MIDI hardware.
- // This id is handed back when we receive the answer in OnAccessApproved().
- if (clients_.find(client) == clients_.end()) {
- int client_id = next_available_id_++;
- clients_[client] = client_id;
-
- io_message_loop_->PostTask(FROM_HERE,
- base::Bind(&MidiMessageFilter::StartSessionOnIOThread, this,
- client_id));
- }
-}
-
-void MidiMessageFilter::StartSessionOnIOThread(int client_id) {
- Send(new MidiHostMsg_StartSession(client_id));
-}
-
-void MidiMessageFilter::RemoveClient(blink::WebMIDIAccessorClient* client) {
- ClientsMap::iterator i = clients_.find(client);
- if (i != clients_.end())
- clients_.erase(i);
+void MidiMessageFilter::OnSessionStarted(media::MidiResult result,
+ MidiPortInfoList inputs,
+ MidiPortInfoList outputs) {
+ TRACE_EVENT0("midi", "MidiMessageFilter::OnSessionStarted");
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ // TODO(toyoshim): |inputs_| and |outputs_| should not be updated on
+ // |io_message_loop_|. This should be fixed in a following change not to
+ // distribute MidiPortInfo via OnSessionStarted().
+ // For now, this is safe because these are not updated later.
+ inputs_ = inputs;
+ outputs_ = outputs;
+ // Handle on the main JS thread.
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&MidiMessageFilter::HandleClientAdded, this, result));
}
-// Received from browser.
-
-void MidiMessageFilter::OnSessionStarted(
- int client_id,
- media::MidiResult result,
- MidiPortInfoList inputs,
- MidiPortInfoList outputs) {
+void MidiMessageFilter::OnDataReceived(uint32 port,
+ const std::vector<uint8>& data,
+ double timestamp) {
+ TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
// Handle on the main JS thread.
main_message_loop_->PostTask(
FROM_HERE,
- base::Bind(&MidiMessageFilter::HandleSessionStarted, this,
- client_id, result, inputs, outputs));
+ base::Bind(&MidiMessageFilter::HandleDataReceived, this, port, data,
+ timestamp));
}
-void MidiMessageFilter::HandleSessionStarted(
- int client_id,
- media::MidiResult result,
- MidiPortInfoList inputs,
- MidiPortInfoList outputs) {
- blink::WebMIDIAccessorClient* client = GetClientFromId(client_id);
- if (!client)
- return;
-
- if (result == media::MIDI_OK) {
- // Add the client's input and output ports.
- const bool active = true;
- for (size_t i = 0; i < inputs.size(); ++i) {
- client->didAddInputPort(
- base::UTF8ToUTF16(inputs[i].id),
- base::UTF8ToUTF16(inputs[i].manufacturer),
- base::UTF8ToUTF16(inputs[i].name),
- base::UTF8ToUTF16(inputs[i].version),
- active);
- }
+void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
+ DCHECK(io_message_loop_->BelongsToCurrentThread());
+ main_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&MidiMessageFilter::HandleAckknowledgeSentData, this,
+ bytes_sent));
+}
- for (size_t i = 0; i < outputs.size(); ++i) {
- client->didAddOutputPort(
- base::UTF8ToUTF16(outputs[i].id),
- base::UTF8ToUTF16(outputs[i].manufacturer),
- base::UTF8ToUTF16(outputs[i].name),
- base::UTF8ToUTF16(outputs[i].version),
- active);
- }
- }
+void MidiMessageFilter::HandleClientAdded(media::MidiResult result) {
+ TRACE_EVENT0("midi", "MidiMessageFilter::HandleClientAdded");
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ session_result_ = result;
std::string error;
std::string message;
switch (result) {
@@ -158,82 +195,52 @@ void MidiMessageFilter::HandleSessionStarted(
message = "Unknown internal error occurred.";
break;
}
- client->didStartSession(result == media::MIDI_OK, base::UTF8ToUTF16(error),
- base::UTF8ToUTF16(message));
-}
-
-blink::WebMIDIAccessorClient*
-MidiMessageFilter::GetClientFromId(int client_id) {
- // Iterating like this seems inefficient, but in practice there generally
- // will be very few clients (usually one). Additionally, this lookup
- // usually happens one time during page load. So the performance hit is
- // negligible.
- for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i) {
- if ((*i).second == client_id)
- return (*i).first;
+ base::string16 error16 = base::UTF8ToUTF16(error);
+ base::string16 message16 = base::UTF8ToUTF16(message);
+ for (blink::WebMIDIAccessorClient* client : clients_waiting_session_queue_) {
+ if (result == media::MIDI_OK) {
+ // Add the client's input and output ports.
+ const bool active = true;
+ for (const auto& info : inputs_) {
+ client->didAddInputPort(
+ base::UTF8ToUTF16(info.id),
+ base::UTF8ToUTF16(info.manufacturer),
+ base::UTF8ToUTF16(info.name),
+ base::UTF8ToUTF16(info.version),
+ active);
+ }
+
+ for (const auto& info : outputs_) {
+ client->didAddOutputPort(
+ base::UTF8ToUTF16(info.id),
+ base::UTF8ToUTF16(info.manufacturer),
+ base::UTF8ToUTF16(info.name),
+ base::UTF8ToUTF16(info.version),
+ active);
+ }
+ }
+ client->didStartSession(result == media::MIDI_OK, error16, message16);
+ clients_.insert(client);
}
- return NULL;
-}
-
-void MidiMessageFilter::OnDataReceived(uint32 port,
- const std::vector<uint8>& data,
- double timestamp) {
- TRACE_EVENT0("midi", "MidiMessageFilter::OnDataReceived");
-
- main_message_loop_->PostTask(
- FROM_HERE,
- base::Bind(&MidiMessageFilter::HandleDataReceived, this,
- port, data, timestamp));
-}
-
-void MidiMessageFilter::OnAcknowledgeSentData(size_t bytes_sent) {
- DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
- if (unacknowledged_bytes_sent_ >= bytes_sent)
- unacknowledged_bytes_sent_ -= bytes_sent;
+ clients_waiting_session_queue_.clear();
}
void MidiMessageFilter::HandleDataReceived(uint32 port,
const std::vector<uint8>& data,
double timestamp) {
- DCHECK(!data.empty());
TRACE_EVENT0("midi", "MidiMessageFilter::HandleDataReceived");
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK(!data.empty());
- for (ClientsMap::iterator i = clients_.begin(); i != clients_.end(); ++i)
- (*i).first->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
-}
-
-void MidiMessageFilter::SendMidiData(uint32 port,
- const uint8* data,
- size_t length,
- double timestamp) {
- if (length > kMaxUnacknowledgedBytesSent) {
- // TODO(toyoshim): buffer up the data to send at a later time.
- // For now we're just dropping these bytes on the floor.
- return;
- }
-
- std::vector<uint8> v(data, data + length);
- io_message_loop_->PostTask(FROM_HERE,
- base::Bind(&MidiMessageFilter::SendMidiDataOnIOThread, this,
- port, v, timestamp));
+ for (blink::WebMIDIAccessorClient* client : clients_)
+ client->didReceiveMIDIData(port, &data[0], data.size(), timestamp);
}
-void MidiMessageFilter::SendMidiDataOnIOThread(uint32 port,
- const std::vector<uint8>& data,
- double timestamp) {
- size_t n = data.size();
- if (n > kMaxUnacknowledgedBytesSent ||
- unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent ||
- n + unacknowledged_bytes_sent_ > kMaxUnacknowledgedBytesSent) {
- // TODO(toyoshim): buffer up the data to send at a later time.
- // For now we're just dropping these bytes on the floor.
- return;
- }
-
- unacknowledged_bytes_sent_ += n;
-
- // Send to the browser.
- Send(new MidiHostMsg_SendData(port, data, timestamp));
+void MidiMessageFilter::HandleAckknowledgeSentData(size_t bytes_sent) {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK_GE(unacknowledged_bytes_sent_, bytes_sent);
+ if (unacknowledged_bytes_sent_ >= bytes_sent)
+ unacknowledged_bytes_sent_ -= bytes_sent;
}
} // namespace content
diff --git a/content/renderer/media/midi_message_filter.h b/content/renderer/media/midi_message_filter.h
index 4aa560a..34cee3d 100644
--- a/content/renderer/media/midi_message_filter.h
+++ b/content/renderer/media/midi_message_filter.h
@@ -5,7 +5,7 @@
#ifndef CONTENT_RENDERER_MEDIA_MIDI_MESSAGE_FILTER_H_
#define CONTENT_RENDERER_MEDIA_MIDI_MESSAGE_FILTER_H_
-#include <map>
+#include <set>
#include <vector>
#include "base/memory/scoped_ptr.h"
@@ -29,9 +29,7 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
// Each client registers for MIDI access here.
// If permission is granted, then the client's
- // addInputPort() and addOutputPort() methods will be called,
- // giving the client access to receive and send data.
- void StartSession(blink::WebMIDIAccessorClient* client);
+ void AddClient(blink::WebMIDIAccessorClient* client);
void RemoveClient(blink::WebMIDIAccessorClient* client);
// A client will only be able to call this method if it has a suitable
@@ -50,6 +48,14 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
~MidiMessageFilter() override;
private:
+ void StartSessionOnIOThread();
+
+ void SendMidiDataOnIOThread(uint32 port,
+ const std::vector<uint8>& data,
+ double timestamp);
+
+ void EndSessionOnIOThread();
+
// Sends an IPC message using |sender_|.
void Send(IPC::Message* message);
@@ -61,8 +67,9 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
// Called when the browser process has approved (or denied) access to
// MIDI hardware.
- void OnSessionStarted(int client_id,
- media::MidiResult result,
+ // TODO(toyoshim): MidiPortInfoList objects should be notified separately
+ // port by port.
+ void OnSessionStarted(media::MidiResult result,
media::MidiPortInfoList inputs,
media::MidiPortInfoList outputs);
@@ -77,22 +84,14 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
// sending too much data before knowing how much has already been sent.
void OnAcknowledgeSentData(size_t bytes_sent);
- void HandleSessionStarted(int client_id,
- media::MidiResult result,
- media::MidiPortInfoList inputs,
- media::MidiPortInfoList outputs);
+ // Following methods, Handle*, run on |main_message_loop_|.
+ void HandleClientAdded(media::MidiResult result);
void HandleDataReceived(uint32 port,
const std::vector<uint8>& data,
double timestamp);
- void StartSessionOnIOThread(int client_id);
-
- void SendMidiDataOnIOThread(uint32 port,
- const std::vector<uint8>& data,
- double timestamp);
-
- blink::WebMIDIAccessorClient* GetClientFromId(int client_id);
+ void HandleAckknowledgeSentData(size_t bytes_sent);
// IPC sender for Send(); must only be accessed on |io_message_loop_|.
IPC::Sender* sender_;
@@ -103,15 +102,24 @@ class CONTENT_EXPORT MidiMessageFilter : public IPC::MessageFilter {
// Main thread's message loop.
scoped_refptr<base::MessageLoopProxy> main_message_loop_;
+ /*
+ * Notice: Following members are designed to be accessed only on
+ * |main_message_loop_|.
+ */
// Keeps track of all MIDI clients.
- // We map client to "client id" used to track permission.
- // When access has been approved, we add the input and output ports to
- // the client, allowing it to actually receive and send MIDI data.
- typedef std::map<blink::WebMIDIAccessorClient*, int> ClientsMap;
- ClientsMap clients_;
-
- // Dishes out client ids.
- int next_available_id_;
+ typedef std::set<blink::WebMIDIAccessorClient*> ClientsSet;
+ ClientsSet clients_;
+
+ // Represents clients that are waiting for a session being open.
+ typedef std::vector<blink::WebMIDIAccessorClient*> ClientsQueue;
+ ClientsQueue clients_waiting_session_queue_;
+
+ // Represents a result on starting a session. Can be accessed only on
+ media::MidiResult session_result_;
+
+ // Holds MidiPortInfoList for input ports and output ports.
+ media::MidiPortInfoList inputs_;
+ media::MidiPortInfoList outputs_;
size_t unacknowledged_bytes_sent_;
diff --git a/content/renderer/media/renderer_webmidiaccessor_impl.cc b/content/renderer/media/renderer_webmidiaccessor_impl.cc
index 75b6b972..b67f48a 100644
--- a/content/renderer/media/renderer_webmidiaccessor_impl.cc
+++ b/content/renderer/media/renderer_webmidiaccessor_impl.cc
@@ -21,7 +21,7 @@ RendererWebMIDIAccessorImpl::~RendererWebMIDIAccessorImpl() {
}
void RendererWebMIDIAccessorImpl::startSession() {
- midi_message_filter()->StartSession(client_);
+ midi_message_filter()->AddClient(client_);
}
void RendererWebMIDIAccessorImpl::sendMIDIData(
diff --git a/media/midi/midi_manager.cc b/media/midi/midi_manager.cc
index c53eef4..b0439ec 100644
--- a/media/midi/midi_manager.cc
+++ b/media/midi/midi_manager.cc
@@ -26,7 +26,7 @@ MidiManager* MidiManager::Create() {
}
#endif
-void MidiManager::StartSession(MidiManagerClient* client, int client_id) {
+void MidiManager::StartSession(MidiManagerClient* client) {
bool session_is_ready;
bool session_needs_initialization = false;
bool too_many_pending_clients_exist = false;
@@ -34,6 +34,12 @@ void MidiManager::StartSession(MidiManagerClient* client, int client_id) {
{
base::AutoLock auto_lock(lock_);
session_is_ready = initialized_;
+ if (clients_.find(client) != clients_.end() ||
+ pending_clients_.find(client) != pending_clients_.end()) {
+ // Should not happen. But just in case the renderer is compromised.
+ NOTREACHED();
+ return;
+ }
if (!session_is_ready) {
// Do not accept a new request if the pending client list contains too
// many clients.
@@ -43,7 +49,7 @@ void MidiManager::StartSession(MidiManagerClient* client, int client_id) {
if (!too_many_pending_clients_exist) {
// Call StartInitialization() only for the first request.
session_needs_initialization = pending_clients_.empty();
- pending_clients_.insert(std::make_pair(client, client_id));
+ pending_clients_.insert(client);
}
}
}
@@ -58,7 +64,7 @@ void MidiManager::StartSession(MidiManagerClient* client, int client_id) {
}
if (too_many_pending_clients_exist) {
// Return an error immediately if there are too many requests.
- client->CompleteStartSession(client_id, MIDI_INITIALIZATION_ERROR);
+ client->CompleteStartSession(MIDI_INITIALIZATION_ERROR);
return;
}
// CompleteInitialization() will be called asynchronously when platform
@@ -75,10 +81,12 @@ void MidiManager::StartSession(MidiManagerClient* client, int client_id) {
clients_.insert(client);
result = result_;
}
- client->CompleteStartSession(client_id, result);
+ client->CompleteStartSession(result);
}
void MidiManager::EndSession(MidiManagerClient* client) {
+ // At this point, |client| can be in the destruction process, and calling
+ // any method of |client| is dangerous.
base::AutoLock auto_lock(lock_);
clients_.erase(client);
pending_clients_.erase(client);
@@ -121,8 +129,8 @@ void MidiManager::ReceiveMidiData(
double timestamp) {
base::AutoLock auto_lock(lock_);
- for (ClientList::iterator i = clients_.begin(); i != clients_.end(); ++i)
- (*i)->ReceiveMidiData(port_index, data, length, timestamp);
+ for (MidiManagerClient* client : clients_)
+ client->ReceiveMidiData(port_index, data, length, timestamp);
}
void MidiManager::CompleteInitializationInternal(MidiResult result) {
@@ -134,12 +142,10 @@ void MidiManager::CompleteInitializationInternal(MidiResult result) {
initialized_ = true;
result_ = result;
- for (PendingClientMap::iterator it = pending_clients_.begin();
- it != pending_clients_.end();
- ++it) {
+ for (MidiManagerClient* client : pending_clients_) {
if (result_ == MIDI_OK)
- clients_.insert(it->first);
- it->first->CompleteStartSession(it->second, result_);
+ clients_.insert(client);
+ client->CompleteStartSession(result_);
}
pending_clients_.clear();
}
diff --git a/media/midi/midi_manager.h b/media/midi/midi_manager.h
index 9fd7a21..1e2bcb8 100644
--- a/media/midi/midi_manager.h
+++ b/media/midi/midi_manager.h
@@ -5,7 +5,6 @@
#ifndef MEDIA_MIDI_MIDI_MANAGER_H_
#define MEDIA_MIDI_MIDI_MANAGER_H_
-#include <map>
#include <set>
#include <vector>
@@ -32,7 +31,7 @@ class MEDIA_EXPORT MidiManagerClient {
// CompleteStartSession() is called when platform dependent preparation is
// finished.
- virtual void CompleteStartSession(int client_id, MidiResult result) = 0;
+ virtual void CompleteStartSession(MidiResult result) = 0;
// ReceiveMidiData() is called when MIDI data has been received from the
// MIDI system.
@@ -71,7 +70,7 @@ class MEDIA_EXPORT MidiManager {
// Otherwise CompleteStartSession() is called with proper MidiResult code.
// StartSession() and EndSession() can be called on the Chrome_IOThread.
// CompleteStartSession() will be invoked on the same Chrome_IOThread.
- void StartSession(MidiManagerClient* client, int client_id);
+ void StartSession(MidiManagerClient* client);
// A client calls EndSession() to stop receiving MIDI data.
void EndSession(MidiManagerClient* client);
@@ -148,12 +147,11 @@ class MEDIA_EXPORT MidiManager {
void CompleteInitializationInternal(MidiResult result);
// Keeps track of all clients who wish to receive MIDI data.
- typedef std::set<MidiManagerClient*> ClientList;
- ClientList clients_;
+ typedef std::set<MidiManagerClient*> ClientSet;
+ ClientSet clients_;
// Keeps track of all clients who are waiting for CompleteStartSession().
- typedef std::multimap<MidiManagerClient*, int> PendingClientMap;
- PendingClientMap pending_clients_;
+ ClientSet pending_clients_;
// Keeps a SingleThreadTaskRunner of the thread that calls StartSession in
// order to invoke CompleteStartSession() on the thread.
diff --git a/media/midi/midi_manager_unittest.cc b/media/midi/midi_manager_unittest.cc
index 846e1bc..4a52ca6 100644
--- a/media/midi/midi_manager_unittest.cc
+++ b/media/midi/midi_manager_unittest.cc
@@ -54,16 +54,14 @@ class FakeMidiManager : public MidiManager {
class FakeMidiManagerClient : public MidiManagerClient {
public:
- explicit FakeMidiManagerClient(int client_id)
- : client_id_(client_id),
- result_(MIDI_NOT_SUPPORTED),
+ FakeMidiManagerClient()
+ : result_(MIDI_NOT_SUPPORTED),
wait_for_result_(true) {}
~FakeMidiManagerClient() override {}
// MidiManagerClient implementation.
- void CompleteStartSession(int client_id, MidiResult result) override {
+ void CompleteStartSession(MidiResult result) override {
EXPECT_TRUE(wait_for_result_);
- CHECK_EQ(client_id_, client_id);
result_ = result;
wait_for_result_ = false;
}
@@ -74,7 +72,6 @@ class FakeMidiManagerClient : public MidiManagerClient {
double timestamp) override {}
void AccumulateMidiBytesSent(size_t size) override {}
- int client_id() const { return client_id_; }
MidiResult result() const { return result_; }
MidiResult WaitForResult() {
@@ -86,7 +83,6 @@ class FakeMidiManagerClient : public MidiManagerClient {
}
private:
- int client_id_;
MidiResult result_;
bool wait_for_result_;
@@ -105,7 +101,7 @@ class MidiManagerTest : public ::testing::Test {
EXPECT_FALSE(manager_->start_initialization_is_called_);
EXPECT_EQ(0U, manager_->GetClientCount());
EXPECT_EQ(0U, manager_->GetPendingClientCount());
- manager_->StartSession(client, client->client_id());
+ manager_->StartSession(client);
EXPECT_EQ(0U, manager_->GetClientCount());
EXPECT_EQ(1U, manager_->GetPendingClientCount());
EXPECT_TRUE(manager_->start_initialization_is_called_);
@@ -122,7 +118,7 @@ class MidiManagerTest : public ::testing::Test {
// StartInitialization() should not be called for the second and later
// sessions.
manager_->start_initialization_is_called_ = false;
- manager_->StartSession(client, client->client_id());
+ manager_->StartSession(client);
EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
manager_->start_initialization_is_called_ = true;
}
@@ -153,7 +149,7 @@ class MidiManagerTest : public ::testing::Test {
TEST_F(MidiManagerTest, StartAndEndSession) {
scoped_ptr<FakeMidiManagerClient> client;
- client.reset(new FakeMidiManagerClient(0));
+ client.reset(new FakeMidiManagerClient);
StartTheFirstSession(client.get());
CompleteInitialization(MIDI_OK);
@@ -163,7 +159,7 @@ TEST_F(MidiManagerTest, StartAndEndSession) {
TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
scoped_ptr<FakeMidiManagerClient> client;
- client.reset(new FakeMidiManagerClient(1));
+ client.reset(new FakeMidiManagerClient);
StartTheFirstSession(client.get());
CompleteInitialization(MIDI_INITIALIZATION_ERROR);
@@ -175,9 +171,9 @@ TEST_F(MidiManagerTest, StartMultipleSessions) {
scoped_ptr<FakeMidiManagerClient> client1;
scoped_ptr<FakeMidiManagerClient> client2;
scoped_ptr<FakeMidiManagerClient> client3;
- client1.reset(new FakeMidiManagerClient(0));
- client2.reset(new FakeMidiManagerClient(1));
- client3.reset(new FakeMidiManagerClient(1));
+ client1.reset(new FakeMidiManagerClient);
+ client2.reset(new FakeMidiManagerClient);
+ client3.reset(new FakeMidiManagerClient);
StartTheFirstSession(client1.get());
StartTheNthSession(client2.get(), 2);
@@ -199,16 +195,15 @@ TEST_F(MidiManagerTest, TooManyPendingSessions) {
ScopedVector<FakeMidiManagerClient> many_existing_clients;
many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
- many_existing_clients[i] = new FakeMidiManagerClient(i);
+ many_existing_clients[i] = new FakeMidiManagerClient;
StartTheNthSession(many_existing_clients[i], i + 1);
}
// Push the last client that should be rejected for too many pending requests.
scoped_ptr<FakeMidiManagerClient> additional_client(
- new FakeMidiManagerClient(MidiManager::kMaxPendingClientCount));
+ new FakeMidiManagerClient);
manager_->start_initialization_is_called_ = false;
- manager_->StartSession(additional_client.get(),
- additional_client->client_id());
+ manager_->StartSession(additional_client.get());
EXPECT_FALSE(manager_->start_initialization_is_called_);
EXPECT_EQ(MIDI_INITIALIZATION_ERROR, additional_client->result());
@@ -232,7 +227,7 @@ TEST_F(MidiManagerTest, AbortSession) {
// A client starting a session can be destructed while an asynchronous
// initialization is performed.
scoped_ptr<FakeMidiManagerClient> client;
- client.reset(new FakeMidiManagerClient(0));
+ client.reset(new FakeMidiManagerClient);
StartTheFirstSession(client.get());
EndSession(client.get(), 0, 0);
@@ -246,10 +241,10 @@ TEST_F(MidiManagerTest, AbortSession) {
TEST_F(MidiManagerTest, CreateMidiManager) {
scoped_ptr<FakeMidiManagerClient> client;
- client.reset(new FakeMidiManagerClient(0));
+ client.reset(new FakeMidiManagerClient);
scoped_ptr<MidiManager> manager(MidiManager::Create());
- manager->StartSession(client.get(), client->client_id());
+ manager->StartSession(client.get());
MidiResult result = client->WaitForResult();
// This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
diff --git a/media/midi/midi_manager_usb_unittest.cc b/media/midi/midi_manager_usb_unittest.cc
index b4f939f..1eeae9e 100644
--- a/media/midi/midi_manager_usb_unittest.cc
+++ b/media/midi/midi_manager_usb_unittest.cc
@@ -78,7 +78,7 @@ class FakeMidiManagerClient : public MidiManagerClient {
logger_(logger) {}
~FakeMidiManagerClient() override {}
- void CompleteStartSession(int client_id, MidiResult result) override {
+ void CompleteStartSession(MidiResult result) override {
complete_start_session_ = true;
result_ = result;
}
@@ -159,7 +159,7 @@ class MidiManagerUsbTest : public ::testing::Test {
protected:
void Initialize() {
client_.reset(new FakeMidiManagerClient(&logger_));
- manager_->StartSession(client_.get(), 0);
+ manager_->StartSession(client_.get());
}
void Finalize() {
diff --git a/media/midi/midi_result.h b/media/midi/midi_result.h
index 1e10401..2fb58a4 100644
--- a/media/midi/midi_result.h
+++ b/media/midi/midi_result.h
@@ -9,7 +9,8 @@ namespace media {
// Result codes for MIDI.
enum MidiResult {
- MIDI_OK,
+ MIDI_NOT_INITIALIZED = -1,
+ MIDI_OK = 0,
MIDI_NOT_SUPPORTED,
MIDI_INITIALIZATION_ERROR,