diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/midi/MIDIAccess.cpp | |
parent | Initial commit. (diff) | |
download | firefox-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.cpp | 221 |
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 |