Backed out changeset ddccd40117a0 (bug 1853271) for causing bug 1854769. CLOSED TREE
[gecko.git] / dom / midi / MIDIPort.cpp
blob9b6892bdf4d79de3507581f650c2da47e1da1df2
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
20 #include "MIDILog.h"
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),
42 mKeepAlive(false) {
43 MOZ_ASSERT(aWindow);
44 MOZ_ASSERT(aMIDIAccessParent);
46 Document* aDoc = GetOwner()->GetExtantDoc();
47 if (aDoc) {
48 aDoc->DisallowBFCaching();
52 MIDIPort::~MIDIPort() {
53 if (mMIDIAccessParent) {
54 mMIDIAccessParent->RemovePortListener(this);
55 mMIDIAccessParent = nullptr;
57 if (Port()) {
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();
67 if (!document) {
68 return false;
71 nsCOMPtr<nsIURI> uri = document->GetDocumentURI();
72 if (!uri) {
73 return false;
76 nsAutoCString origin;
77 nsresult rv = nsContentUtils::GetWebExposedOriginSerialization(uri, origin);
78 if (NS_FAILED(rv)) {
79 return false;
81 RefPtr<MIDIPortChild> port =
82 new MIDIPortChild(aPortInfo, aSysexEnabled, this);
83 if (NS_FAILED(port->GenerateStableId(origin))) {
84 return false;
86 PBackgroundChild* b = BackgroundChild::GetForCurrentThread();
87 MOZ_ASSERT(b,
88 "Should always have a valid BackgroundChild when creating a port "
89 "object!");
91 // Create the endpoints and bind the one on the child side.
92 Endpoint<PMIDIPortParent> parentEndpoint;
93 Endpoint<PMIDIPortChild> childEndpoint;
94 MOZ_ALWAYS_SUCCEEDS(
95 PMIDIPort::CreateEndpoints(&parentEndpoint, &childEndpoint));
96 MOZ_ALWAYS_TRUE(childEndpoint.Bind(port));
98 if (!b->SendCreateMIDIPort(std::move(parentEndpoint), aPortInfo,
99 aSysexEnabled)) {
100 return false;
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);
106 return true;
109 void MIDIPort::UnsetIPCPort() {
110 LOG("MIDIPort::UnsetIPCPort (%s, %s)",
111 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
112 MIDIPortTypeValues::strings[uint32_t(Port()->Type())].value);
113 mPortHolder.Clear();
116 void MIDIPort::GetId(nsString& aRetVal) const {
117 MOZ_ASSERT(Port());
118 aRetVal = Port()->StableId();
121 void MIDIPort::GetManufacturer(nsString& aRetVal) const {
122 MOZ_ASSERT(Port());
123 aRetVal = Port()->Manufacturer();
126 void MIDIPort::GetName(nsString& aRetVal) const {
127 MOZ_ASSERT(Port());
128 aRetVal = Port()->Name();
131 void MIDIPort::GetVersion(nsString& aRetVal) const {
132 MOZ_ASSERT(Port());
133 aRetVal = Port()->Version();
136 MIDIPortType MIDIPort::Type() const {
137 MOZ_ASSERT(Port());
138 return Port()->Type();
141 MIDIPortConnectionState MIDIPort::Connection() const {
142 MOZ_ASSERT(Port());
143 return Port()->ConnectionState();
146 MIDIPortDeviceState MIDIPort::State() const {
147 MOZ_ASSERT(Port());
148 return Port()->DeviceState();
151 bool MIDIPort::SysexEnabled() const {
152 MOZ_ASSERT(Port());
153 return Port()->SysexEnabled();
156 already_AddRefed<Promise> MIDIPort::Open(ErrorResult& aError) {
157 LOG("MIDIPort::Open");
158 MOZ_ASSERT(Port());
159 RefPtr<Promise> p;
160 if (mOpeningPromise) {
161 p = mOpeningPromise;
162 return p.forget();
164 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
165 p = Promise::Create(go, aError);
166 if (aError.Failed()) {
167 return nullptr;
169 mOpeningPromise = p;
170 Port()->SendOpen();
171 return p.forget();
174 already_AddRefed<Promise> MIDIPort::Close(ErrorResult& aError) {
175 LOG("MIDIPort::Close");
176 MOZ_ASSERT(Port());
177 RefPtr<Promise> p;
178 if (mClosingPromise) {
179 p = mClosingPromise;
180 return p.forget();
182 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
183 p = Promise::Create(go, aError);
184 if (aError.Failed()) {
185 return nullptr;
187 mClosingPromise = p;
188 Port()->SendClose();
189 return p.forget();
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.
195 // Nullify our copy.
196 mMIDIAccessParent = nullptr;
199 void MIDIPort::FireStateChangeEvent() {
200 if (!GetOwner()) {
201 return; // Ignore changes once we've been disconnected from the owner
204 StateChange();
206 MOZ_ASSERT(Port());
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) {
226 Port()->SendOpen();
229 if (Port()->ConnectionState() == MIDIPortConnectionState::Open ||
230 (Port()->DeviceState() == MIDIPortDeviceState::Connected &&
231 Port()->ConnectionState() == MIDIPortConnectionState::Pending)) {
232 KeepAliveOnStatechange();
233 } else {
234 DontKeepAliveOnStatechange();
237 // Fire MIDIAccess events first so that the port is no longer in the port
238 // maps.
239 if (mMIDIAccessParent) {
240 mMIDIAccessParent->FireConnectionEvent(this);
243 MIDIConnectionEventInit init;
244 init.mPort = this;
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();
258 Port()->SendClose();
260 DOMEventTargetHelper::DisconnectFromOwner();
263 void MIDIPort::KeepAliveOnStatechange() {
264 if (!mKeepAlive) {
265 mKeepAlive = true;
266 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
270 void MIDIPort::DontKeepAliveOnStatechange() {
271 if (mKeepAlive) {
272 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
273 mKeepAlive = false;
277 const nsString& MIDIPort::StableId() { return Port()->StableId(); }
279 } // namespace mozilla::dom