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 "mozilla/dom/MIDIPort.h"
8 #include "mozilla/dom/MIDIConnectionEvent.h"
9 #include "mozilla/dom/MIDIPortChild.h"
10 #include "mozilla/dom/MIDIAccess.h"
11 #include "mozilla/dom/MIDITypes.h"
12 #include "mozilla/ipc/PBackgroundChild.h"
13 #include "mozilla/ipc/BackgroundChild.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/Promise.h"
16 #include "mozilla/Unused.h"
17 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
20 using namespace mozilla::ipc
;
22 namespace mozilla::dom
{
24 NS_IMPL_CYCLE_COLLECTION_INHERITED(MIDIPort
, DOMEventTargetHelper
,
25 mOpeningPromise
, mClosingPromise
)
27 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIPort
, DOMEventTargetHelper
)
28 NS_IMPL_CYCLE_COLLECTION_TRACE_END
30 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIPort
)
31 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
32 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
34 NS_IMPL_ADDREF_INHERITED(MIDIPort
, DOMEventTargetHelper
)
35 NS_IMPL_RELEASE_INHERITED(MIDIPort
, DOMEventTargetHelper
)
37 MIDIPort::MIDIPort(nsPIDOMWindowInner
* aWindow
, MIDIAccess
* aMIDIAccessParent
)
38 : DOMEventTargetHelper(aWindow
),
39 mMIDIAccessParent(aMIDIAccessParent
),
42 MOZ_ASSERT(aMIDIAccessParent
);
44 Document
* aDoc
= GetOwner()->GetExtantDoc();
46 aDoc
->DisallowBFCaching();
50 MIDIPort::~MIDIPort() {
51 if (mMIDIAccessParent
) {
52 mMIDIAccessParent
->RemovePortListener(this);
53 mMIDIAccessParent
= nullptr;
56 // If the IPC port channel is still alive at this point, it means we're
57 // probably CC'ing this port object. Send the shutdown message to also clean
58 // up the IPC channel.
59 mPort
->SendShutdown();
60 // This will unset the IPC Port pointer. Don't call anything after this.
65 bool MIDIPort::Initialize(const MIDIPortInfo
& aPortInfo
, bool aSysexEnabled
) {
66 RefPtr
<MIDIPortChild
> port
=
67 new MIDIPortChild(aPortInfo
, aSysexEnabled
, this);
68 PBackgroundChild
* b
= BackgroundChild::GetForCurrentThread();
70 "Should always have a valid BackgroundChild when creating a port "
72 if (!b
->SendPMIDIPortConstructor(port
, aPortInfo
, aSysexEnabled
)) {
76 LOG("MIDIPort::Initialize (%s, %s)",
77 NS_ConvertUTF16toUTF8(mPort
->Name()).get(),
78 MIDIPortTypeValues::strings
[uint32_t(mPort
->Type())].value
);
79 // Make sure to increase the ref count for the port, so it can be cleaned up
80 // by the IPC manager.
81 mPort
->SetActorAlive();
85 void MIDIPort::UnsetIPCPort() {
86 LOG("MIDIPort::UnsetIPCPort (%s, %s)",
87 NS_ConvertUTF16toUTF8(mPort
->Name()).get(),
88 MIDIPortTypeValues::strings
[uint32_t(mPort
->Type())].value
);
92 void MIDIPort::GetId(nsString
& aRetVal
) const {
94 aRetVal
= mPort
->MIDIPortInterface::Id();
97 void MIDIPort::GetManufacturer(nsString
& aRetVal
) const {
99 aRetVal
= mPort
->Manufacturer();
102 void MIDIPort::GetName(nsString
& aRetVal
) const {
104 aRetVal
= mPort
->Name();
107 void MIDIPort::GetVersion(nsString
& aRetVal
) const {
109 aRetVal
= mPort
->Version();
112 MIDIPortType
MIDIPort::Type() const {
114 return mPort
->Type();
117 MIDIPortConnectionState
MIDIPort::Connection() const {
119 return mPort
->ConnectionState();
122 MIDIPortDeviceState
MIDIPort::State() const {
124 return mPort
->DeviceState();
127 bool MIDIPort::SysexEnabled() const {
129 return mPort
->SysexEnabled();
132 already_AddRefed
<Promise
> MIDIPort::Open() {
133 LOG("MIDIPort::Open");
136 if (mOpeningPromise
) {
141 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(GetOwner());
142 p
= Promise::Create(go
, rv
);
151 already_AddRefed
<Promise
> MIDIPort::Close() {
152 LOG("MIDIPort::Close");
155 if (mClosingPromise
) {
160 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(GetOwner());
161 p
= Promise::Create(go
, rv
);
170 void MIDIPort::Notify(const void_t
& aVoid
) {
171 LOG("MIDIPort::notify MIDIAccess shutting down, dropping reference.");
172 // If we're getting notified, it means the MIDIAccess parent object is dead.
174 mMIDIAccessParent
= nullptr;
177 void MIDIPort::FireStateChangeEvent() {
179 return; // Ignore changes once we've been disconnected from the owner
185 if (mPort
->ConnectionState() == MIDIPortConnectionState::Open
||
186 mPort
->ConnectionState() == MIDIPortConnectionState::Pending
) {
187 if (mOpeningPromise
) {
188 mOpeningPromise
->MaybeResolve(this);
189 mOpeningPromise
= nullptr;
191 } else if (mPort
->ConnectionState() == MIDIPortConnectionState::Closed
) {
192 if (mOpeningPromise
) {
193 mOpeningPromise
->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
194 mOpeningPromise
= nullptr;
196 if (mClosingPromise
) {
197 mClosingPromise
->MaybeResolve(this);
198 mClosingPromise
= nullptr;
202 if (mPort
->DeviceState() == MIDIPortDeviceState::Connected
&&
203 mPort
->ConnectionState() == MIDIPortConnectionState::Pending
) {
207 if (mPort
->ConnectionState() == MIDIPortConnectionState::Open
||
208 (mPort
->DeviceState() == MIDIPortDeviceState::Connected
&&
209 mPort
->ConnectionState() == MIDIPortConnectionState::Pending
)) {
210 KeepAliveOnStatechange();
212 DontKeepAliveOnStatechange();
215 // Fire MIDIAccess events first so that the port is no longer in the port
217 if (mMIDIAccessParent
) {
218 mMIDIAccessParent
->FireConnectionEvent(this);
221 MIDIConnectionEventInit init
;
223 RefPtr
<MIDIConnectionEvent
> event(
224 MIDIConnectionEvent::Constructor(this, u
"statechange"_ns
, init
));
225 DispatchTrustedEvent(event
);
228 void MIDIPort::StateChange() {}
230 void MIDIPort::Receive(const nsTArray
<MIDIMessage
>& aMsg
) {
231 MOZ_CRASH("We should never get here!");
234 void MIDIPort::DisconnectFromOwner() {
235 DontKeepAliveOnStatechange();
238 DOMEventTargetHelper::DisconnectFromOwner();
241 void MIDIPort::KeepAliveOnStatechange() {
244 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange
);
248 void MIDIPort::DontKeepAliveOnStatechange() {
250 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange
);
255 } // namespace mozilla::dom