summaryrefslogtreecommitdiffstats
path: root/dom/midi/MIDIAccess.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/midi/MIDIAccess.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/midi/MIDIAccess.cpp')
-rw-r--r--dom/midi/MIDIAccess.cpp221
1 files changed, 221 insertions, 0 deletions
diff --git a/dom/midi/MIDIAccess.cpp b/dom/midi/MIDIAccess.cpp
new file mode 100644
index 0000000000..698473c20a
--- /dev/null
+++ b/dom/midi/MIDIAccess.cpp
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/MIDIAccess.h"
+#include "mozilla/dom/MIDIAccessManager.h"
+#include "mozilla/dom/MIDIPort.h"
+#include "mozilla/dom/MIDIAccessBinding.h"
+#include "mozilla/dom/MIDIConnectionEvent.h"
+#include "mozilla/dom/MIDIOptionsBinding.h"
+#include "mozilla/dom/MIDIOutputMapBinding.h"
+#include "mozilla/dom/MIDIInputMapBinding.h"
+#include "mozilla/dom/MIDIOutputMap.h"
+#include "mozilla/dom/MIDIInputMap.h"
+#include "mozilla/dom/MIDIOutput.h"
+#include "mozilla/dom/MIDIInput.h"
+#include "mozilla/dom/MIDITypes.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/Document.h"
+#include "nsPIDOMWindow.h"
+#include "nsContentPermissionHelper.h"
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
+#include "IPCMessageUtils.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MIDIAccess)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIAccess, DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MIDIAccess,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputMap)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputMap)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessPromise)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MIDIAccess,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputMap)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputMap)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessPromise)
+ tmp->Shutdown();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIAccess)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(MIDIAccess, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MIDIAccess, DOMEventTargetHelper)
+
+MIDIAccess::MIDIAccess(nsPIDOMWindowInner* aWindow, bool aSysexEnabled,
+ Promise* aPromise)
+ : DOMEventTargetHelper(aWindow),
+ mInputMap(new MIDIInputMap(aWindow)),
+ mOutputMap(new MIDIOutputMap(aWindow)),
+ mSysexEnabled(aSysexEnabled),
+ mAccessPromise(aPromise),
+ mHasShutdown(false) {
+ MOZ_ASSERT(aWindow);
+ MOZ_ASSERT(aPromise);
+}
+
+MIDIAccess::~MIDIAccess() { Shutdown(); }
+
+void MIDIAccess::Shutdown() {
+ if (mHasShutdown) {
+ return;
+ }
+ mDestructionObservers.Broadcast(void_t());
+ if (MIDIAccessManager::IsRunning()) {
+ MIDIAccessManager::Get()->RemoveObserver(this);
+ }
+ mHasShutdown = true;
+}
+
+void MIDIAccess::FireConnectionEvent(MIDIPort* aPort) {
+ MOZ_ASSERT(aPort);
+ MIDIConnectionEventInit init;
+ init.mPort = aPort;
+ nsAutoString id;
+ aPort->GetId(id);
+ ErrorResult rv;
+ if (aPort->State() == MIDIPortDeviceState::Disconnected) {
+ if (aPort->Type() == MIDIPortType::Input &&
+ MIDIInputMap_Binding::MaplikeHelpers::Has(mInputMap, id, rv)) {
+ MIDIInputMap_Binding::MaplikeHelpers::Delete(mInputMap, id, rv);
+ } else if (aPort->Type() == MIDIPortType::Output &&
+ MIDIOutputMap_Binding::MaplikeHelpers::Has(mOutputMap, id, rv)) {
+ MIDIOutputMap_Binding::MaplikeHelpers::Delete(mOutputMap, id, rv);
+ }
+ // Check to make sure Has()/Delete() calls haven't failed.
+ if (NS_WARN_IF(rv.Failed())) {
+ return;
+ }
+ } else {
+ // If we receive an event from a port that is not in one of our port maps,
+ // this means a port that was disconnected has been reconnected, with the
+ // port owner holding the object during that time, and we should add that
+ // port object to our maps again.
+ if (aPort->Type() == MIDIPortType::Input &&
+ !MIDIInputMap_Binding::MaplikeHelpers::Has(mInputMap, id, rv)) {
+ if (NS_WARN_IF(rv.Failed())) {
+ return;
+ }
+ MIDIInputMap_Binding::MaplikeHelpers::Set(
+ mInputMap, id, *(static_cast<MIDIInput*>(aPort)), rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return;
+ }
+ } else if (aPort->Type() == MIDIPortType::Output &&
+ !MIDIOutputMap_Binding::MaplikeHelpers::Has(mOutputMap, id,
+ rv)) {
+ if (NS_WARN_IF(rv.Failed())) {
+ return;
+ }
+ MIDIOutputMap_Binding::MaplikeHelpers::Set(
+ mOutputMap, id, *(static_cast<MIDIOutput*>(aPort)), rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return;
+ }
+ }
+ }
+ RefPtr<MIDIConnectionEvent> event =
+ MIDIConnectionEvent::Constructor(this, u"statechange"_ns, init);
+ DispatchTrustedEvent(event);
+}
+
+void MIDIAccess::MaybeCreateMIDIPort(const MIDIPortInfo& aInfo,
+ ErrorResult& aRv) {
+ nsAutoString id(aInfo.id());
+ MIDIPortType type = static_cast<MIDIPortType>(aInfo.type());
+ RefPtr<MIDIPort> port;
+ if (type == MIDIPortType::Input) {
+ bool hasPort =
+ MIDIInputMap_Binding::MaplikeHelpers::Has(mInputMap, id, aRv);
+ if (hasPort || NS_WARN_IF(aRv.Failed())) {
+ // We already have the port in our map.
+ return;
+ }
+ port = MIDIInput::Create(GetOwner(), this, aInfo, mSysexEnabled);
+ if (NS_WARN_IF(!port)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ MIDIInputMap_Binding::MaplikeHelpers::Set(
+ mInputMap, id, *(static_cast<MIDIInput*>(port.get())), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+ } else if (type == MIDIPortType::Output) {
+ bool hasPort =
+ MIDIOutputMap_Binding::MaplikeHelpers::Has(mOutputMap, id, aRv);
+ if (hasPort || NS_WARN_IF(aRv.Failed())) {
+ // We already have the port in our map.
+ return;
+ }
+ port = MIDIOutput::Create(GetOwner(), this, aInfo, mSysexEnabled);
+ if (NS_WARN_IF(!port)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+ MIDIOutputMap_Binding::MaplikeHelpers::Set(
+ mOutputMap, id, *(static_cast<MIDIOutput*>(port.get())), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+ } else {
+ // If we hit this, then we have some port that is neither input nor output.
+ // That is bad.
+ MOZ_CRASH("We shouldn't be here!");
+ }
+ // Set up port to listen for destruction of this access object.
+ mDestructionObservers.AddObserver(port);
+
+ // If we haven't resolved the promise for handing the MIDIAccess object to
+ // content, this means we're still populating the list of already connected
+ // devices. Don't fire events yet.
+ if (!mAccessPromise) {
+ FireConnectionEvent(port);
+ }
+}
+
+// For the MIDIAccess object, only worry about new connections, where we create
+// MIDIPort objects. When a port is removed and the MIDIPortRemove event is
+// received, that will be handled by the MIDIPort object itself, and it will
+// request removal from MIDIAccess's maps.
+void MIDIAccess::Notify(const MIDIPortList& aEvent) {
+ for (auto& port : aEvent.ports()) {
+ // Something went very wrong. Warn and return.
+ ErrorResult rv;
+ MaybeCreateMIDIPort(port, rv);
+ if (rv.Failed()) {
+ if (!mAccessPromise) {
+ return;
+ }
+ mAccessPromise->MaybeReject(std::move(rv));
+ mAccessPromise = nullptr;
+ }
+ }
+ if (!mAccessPromise) {
+ return;
+ }
+ mAccessPromise->MaybeResolve(this);
+ mAccessPromise = nullptr;
+}
+
+JSObject* MIDIAccess::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return MIDIAccess_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void MIDIAccess::RemovePortListener(MIDIAccessDestructionObserver* aObs) {
+ mDestructionObservers.RemoveObserver(aObs);
+}
+
+} // namespace mozilla::dom