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/Endpoint.h"
13 #include "mozilla/ipc/PBackgroundChild.h"
14 #include "mozilla/ipc/BackgroundChild.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/Unused.h"
18 #include "nsContentUtils.h"
19 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
22 using namespace mozilla::ipc
;
24 namespace mozilla::dom
{
26 NS_IMPL_CYCLE_COLLECTION_INHERITED(MIDIPort
, DOMEventTargetHelper
,
27 mOpeningPromise
, mClosingPromise
)
29 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIPort
, DOMEventTargetHelper
)
30 NS_IMPL_CYCLE_COLLECTION_TRACE_END
32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIPort
)
33 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
34 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
36 NS_IMPL_ADDREF_INHERITED(MIDIPort
, DOMEventTargetHelper
)
37 NS_IMPL_RELEASE_INHERITED(MIDIPort
, DOMEventTargetHelper
)
39 MIDIPort::MIDIPort(nsPIDOMWindowInner
* aWindow
, MIDIAccess
* aMIDIAccessParent
)
40 : DOMEventTargetHelper(aWindow
),
41 mMIDIAccessParent(aMIDIAccessParent
),
44 MOZ_ASSERT(aMIDIAccessParent
);
46 Document
* aDoc
= GetOwner()->GetExtantDoc();
48 aDoc
->DisallowBFCaching();
52 MIDIPort::~MIDIPort() {
53 if (mMIDIAccessParent
) {
54 mMIDIAccessParent
->RemovePortListener(this);
55 mMIDIAccessParent
= nullptr;
58 // If the IPC port channel is still alive at this point, it means we're
59 // probably CC'ing this port object. Send the shutdown message to also clean
60 // up the IPC channel.
61 Port()->SendShutdown();
65 bool MIDIPort::Initialize(const MIDIPortInfo
& aPortInfo
, bool aSysexEnabled
) {
66 nsCOMPtr
<Document
> document
= GetDocumentIfCurrent();
71 nsCOMPtr
<nsIURI
> uri
= document
->GetDocumentURI();
77 nsresult rv
= nsContentUtils::GetWebExposedOriginSerialization(uri
, origin
);
81 RefPtr
<MIDIPortChild
> port
=
82 new MIDIPortChild(aPortInfo
, aSysexEnabled
, this);
83 if (NS_FAILED(port
->GenerateStableId(origin
))) {
86 PBackgroundChild
* b
= BackgroundChild::GetForCurrentThread();
88 "Should always have a valid BackgroundChild when creating a port "
91 // Create the endpoints and bind the one on the child side.
92 Endpoint
<PMIDIPortParent
> parentEndpoint
;
93 Endpoint
<PMIDIPortChild
> childEndpoint
;
95 PMIDIPort::CreateEndpoints(&parentEndpoint
, &childEndpoint
));
96 MOZ_ALWAYS_TRUE(childEndpoint
.Bind(port
));
98 if (!b
->SendCreateMIDIPort(std::move(parentEndpoint
), aPortInfo
,
102 mPortHolder
.Init(port
.forget());
103 LOG("MIDIPort::Initialize (%s, %s)",
104 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
105 MIDIPortTypeValues::strings
[uint32_t(Port()->Type())].value
);
109 void MIDIPort::UnsetIPCPort() {
110 LOG("MIDIPort::UnsetIPCPort (%s, %s)",
111 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
112 MIDIPortTypeValues::strings
[uint32_t(Port()->Type())].value
);
116 void MIDIPort::GetId(nsString
& aRetVal
) const {
118 aRetVal
= Port()->StableId();
121 void MIDIPort::GetManufacturer(nsString
& aRetVal
) const {
123 aRetVal
= Port()->Manufacturer();
126 void MIDIPort::GetName(nsString
& aRetVal
) const {
128 aRetVal
= Port()->Name();
131 void MIDIPort::GetVersion(nsString
& aRetVal
) const {
133 aRetVal
= Port()->Version();
136 MIDIPortType
MIDIPort::Type() const {
138 return Port()->Type();
141 MIDIPortConnectionState
MIDIPort::Connection() const {
143 return Port()->ConnectionState();
146 MIDIPortDeviceState
MIDIPort::State() const {
148 return Port()->DeviceState();
151 bool MIDIPort::SysexEnabled() const {
153 return Port()->SysexEnabled();
156 already_AddRefed
<Promise
> MIDIPort::Open(ErrorResult
& aError
) {
157 LOG("MIDIPort::Open");
160 if (mOpeningPromise
) {
164 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(GetOwner());
165 p
= Promise::Create(go
, aError
);
166 if (aError
.Failed()) {
174 already_AddRefed
<Promise
> MIDIPort::Close(ErrorResult
& aError
) {
175 LOG("MIDIPort::Close");
178 if (mClosingPromise
) {
182 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(GetOwner());
183 p
= Promise::Create(go
, aError
);
184 if (aError
.Failed()) {
192 void MIDIPort::Notify(const void_t
& aVoid
) {
193 LOG("MIDIPort::notify MIDIAccess shutting down, dropping reference.");
194 // If we're getting notified, it means the MIDIAccess parent object is dead.
196 mMIDIAccessParent
= nullptr;
199 void MIDIPort::FireStateChangeEvent() {
201 return; // Ignore changes once we've been disconnected from the owner
207 if (Port()->ConnectionState() == MIDIPortConnectionState::Open
||
208 Port()->ConnectionState() == MIDIPortConnectionState::Pending
) {
209 if (mOpeningPromise
) {
210 mOpeningPromise
->MaybeResolve(this);
211 mOpeningPromise
= nullptr;
213 } else if (Port()->ConnectionState() == MIDIPortConnectionState::Closed
) {
214 if (mOpeningPromise
) {
215 mOpeningPromise
->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
216 mOpeningPromise
= nullptr;
218 if (mClosingPromise
) {
219 mClosingPromise
->MaybeResolve(this);
220 mClosingPromise
= nullptr;
224 if (Port()->DeviceState() == MIDIPortDeviceState::Connected
&&
225 Port()->ConnectionState() == MIDIPortConnectionState::Pending
) {
229 if (Port()->ConnectionState() == MIDIPortConnectionState::Open
||
230 (Port()->DeviceState() == MIDIPortDeviceState::Connected
&&
231 Port()->ConnectionState() == MIDIPortConnectionState::Pending
)) {
232 KeepAliveOnStatechange();
234 DontKeepAliveOnStatechange();
237 // Fire MIDIAccess events first so that the port is no longer in the port
239 if (mMIDIAccessParent
) {
240 mMIDIAccessParent
->FireConnectionEvent(this);
243 MIDIConnectionEventInit init
;
245 RefPtr
<MIDIConnectionEvent
> event(
246 MIDIConnectionEvent::Constructor(this, u
"statechange"_ns
, init
));
247 DispatchTrustedEvent(event
);
250 void MIDIPort::StateChange() {}
252 void MIDIPort::Receive(const nsTArray
<MIDIMessage
>& aMsg
) {
253 MOZ_CRASH("We should never get here!");
256 void MIDIPort::DisconnectFromOwner() {
257 DontKeepAliveOnStatechange();
260 DOMEventTargetHelper::DisconnectFromOwner();
263 void MIDIPort::KeepAliveOnStatechange() {
266 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange
);
270 void MIDIPort::DontKeepAliveOnStatechange() {
272 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange
);
277 const nsString
& MIDIPort::StableId() { return Port()->StableId(); }
279 } // namespace mozilla::dom