Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / dom / midi / MIDIPlatformService.cpp
bloba3ef5911a7db6fc080f906efb50b080a09efc527
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MIDIPlatformService.h"
8 #include "MIDIMessageQueue.h"
9 #include "TestMIDIPlatformService.h"
10 #ifdef MOZ_WEBMIDI_MIDIR_IMPL
11 # include "midirMIDIPlatformService.h"
12 #endif // MOZ_WEBMIDI_MIDIR_IMPL
13 #include "mozilla/ErrorResult.h"
14 #include "mozilla/StaticPrefs_midi.h"
15 #include "mozilla/StaticPtr.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/dom/MIDIManagerParent.h"
18 #include "mozilla/dom/MIDIPlatformRunnables.h"
19 #include "mozilla/dom/MIDIUtils.h"
20 #include "mozilla/dom/PMIDIManagerParent.h"
21 #include "mozilla/ipc/BackgroundParent.h"
22 #include "mozilla/dom/MIDIPortParent.h"
24 using namespace mozilla;
25 using namespace mozilla::dom;
27 MIDIPlatformService::MIDIPlatformService()
28 : mHasSentPortList(false),
29 mMessageQueueMutex("MIDIPlatformServce::mMessageQueueMutex") {}
31 MIDIPlatformService::~MIDIPlatformService() = default;
33 void MIDIPlatformService::CheckAndReceive(const nsAString& aPortId,
34 const nsTArray<MIDIMessage>& aMsgs) {
35 AssertThread();
36 for (auto& port : mPorts) {
37 // TODO Clean this up when we split input/output port arrays
38 if (port->MIDIPortInterface::Id() != aPortId ||
39 port->Type() != MIDIPortType::Input ||
40 port->ConnectionState() != MIDIPortConnectionState::Open) {
41 continue;
43 if (!port->SysexEnabled()) {
44 nsTArray<MIDIMessage> msgs;
45 for (const auto& msg : aMsgs) {
46 if (!MIDIUtils::IsSysexMessage(msg)) {
47 msgs.AppendElement(msg);
50 Unused << port->SendReceive(msgs);
51 } else {
52 Unused << port->SendReceive(aMsgs);
57 void MIDIPlatformService::AddPort(MIDIPortParent* aPort) {
58 MOZ_ASSERT(aPort);
59 AssertThread();
60 mPorts.AppendElement(aPort);
63 void MIDIPlatformService::RemovePort(MIDIPortParent* aPort) {
64 // This should only be called from the background thread, when a MIDIPort
65 // actor has been destroyed.
66 AssertThread();
67 MOZ_ASSERT(aPort);
68 mPorts.RemoveElement(aPort);
69 MaybeStop();
72 void MIDIPlatformService::BroadcastState(const MIDIPortInfo& aPortInfo,
73 const MIDIPortDeviceState& aState) {
74 AssertThread();
75 for (auto& p : mPorts) {
76 if (p->MIDIPortInterface::Id() == aPortInfo.id() &&
77 p->DeviceState() != aState) {
78 p->SendUpdateStatus(aState, p->ConnectionState());
83 void MIDIPlatformService::QueueMessages(const nsAString& aId,
84 nsTArray<MIDIMessage>& aMsgs) {
85 AssertThread();
87 MutexAutoLock lock(mMessageQueueMutex);
88 MIDIMessageQueue* msgQueue = mMessageQueues.GetOrInsertNew(aId);
89 msgQueue->Add(aMsgs);
92 ScheduleSend(aId);
95 void MIDIPlatformService::SendPortList() {
96 AssertThread();
97 mHasSentPortList = true;
98 MIDIPortList l;
99 for (auto& el : mPortInfo) {
100 l.ports().AppendElement(el);
102 for (auto& mgr : mManagers) {
103 Unused << mgr->SendMIDIPortListUpdate(l);
107 void MIDIPlatformService::Clear(MIDIPortParent* aPort) {
108 AssertThread();
109 MOZ_ASSERT(aPort);
111 MutexAutoLock lock(mMessageQueueMutex);
112 MIDIMessageQueue* msgQueue =
113 mMessageQueues.Get(aPort->MIDIPortInterface::Id());
114 if (msgQueue) {
115 msgQueue->Clear();
120 void MIDIPlatformService::AddPortInfo(MIDIPortInfo& aPortInfo) {
121 AssertThread();
122 MOZ_ASSERT(XRE_IsParentProcess());
124 mPortInfo.AppendElement(aPortInfo);
126 // ORDER MATTERS HERE.
128 // When MIDI hardware is disconnected, all open MIDIPort objects revert to a
129 // "pending" state, and they are removed from the port maps of MIDIAccess
130 // objects. We need to send connection updates to all living ports first, THEN
131 // we can send port list updates to all of the live MIDIAccess objects. We
132 // have to go in this order because if a port object is still held live but is
133 // disconnected, it needs to readd itself to its originating MIDIAccess
134 // object. Running SendPortList first would cause MIDIAccess to create a new
135 // MIDIPort object, which would conflict (i.e. old disconnected object != new
136 // object in port map, which is against spec).
137 for (auto& port : mPorts) {
138 if (port->MIDIPortInterface::Id() == aPortInfo.id()) {
139 port->SendUpdateStatus(MIDIPortDeviceState::Connected,
140 port->ConnectionState());
143 if (mHasSentPortList) {
144 SendPortList();
148 void MIDIPlatformService::RemovePortInfo(MIDIPortInfo& aPortInfo) {
149 AssertThread();
150 mPortInfo.RemoveElement(aPortInfo);
151 BroadcastState(aPortInfo, MIDIPortDeviceState::Disconnected);
152 if (mHasSentPortList) {
153 SendPortList();
157 StaticRefPtr<nsISerialEventTarget> gMIDITaskQueue;
159 // static
160 void MIDIPlatformService::InitStatics() {
161 nsCOMPtr<nsISerialEventTarget> queue;
162 MOZ_ALWAYS_SUCCEEDS(
163 NS_CreateBackgroundTaskQueue("MIDITaskQueue", getter_AddRefs(queue)));
164 gMIDITaskQueue = queue.forget();
165 ClearOnShutdown(&gMIDITaskQueue);
168 // static
169 nsISerialEventTarget* MIDIPlatformService::OwnerThread() {
170 return gMIDITaskQueue;
173 StaticRefPtr<MIDIPlatformService> gMIDIPlatformService;
175 // static
176 bool MIDIPlatformService::IsRunning() {
177 return gMIDIPlatformService != nullptr;
180 void MIDIPlatformService::Close(mozilla::dom::MIDIPortParent* aPort) {
181 AssertThread();
183 MutexAutoLock lock(mMessageQueueMutex);
184 MIDIMessageQueue* msgQueue =
185 mMessageQueues.Get(aPort->MIDIPortInterface::Id());
186 if (msgQueue) {
187 msgQueue->ClearAfterNow();
190 // Send all messages before sending a close request
191 ScheduleSend(aPort->MIDIPortInterface::Id());
192 // TODO We should probably have the send function schedule closing
193 ScheduleClose(aPort);
196 // static
197 MIDIPlatformService* MIDIPlatformService::Get() {
198 // We should never touch the platform service in a child process.
199 MOZ_ASSERT(XRE_IsParentProcess());
200 AssertThread();
201 if (!IsRunning()) {
202 if (StaticPrefs::midi_testing()) {
203 gMIDIPlatformService = new TestMIDIPlatformService();
205 #ifdef MOZ_WEBMIDI_MIDIR_IMPL
206 else {
207 gMIDIPlatformService = new midirMIDIPlatformService();
209 #endif // MOZ_WEBMIDI_MIDIR_IMPL
210 gMIDIPlatformService->Init();
212 return gMIDIPlatformService;
215 void MIDIPlatformService::MaybeStop() {
216 AssertThread();
217 if (!IsRunning()) {
218 // Service already stopped or never started. Exit.
219 return;
221 // If we have any ports or managers left, we should still be alive.
222 if (!mPorts.IsEmpty() || !mManagers.IsEmpty()) {
223 return;
225 Stop();
226 gMIDIPlatformService = nullptr;
229 void MIDIPlatformService::AddManager(MIDIManagerParent* aManager) {
230 AssertThread();
231 mManagers.AppendElement(aManager);
232 // Managers add themselves during construction. We have to wait for the
233 // protocol construction to finish before we send them a port list. The
234 // runnable calls SendPortList, which iterates through the live manager list,
235 // so this saves us from having to worry about Manager pointer validity at
236 // time of runnable execution.
237 nsCOMPtr<nsIRunnable> r(new SendPortListRunnable());
238 OwnerThread()->Dispatch(r.forget());
241 void MIDIPlatformService::RemoveManager(MIDIManagerParent* aManager) {
242 AssertThread();
243 mManagers.RemoveElement(aManager);
244 MaybeStop();
247 void MIDIPlatformService::UpdateStatus(
248 MIDIPortParent* aPort, const MIDIPortDeviceState& aDeviceState,
249 const MIDIPortConnectionState& aConnectionState) {
250 AssertThread();
251 aPort->SendUpdateStatus(aDeviceState, aConnectionState);
254 void MIDIPlatformService::GetMessages(const nsAString& aPortId,
255 nsTArray<MIDIMessage>& aMsgs) {
256 // Can run on either background thread or platform specific IO Thread.
258 MutexAutoLock lock(mMessageQueueMutex);
259 MIDIMessageQueue* msgQueue;
260 if (!mMessageQueues.Get(aPortId, &msgQueue)) {
261 return;
263 msgQueue->GetMessages(aMsgs);
267 void MIDIPlatformService::GetMessagesBefore(const nsAString& aPortId,
268 const TimeStamp& aTimeStamp,
269 nsTArray<MIDIMessage>& aMsgs) {
270 // Can run on either background thread or platform specific IO Thread.
272 MutexAutoLock lock(mMessageQueueMutex);
273 MIDIMessageQueue* msgQueue;
274 if (!mMessageQueues.Get(aPortId, &msgQueue)) {
275 return;
277 msgQueue->GetMessagesBefore(aTimeStamp, aMsgs);