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 "nsContentUtils.h"
18 #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
21 using namespace mozilla::ipc
;
23 namespace mozilla::dom
{
25 NS_IMPL_CYCLE_COLLECTION_INHERITED(MIDIPort
, DOMEventTargetHelper
,
26 mOpeningPromise
, mClosingPromise
)
28 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIPort
, DOMEventTargetHelper
)
29 NS_IMPL_CYCLE_COLLECTION_TRACE_END
31 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIPort
)
32 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
33 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
35 NS_IMPL_ADDREF_INHERITED(MIDIPort
, DOMEventTargetHelper
)
36 NS_IMPL_RELEASE_INHERITED(MIDIPort
, DOMEventTargetHelper
)
38 MIDIPort::MIDIPort(nsPIDOMWindowInner
* aWindow
)
39 : DOMEventTargetHelper(aWindow
),
40 mMIDIAccessParent(nullptr),
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 Port()->SendShutdown();
63 bool MIDIPort::Initialize(const MIDIPortInfo
& aPortInfo
, bool aSysexEnabled
,
64 MIDIAccess
* aMIDIAccessParent
) {
65 MOZ_ASSERT(aMIDIAccessParent
);
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
,
103 mMIDIAccessParent
= aMIDIAccessParent
;
104 mPortHolder
.Init(port
.forget());
105 LOG("MIDIPort::Initialize (%s, %s)",
106 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
107 MIDIPortTypeValues::strings
[uint32_t(Port()->Type())].value
);
111 void MIDIPort::UnsetIPCPort() {
112 LOG("MIDIPort::UnsetIPCPort (%s, %s)",
113 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
114 MIDIPortTypeValues::strings
[uint32_t(Port()->Type())].value
);
118 void MIDIPort::GetId(nsString
& aRetVal
) const {
120 aRetVal
= Port()->StableId();
123 void MIDIPort::GetManufacturer(nsString
& aRetVal
) const {
125 aRetVal
= Port()->Manufacturer();
128 void MIDIPort::GetName(nsString
& aRetVal
) const {
130 aRetVal
= Port()->Name();
133 void MIDIPort::GetVersion(nsString
& aRetVal
) const {
135 aRetVal
= Port()->Version();
138 MIDIPortType
MIDIPort::Type() const {
140 return Port()->Type();
143 MIDIPortConnectionState
MIDIPort::Connection() const {
145 return Port()->ConnectionState();
148 MIDIPortDeviceState
MIDIPort::State() const {
150 return Port()->DeviceState();
153 bool MIDIPort::SysexEnabled() const {
155 return Port()->SysexEnabled();
158 already_AddRefed
<Promise
> MIDIPort::Open(ErrorResult
& aError
) {
159 LOG("MIDIPort::Open");
162 if (mOpeningPromise
) {
166 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(GetOwner());
167 p
= Promise::Create(go
, aError
);
168 if (aError
.Failed()) {
176 already_AddRefed
<Promise
> MIDIPort::Close(ErrorResult
& aError
) {
177 LOG("MIDIPort::Close");
180 if (mClosingPromise
) {
184 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(GetOwner());
185 p
= Promise::Create(go
, aError
);
186 if (aError
.Failed()) {
194 void MIDIPort::Notify(const void_t
& aVoid
) {
195 LOG("MIDIPort::notify MIDIAccess shutting down, dropping reference.");
196 // If we're getting notified, it means the MIDIAccess parent object is dead.
198 mMIDIAccessParent
= nullptr;
201 void MIDIPort::FireStateChangeEvent() {
203 return; // Ignore changes once we've been disconnected from the owner
209 if (Port()->ConnectionState() == MIDIPortConnectionState::Open
||
210 Port()->ConnectionState() == MIDIPortConnectionState::Pending
) {
211 if (mOpeningPromise
) {
212 mOpeningPromise
->MaybeResolve(this);
213 mOpeningPromise
= nullptr;
215 } else if (Port()->ConnectionState() == MIDIPortConnectionState::Closed
) {
216 if (mOpeningPromise
) {
217 mOpeningPromise
->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
218 mOpeningPromise
= nullptr;
220 if (mClosingPromise
) {
221 mClosingPromise
->MaybeResolve(this);
222 mClosingPromise
= nullptr;
226 if (Port()->DeviceState() == MIDIPortDeviceState::Connected
&&
227 Port()->ConnectionState() == MIDIPortConnectionState::Pending
) {
231 if (Port()->ConnectionState() == MIDIPortConnectionState::Open
||
232 (Port()->DeviceState() == MIDIPortDeviceState::Connected
&&
233 Port()->ConnectionState() == MIDIPortConnectionState::Pending
)) {
234 KeepAliveOnStatechange();
236 DontKeepAliveOnStatechange();
239 // Fire MIDIAccess events first so that the port is no longer in the port
241 if (mMIDIAccessParent
) {
242 mMIDIAccessParent
->FireConnectionEvent(this);
245 MIDIConnectionEventInit init
;
247 RefPtr
<MIDIConnectionEvent
> event(
248 MIDIConnectionEvent::Constructor(this, u
"statechange"_ns
, init
));
249 DispatchTrustedEvent(event
);
252 void MIDIPort::StateChange() {}
254 void MIDIPort::Receive(const nsTArray
<MIDIMessage
>& aMsg
) {
255 MOZ_CRASH("We should never get here!");
258 void MIDIPort::DisconnectFromOwner() {
262 DontKeepAliveOnStatechange();
264 DOMEventTargetHelper::DisconnectFromOwner();
267 void MIDIPort::KeepAliveOnStatechange() {
270 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange
);
274 void MIDIPort::DontKeepAliveOnStatechange() {
276 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange
);
281 const nsString
& MIDIPort::StableId() { return Port()->StableId(); }
283 } // namespace mozilla::dom