Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / midi_host.cc
index 934d5a2..b6e2688 100644 (file)
 #include "content/public/browser/media_observer.h"
 #include "content/public/browser/user_metrics.h"
 #include "media/midi/midi_manager.h"
+#include "media/midi/midi_message_queue.h"
+#include "media/midi/midi_message_util.h"
 
-using media::MIDIManager;
-using media::MIDIPortInfoList;
+using media::MidiManager;
+using media::MidiPortInfoList;
+
+namespace content {
+namespace {
 
 // The total number of bytes which we're allowed to send to the OS
 // before knowing that they have been successfully sent.
-static const size_t kMaxInFlightBytes = 10 * 1024 * 1024;  // 10 MB.
+const size_t kMaxInFlightBytes = 10 * 1024 * 1024;  // 10 MB.
 
 // We keep track of the number of bytes successfully sent to
 // the hardware.  Every once in a while we report back to the renderer
 // the number of bytes sent since the last report. This threshold determines
 // how many bytes will be sent before reporting back to the renderer.
-static const size_t kAcknowledgementThresholdBytes = 1024 * 1024;  // 1 MB.
+const size_t kAcknowledgementThresholdBytes = 1024 * 1024;  // 1 MB.
 
-static const uint8 kSysExMessage = 0xf0;
+bool IsDataByte(uint8 data) {
+  return (data & 0x80) == 0;
+}
 
-namespace content {
+bool IsSystemRealTimeMessage(uint8 data) {
+  return 0xf8 <= data && data <= 0xff;
+}
+
+}  // namespace
+
+using media::kSysExByte;
+using media::kEndOfSysExByte;
 
-MIDIHost::MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager)
+MidiHost::MidiHost(int renderer_process_id, media::MidiManager* midi_manager)
     : renderer_process_id_(renderer_process_id),
+      has_sys_ex_permission_(false),
       midi_manager_(midi_manager),
       sent_bytes_in_flight_(0),
       bytes_sent_since_last_acknowledgement_(0) {
 }
 
-MIDIHost::~MIDIHost() {
+MidiHost::~MidiHost() {
   if (midi_manager_)
     midi_manager_->EndSession(this);
 }
 
-void MIDIHost::OnDestruct() const {
+void MidiHost::OnDestruct() const {
   BrowserThread::DeleteOnIOThread::Destruct(this);
 }
 
-///////////////////////////////////////////////////////////////////////////////
 // IPC Messages handler
-bool MIDIHost::OnMessageReceived(const IPC::Message& message,
+bool MidiHost::OnMessageReceived(const IPC::Message& message,
                                  bool* message_was_ok) {
   bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP_EX(MIDIHost, message, *message_was_ok)
-    IPC_MESSAGE_HANDLER(MIDIHostMsg_StartSession, OnStartSession)
-    IPC_MESSAGE_HANDLER(MIDIHostMsg_SendData, OnSendData)
+  IPC_BEGIN_MESSAGE_MAP_EX(MidiHost, message, *message_was_ok)
+    IPC_MESSAGE_HANDLER(MidiHostMsg_StartSession, OnStartSession)
+    IPC_MESSAGE_HANDLER(MidiHostMsg_SendData, OnSendData)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP_EX()
 
   return handled;
 }
 
-void MIDIHost::OnStartSession(int client_id) {
-  MIDIPortInfoList input_ports;
-  MIDIPortInfoList output_ports;
+void MidiHost::OnStartSession(int client_id) {
+  MidiPortInfoList input_ports;
+  MidiPortInfoList output_ports;
 
   // Initialize devices and register to receive MIDI data.
   bool success = false;
@@ -75,17 +89,23 @@ void MIDIHost::OnStartSession(int client_id) {
     if (success) {
       input_ports = midi_manager_->input_ports();
       output_ports = midi_manager_->output_ports();
+      received_messages_queues_.clear();
+      received_messages_queues_.resize(input_ports.size());
+      // ChildSecurityPolicy is set just before OnStartSession by
+      // MidiDispatcherHost. So we can safely cache the policy.
+      has_sys_ex_permission_ = ChildProcessSecurityPolicyImpl::GetInstance()->
+          CanSendMidiSysExMessage(renderer_process_id_);
     }
   }
 
-  Send(new MIDIMsg_SessionStarted(
+  Send(new MidiMsg_SessionStarted(
        client_id,
        success,
        input_ports,
        output_ports));
 }
 
-void MIDIHost::OnSendData(uint32 port,
+void MidiHost::OnSendData(uint32 port,
                           const std::vector<uint8>& data,
                           double timestamp) {
   if (!midi_manager_)
@@ -94,59 +114,64 @@ void MIDIHost::OnSendData(uint32 port,
   if (data.empty())
     return;
 
-  base::AutoLock auto_lock(in_flight_lock_);
+  // Blink running in a renderer checks permission to raise a SecurityError
+  // in JavaScript. The actual permission check for security purposes
+  // happens here in the browser process.
+  if (!has_sys_ex_permission_ &&
+      std::find(data.begin(), data.end(), kSysExByte) != data.end()) {
+    RecordAction(base::UserMetricsAction("BadMessageTerminate_MIDI"));
+    BadMessageReceived();
+    return;
+  }
 
-  // Sanity check that we won't send too much.
-  if (sent_bytes_in_flight_ > kMaxInFlightBytes ||
-      data.size() > kMaxInFlightBytes ||
-      data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes)
+  if (!IsValidWebMIDIData(data))
     return;
 
-  if (data[0] >= kSysExMessage) {
-    // Blink running in a renderer checks permission to raise a SecurityError in
-    // JavaScript. The actual permission check for security perposes happens
-    // here in the browser process.
-    if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage(
-        renderer_process_id_)) {
-      RecordAction(UserMetricsAction("BadMessageTerminate_MIDI"));
-      BadMessageReceived();
+  {
+    base::AutoLock auto_lock(in_flight_lock_);
+    // Sanity check that we won't send too much data.
+    // TODO(yukawa): Consider to send an error event back to the renderer
+    // after some future discussion in W3C.
+    if (data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes)
       return;
-    }
+    sent_bytes_in_flight_ += data.size();
   }
-
-  midi_manager_->DispatchSendMIDIData(
-      this,
-      port,
-      data,
-      timestamp);
-
-  sent_bytes_in_flight_ += data.size();
+  midi_manager_->DispatchSendMidiData(this, port, data, timestamp);
 }
 
-void MIDIHost::ReceiveMIDIData(
+void MidiHost::ReceiveMidiData(
     uint32 port,
     const uint8* data,
     size_t length,
     double timestamp) {
-  TRACE_EVENT0("midi", "MIDIHost::ReceiveMIDIData");
-
-  // Check a process security policy to receive a system exclusive message.
-  if (length > 0 && data[0] >= kSysExMessage) {
-    if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanSendMIDISysExMessage(
-        renderer_process_id_)) {
-      // MIDI devices may send a system exclusive messages even if the renderer
-      // doesn't have a permission to receive it. Don't kill the renderer as
-      // OnSendData() does.
-      return;
-    }
-  }
+  TRACE_EVENT0("midi", "MidiHost::ReceiveMidiData");
 
-  // Send to the renderer.
-  std::vector<uint8> v(data, data + length);
-  Send(new MIDIMsg_DataReceived(port, v, timestamp));
+  if (received_messages_queues_.size() <= port)
+    return;
+
+  // Lazy initialization
+  if (received_messages_queues_[port] == NULL)
+    received_messages_queues_[port] = new media::MidiMessageQueue(true);
+
+  received_messages_queues_[port]->Add(data, length);
+  std::vector<uint8> message;
+  while (true) {
+    received_messages_queues_[port]->Get(&message);
+    if (message.empty())
+      break;
+
+    // MIDI devices may send a system exclusive messages even if the renderer
+    // doesn't have a permission to receive it. Don't kill the renderer as
+    // OnSendData() does.
+    if (message[0] == kSysExByte && !has_sys_ex_permission_)
+      continue;
+
+    // Send to the renderer.
+    Send(new MidiMsg_DataReceived(port, message, timestamp));
+  }
 }
 
-void MIDIHost::AccumulateMIDIBytesSent(size_t n) {
+void MidiHost::AccumulateMidiBytesSent(size_t n) {
   {
     base::AutoLock auto_lock(in_flight_lock_);
     if (n <= sent_bytes_in_flight_)
@@ -159,10 +184,43 @@ void MIDIHost::AccumulateMIDIBytesSent(size_t n) {
 
   if (bytes_sent_since_last_acknowledgement_ >=
       kAcknowledgementThresholdBytes) {
-    Send(new MIDIMsg_AcknowledgeSentData(
+    Send(new MidiMsg_AcknowledgeSentData(
         bytes_sent_since_last_acknowledgement_));
     bytes_sent_since_last_acknowledgement_ = 0;
   }
 }
 
+// static
+bool MidiHost::IsValidWebMIDIData(const std::vector<uint8>& data) {
+  bool in_sysex = false;
+  size_t waiting_data_length = 0;
+  for (size_t i = 0; i < data.size(); ++i) {
+    const uint8 current = data[i];
+    if (IsSystemRealTimeMessage(current))
+      continue;  // Real time message can be placed at any point.
+    if (waiting_data_length > 0) {
+      if (!IsDataByte(current))
+        return false;  // Error: |current| should have been data byte.
+      --waiting_data_length;
+      continue;  // Found data byte as expected.
+    }
+    if (in_sysex) {
+      if (data[i] == kEndOfSysExByte)
+        in_sysex = false;
+      else if (!IsDataByte(current))
+        return false;  // Error: |current| should have been data byte.
+      continue;  // Found data byte as expected.
+    }
+    if (current == kSysExByte) {
+      in_sysex = true;
+      continue;  // Found SysEX
+    }
+    waiting_data_length = media::GetMidiMessageLength(current);
+    if (waiting_data_length == 0)
+      return false;  // Error: |current| should have been a valid status byte.
+    --waiting_data_length;  // Found status byte
+  }
+  return waiting_data_length == 0 && !in_sysex;
+}
+
 }  // namespace content