Bug 1733673 [wpt PR 31066] - Annotate CSS Transforms WPT reftests as fuzzy where...
[gecko.git] / dom / midi / MIDIPlatformService.cpp
blob4b33fd48c9e64f6d57b05cde345096913861879f
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 #include "mozilla/ErrorResult.h"
11 #include "mozilla/StaticPtr.h"
12 #include "mozilla/Unused.h"
13 #include "mozilla/dom/MIDIManagerParent.h"
14 #include "mozilla/dom/MIDIPlatformRunnables.h"
15 #include "mozilla/dom/MIDIUtils.h"
16 #include "mozilla/dom/PMIDIManagerParent.h"
17 #include "mozilla/ipc/BackgroundParent.h"
18 #include "mozilla/dom/MIDIPortParent.h"
20 using namespace mozilla;
21 using namespace mozilla::dom;
23 MIDIPlatformService::MIDIPlatformService()
24 : mHasSentPortList(false),
25 mMessageQueueMutex("MIDIPlatformServce::mMessageQueueMutex") {}
27 MIDIPlatformService::~MIDIPlatformService() = default;
29 void MIDIPlatformService::CheckAndReceive(const nsAString& aPortId,
30 const nsTArray<MIDIMessage>& aMsgs) {
31 ::mozilla::ipc::AssertIsOnBackgroundThread();
32 for (auto& port : mPorts) {
33 // TODO Clean this up when we split input/output port arrays
34 if (port->MIDIPortInterface::Id() != aPortId ||
35 port->Type() != MIDIPortType::Input ||
36 port->ConnectionState() != MIDIPortConnectionState::Open) {
37 continue;
39 if (!port->SysexEnabled()) {
40 nsTArray<MIDIMessage> msgs;
41 for (auto& msg : aMsgs) {
42 if (!MIDIUtils::IsSysexMessage(msg)) {
43 msgs.AppendElement(msg);
46 Unused << port->SendReceive(msgs);
47 } else {
48 Unused << port->SendReceive(aMsgs);
53 void MIDIPlatformService::AddPort(MIDIPortParent* aPort) {
54 MOZ_ASSERT(aPort);
55 ::mozilla::ipc::AssertIsOnBackgroundThread();
56 mPorts.AppendElement(aPort);
59 void MIDIPlatformService::RemovePort(MIDIPortParent* aPort) {
60 // This should only be called from the background thread, when a MIDIPort
61 // actor has been destroyed.
62 ::mozilla::ipc::AssertIsOnBackgroundThread();
63 MOZ_ASSERT(aPort);
64 mPorts.RemoveElement(aPort);
65 MaybeStop();
68 void MIDIPlatformService::BroadcastState(const MIDIPortInfo& aPortInfo,
69 const MIDIPortDeviceState& aState) {
70 ::mozilla::ipc::AssertIsOnBackgroundThread();
71 for (auto& p : mPorts) {
72 if (p->MIDIPortInterface::Id() == aPortInfo.id() &&
73 p->DeviceState() != aState) {
74 p->SendUpdateStatus(aState, p->ConnectionState());
79 void MIDIPlatformService::QueueMessages(const nsAString& aId,
80 nsTArray<MIDIMessage>& aMsgs) {
81 ::mozilla::ipc::AssertIsOnBackgroundThread();
83 MutexAutoLock lock(mMessageQueueMutex);
84 MIDIMessageQueue* msgQueue = mMessageQueues.GetOrInsertNew(aId);
85 msgQueue->Add(aMsgs);
86 ScheduleSend(aId);
90 void MIDIPlatformService::SendPortList() {
91 ::mozilla::ipc::AssertIsOnBackgroundThread();
92 mHasSentPortList = true;
93 MIDIPortList l;
94 for (auto& el : mPortInfo) {
95 l.ports().AppendElement(el);
97 for (auto& mgr : mManagers) {
98 Unused << mgr->SendMIDIPortListUpdate(l);
102 void MIDIPlatformService::Clear(MIDIPortParent* aPort) {
103 ::mozilla::ipc::AssertIsOnBackgroundThread();
104 MOZ_ASSERT(aPort);
106 MutexAutoLock lock(mMessageQueueMutex);
107 MIDIMessageQueue* msgQueue =
108 mMessageQueues.Get(aPort->MIDIPortInterface::Id());
109 if (msgQueue) {
110 msgQueue->Clear();
115 void MIDIPlatformService::AddPortInfo(MIDIPortInfo& aPortInfo) {
116 ::mozilla::ipc::AssertIsOnBackgroundThread();
117 MOZ_ASSERT(XRE_IsParentProcess());
119 mPortInfo.AppendElement(aPortInfo);
121 // ORDER MATTERS HERE.
123 // When MIDI hardware is disconnected, all open MIDIPort objects revert to a
124 // "pending" state, and they are removed from the port maps of MIDIAccess
125 // objects. We need to send connection updates to all living ports first, THEN
126 // we can send port list updates to all of the live MIDIAccess objects. We
127 // have to go in this order because if a port object is still held live but is
128 // disconnected, it needs to readd itself to its originating MIDIAccess
129 // object. Running SendPortList first would cause MIDIAccess to create a new
130 // MIDIPort object, which would conflict (i.e. old disconnected object != new
131 // object in port map, which is against spec).
132 for (auto& port : mPorts) {
133 if (port->MIDIPortInterface::Id() == aPortInfo.id()) {
134 port->SendUpdateStatus(MIDIPortDeviceState::Connected,
135 port->ConnectionState());
138 if (mHasSentPortList) {
139 SendPortList();
143 void MIDIPlatformService::RemovePortInfo(MIDIPortInfo& aPortInfo) {
144 ::mozilla::ipc::AssertIsOnBackgroundThread();
145 mPortInfo.RemoveElement(aPortInfo);
146 BroadcastState(aPortInfo, MIDIPortDeviceState::Disconnected);
147 if (mHasSentPortList) {
148 SendPortList();
152 StaticRefPtr<MIDIPlatformService> gMIDIPlatformService;
154 // static
155 bool MIDIPlatformService::IsRunning() {
156 return gMIDIPlatformService != nullptr;
159 void MIDIPlatformService::Close(mozilla::dom::MIDIPortParent* aPort) {
160 ::mozilla::ipc::AssertIsOnBackgroundThread();
162 MutexAutoLock lock(mMessageQueueMutex);
163 MIDIMessageQueue* msgQueue =
164 mMessageQueues.Get(aPort->MIDIPortInterface::Id());
165 if (msgQueue) {
166 msgQueue->ClearAfterNow();
169 // Send all messages before sending a close request
170 ScheduleSend(aPort->MIDIPortInterface::Id());
171 // TODO We should probably have the send function schedule closing
172 ScheduleClose(aPort);
175 // static
176 MIDIPlatformService* MIDIPlatformService::Get() {
177 // We should never touch the platform service in a child process.
178 MOZ_ASSERT(XRE_IsParentProcess());
179 ::mozilla::ipc::AssertIsOnBackgroundThread();
180 if (!IsRunning()) {
181 ErrorResult rv;
182 // Uncomment once we have an actual platform library to test.
184 // bool useTestService = false;
185 // rv = Preferences::GetRootBranch()->GetBoolPref("midi.testing",
186 // &useTestService);
187 gMIDIPlatformService = new TestMIDIPlatformService();
188 gMIDIPlatformService->Init();
190 return gMIDIPlatformService;
193 void MIDIPlatformService::MaybeStop() {
194 ::mozilla::ipc::AssertIsOnBackgroundThread();
195 if (!IsRunning()) {
196 // Service already stopped or never started. Exit.
197 return;
199 // If we have any ports or managers left, we should still be alive.
200 if (!mPorts.IsEmpty() || !mManagers.IsEmpty()) {
201 return;
203 Stop();
204 gMIDIPlatformService = nullptr;
207 void MIDIPlatformService::AddManager(MIDIManagerParent* aManager) {
208 ::mozilla::ipc::AssertIsOnBackgroundThread();
209 mManagers.AppendElement(aManager);
210 // Managers add themselves during construction. We have to wait for the
211 // protocol construction to finish before we send them a port list. The
212 // runnable calls SendPortList, which iterates through the live manager list,
213 // so this saves us from having to worry about Manager pointer validity at
214 // time of runnable execution.
215 nsCOMPtr<nsIRunnable> r(new SendPortListRunnable());
216 NS_DispatchToCurrentThread(r);
219 void MIDIPlatformService::RemoveManager(MIDIManagerParent* aManager) {
220 ::mozilla::ipc::AssertIsOnBackgroundThread();
221 mManagers.RemoveElement(aManager);
222 MaybeStop();
225 void MIDIPlatformService::UpdateStatus(
226 const nsAString& aPortId, const MIDIPortDeviceState& aDeviceState,
227 const MIDIPortConnectionState& aConnectionState) {
228 ::mozilla::ipc::AssertIsOnBackgroundThread();
229 for (auto port : mPorts) {
230 if (port->MIDIPortInterface::Id() == aPortId) {
231 port->SendUpdateStatus(aDeviceState, aConnectionState);
236 void MIDIPlatformService::GetMessages(const nsAString& aPortId,
237 nsTArray<MIDIMessage>& aMsgs) {
238 // Can run on either background thread or platform specific IO Thread.
240 MutexAutoLock lock(mMessageQueueMutex);
241 MIDIMessageQueue* msgQueue;
242 if (!mMessageQueues.Get(aPortId, &msgQueue)) {
243 return;
245 msgQueue->GetMessages(aMsgs);
249 void MIDIPlatformService::GetMessagesBefore(const nsAString& aPortId,
250 const TimeStamp& aTimeStamp,
251 nsTArray<MIDIMessage>& aMsgs) {
252 // Can run on either background thread or platform specific IO Thread.
254 MutexAutoLock lock(mMessageQueueMutex);
255 MIDIMessageQueue* msgQueue;
256 if (!mMessageQueues.Get(aPortId, &msgQueue)) {
257 return;
259 msgQueue->GetMessagesBefore(aTimeStamp, aMsgs);