Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/midi/TestMIDIPlatformService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "TestMIDIPlatformService.h"
6
#include "mozilla/dom/MIDIPort.h"
7
#include "mozilla/dom/MIDITypes.h"
8
#include "mozilla/dom/MIDIPortInterface.h"
9
#include "mozilla/dom/MIDIPortParent.h"
10
#include "mozilla/dom/MIDIPlatformRunnables.h"
11
#include "mozilla/dom/MIDIUtils.h"
12
#include "mozilla/ipc/BackgroundParent.h"
13
#include "mozilla/Unused.h"
14
15
using namespace mozilla;
16
using namespace mozilla::dom;
17
using namespace mozilla::ipc;
18
19
/**
20
 * Runnable used for making sure ProcessMessages only happens on the IO thread.
21
 *
22
 */
23
class ProcessMessagesRunnable : public mozilla::Runnable
24
{
25
public:
26
  explicit ProcessMessagesRunnable(const nsAString& aPortID) :
27
    Runnable("ProcessMessagesRunnable"),
28
    mPortID(aPortID)
29
0
  {}
30
0
  ~ProcessMessagesRunnable() {}
31
  NS_IMETHOD Run()
32
0
  {
33
0
    // If service is no longer running, just exist without processing.
34
0
    if (!MIDIPlatformService::IsRunning()) {
35
0
      return NS_OK;
36
0
    }
37
0
    TestMIDIPlatformService* srv = static_cast<TestMIDIPlatformService*>(MIDIPlatformService::Get());
38
0
    srv->ProcessMessages(mPortID);
39
0
    return NS_OK;
40
0
  }
41
private:
42
  nsString mPortID;
43
};
44
45
/**
46
 * Runnable used for allowing IO thread to queue more messages for processing,
47
 * since it can't access the service object directly.
48
 *
49
 */
50
class QueueMessagesRunnable : public MIDIBackgroundRunnable
51
{
52
public:
53
  QueueMessagesRunnable(const nsAString& aPortID,
54
                        const nsTArray<MIDIMessage>& aMsgs) :
55
    MIDIBackgroundRunnable("QueueMessagesRunnable"),
56
    mPortID(aPortID),
57
    mMsgs(aMsgs)
58
0
  {}
59
0
  ~QueueMessagesRunnable() {}
60
  virtual void RunInternal()
61
0
  {
62
0
    AssertIsOnBackgroundThread();
63
0
    MIDIPlatformService::Get()->QueueMessages(mPortID, mMsgs);
64
0
  }
65
private:
66
  nsString mPortID;
67
  nsTArray<MIDIMessage> mMsgs;
68
};
69
70
TestMIDIPlatformService::TestMIDIPlatformService() :
71
  mBackgroundThread(NS_GetCurrentThread()),
72
  mControlInputPort(NS_LITERAL_STRING("b744eebe-f7d8-499b-872b-958f63c8f522"),
73
                    NS_LITERAL_STRING("Test Control MIDI Device Input Port"),
74
                    NS_LITERAL_STRING("Test Manufacturer"),
75
                    NS_LITERAL_STRING("1.0.0"),
76
                    static_cast<uint32_t>(MIDIPortType::Input)),
77
  mControlOutputPort(NS_LITERAL_STRING("ab8e7fe8-c4de-436a-a960-30898a7c9a3d"),
78
                     NS_LITERAL_STRING("Test Control MIDI Device Output Port"),
79
                     NS_LITERAL_STRING("Test Manufacturer"),
80
                     NS_LITERAL_STRING("1.0.0"),
81
                     static_cast<uint32_t>(MIDIPortType::Output)),
82
  mStateTestInputPort(NS_LITERAL_STRING("a9329677-8588-4460-a091-9d4a7f629a48"),
83
                      NS_LITERAL_STRING("Test State MIDI Device Input Port"),
84
                      NS_LITERAL_STRING("Test Manufacturer"),
85
                      NS_LITERAL_STRING("1.0.0"),
86
                      static_cast<uint32_t>(MIDIPortType::Input)),
87
  mStateTestOutputPort(NS_LITERAL_STRING("478fa225-b5fc-4fa6-a543-d32d9cb651e7"),
88
                       NS_LITERAL_STRING("Test State MIDI Device Output Port"),
89
                       NS_LITERAL_STRING("Test Manufacturer"),
90
                       NS_LITERAL_STRING("1.0.0"),
91
                       static_cast<uint32_t>(MIDIPortType::Output)),
92
  mAlwaysClosedTestOutputPort(NS_LITERAL_STRING("f87d0c76-3c68-49a9-a44f-700f1125c07a"),
93
                              NS_LITERAL_STRING("Always Closed MIDI Device Output Port"),
94
                              NS_LITERAL_STRING("Test Manufacturer"),
95
                              NS_LITERAL_STRING("1.0.0"),
96
                              static_cast<uint32_t>(MIDIPortType::Output)),
97
  mIsInitialized(false)
98
0
{
99
0
  AssertIsOnBackgroundThread();
100
0
}
101
102
TestMIDIPlatformService::~TestMIDIPlatformService()
103
0
{
104
0
  AssertIsOnBackgroundThread();
105
0
}
106
107
void
108
TestMIDIPlatformService::Init()
109
0
{
110
0
  AssertIsOnBackgroundThread();
111
0
112
0
  if (mIsInitialized) {
113
0
    return;
114
0
  }
115
0
  mIsInitialized = true;
116
0
117
0
  // Treat all of our special ports as always connected. When the service comes
118
0
  // up, prepopulate the port list with them.
119
0
  MIDIPlatformService::Get()->AddPortInfo(mControlInputPort);
120
0
  MIDIPlatformService::Get()->AddPortInfo(mControlOutputPort);
121
0
  MIDIPlatformService::Get()->AddPortInfo(mAlwaysClosedTestOutputPort);
122
0
  nsCOMPtr<nsIRunnable> r(new SendPortListRunnable());
123
0
124
0
  // Start the IO Thread.
125
0
  NS_DispatchToCurrentThread(r);
126
0
}
127
128
void
129
TestMIDIPlatformService::Open(MIDIPortParent* aPort)
130
0
{
131
0
  MOZ_ASSERT(aPort);
132
0
  MIDIPortConnectionState s = MIDIPortConnectionState::Open;
133
0
  if (aPort->MIDIPortInterface::Id() == mAlwaysClosedTestOutputPort.id()) {
134
0
    // If it's the always closed testing port, act like it's already opened
135
0
    // exclusively elsewhere.
136
0
    s = MIDIPortConnectionState::Closed;
137
0
  }
138
0
  // Connection events are just simulated on the background thread, no need to
139
0
  // push to IO thread.
140
0
  nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(aPort->MIDIPortInterface::Id(),
141
0
                                                aPort->DeviceState(),
142
0
                                                s));
143
0
  NS_DispatchToCurrentThread(r);
144
0
}
145
146
void
147
TestMIDIPlatformService::ScheduleClose(MIDIPortParent* aPort)
148
0
{
149
0
  MOZ_ASSERT(aPort);
150
0
  if (aPort->ConnectionState() == MIDIPortConnectionState::Open) {
151
0
    // Connection events are just simulated on the background thread, no need to
152
0
    // push to IO thread.
153
0
    nsCOMPtr<nsIRunnable> r(new SetStatusRunnable(aPort->MIDIPortInterface::Id(),
154
0
                                                  aPort->DeviceState(),
155
0
                                                  MIDIPortConnectionState::Closed));
156
0
    NS_DispatchToCurrentThread(r);
157
0
  }
158
0
}
159
160
void
161
TestMIDIPlatformService::Stop()
162
0
{
163
0
  AssertIsOnBackgroundThread();
164
0
}
165
166
void TestMIDIPlatformService::ScheduleSend(const nsAString& aPortId)
167
0
{
168
0
  nsCOMPtr<nsIRunnable> r(new ProcessMessagesRunnable(aPortId));
169
0
  NS_DispatchToCurrentThread(r);
170
0
}
171
172
void TestMIDIPlatformService::ProcessMessages(const nsAString& aPortId)
173
0
{
174
0
  nsTArray<MIDIMessage> msgs;
175
0
  GetMessagesBefore(aPortId, TimeStamp::Now(), msgs);
176
0
177
0
  for (MIDIMessage msg : msgs) {
178
0
    // receiving message from test control port
179
0
    if (aPortId == mControlOutputPort.id()) {
180
0
      switch (msg.data()[0]) {
181
0
      // Hit a note, get a test!
182
0
      case 0x90:
183
0
        switch (msg.data()[1]) {
184
0
        // Echo data/timestamp back through output port
185
0
        case 0x00:
186
0
        {
187
0
          nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), msg));
188
0
          mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
189
0
          break;
190
0
        }
191
0
        // Cause control test ports to connect
192
0
        case 0x01:
193
0
        {
194
0
          nsCOMPtr<nsIRunnable> r1(new AddPortRunnable(mStateTestInputPort));
195
0
          mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL);
196
0
          break;
197
0
        }
198
0
        // Cause control test ports to disconnect
199
0
        case 0x02:
200
0
        {
201
0
          nsCOMPtr<nsIRunnable> r1(new RemovePortRunnable(mStateTestInputPort));
202
0
          mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL);
203
0
          break;
204
0
        }
205
0
        // Test for packet timing
206
0
        case 0x03:
207
0
        {
208
0
          // Append a few echo command packets in reverse timing order, should
209
0
          // come out in correct order on other end.
210
0
          nsTArray<MIDIMessage> newMsgs;
211
0
          nsTArray<uint8_t> msg;
212
0
          msg.AppendElement(0x90);
213
0
          msg.AppendElement(0x00);
214
0
          msg.AppendElement(0x00);
215
0
          // PR_Now() returns nanosecods, and we need a double with fractional
216
0
          // milliseconds.
217
0
          TimeStamp currentTime = TimeStamp::Now();
218
0
          for (int i = 0; i <= 5; ++i) {
219
0
            // Insert messages with timestamps in reverse order, to make sure
220
0
            // we're sorting correctly.
221
0
            newMsgs.AppendElement(
222
0
              MIDIMessage(msg, currentTime -
223
0
                          TimeDuration::FromMilliseconds(i * 2)));
224
0
          }
225
0
          nsCOMPtr<nsIRunnable> r(new QueueMessagesRunnable(aPortId, newMsgs));
226
0
          mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
227
0
          break;
228
0
        }
229
0
        default:
230
0
          NS_WARNING("Unknown Test MIDI message received!");
231
0
        }
232
0
        break;
233
0
        // Sysex tests
234
0
      case 0xF0:
235
0
        switch (msg.data()[1]) {
236
0
          // Echo data/timestamp back through output port
237
0
        case 0x00:
238
0
        {
239
0
          nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), msg));
240
0
          mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
241
0
          break;
242
0
        }
243
0
        // Test for system real time messages in the middle of sysex messages.
244
0
        case 0x01:
245
0
        {
246
0
          nsTArray<uint8_t> msgs;
247
0
          const uint8_t msg[] = {0xF0, 0x01, 0xF8, 0x02, 0x03, 0x04, 0xF9, 0x05, 0xF7};
248
0
          // Can't use AppendElements on an array here, so just do range based loading.
249
0
          for (auto& s : msg) {
250
0
            msgs.AppendElement(s);
251
0
          }
252
0
          nsTArray<MIDIMessage> newMsgs;
253
0
          MIDIUtils::ParseMessages(msgs, TimeStamp::Now(), newMsgs);
254
0
          nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), newMsgs));
255
0
          mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
256
0
          break;
257
0
        }
258
0
        default:
259
0
          NS_WARNING("Unknown Test Sysex MIDI message received!");
260
0
        }
261
0
        break;
262
0
      }
263
0
    }
264
0
  }
265
0
}
266