Bug 1880216 - Migrate Fenix docs into Sphinx. r=owlish,geckoview-reviewers,android...
[gecko.git] / dom / midi / MIDIPort.cpp
blob94d7280c73d4e4333132ec5b8d49d3fce52ef5a5
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
19 #include "MIDILog.h"
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),
41 mKeepAlive(false) {
42 MOZ_ASSERT(aWindow);
44 Document* aDoc = GetOwner()->GetExtantDoc();
45 if (aDoc) {
46 aDoc->DisallowBFCaching();
50 MIDIPort::~MIDIPort() {
51 if (mMIDIAccessParent) {
52 mMIDIAccessParent->RemovePortListener(this);
53 mMIDIAccessParent = nullptr;
55 if (Port()) {
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();
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;
103 mMIDIAccessParent = aMIDIAccessParent;
104 mPortHolder.Init(port.forget());
105 LOG("MIDIPort::Initialize (%s, %s)",
106 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
107 GetEnumString(Port()->Type()).get());
108 return true;
111 void MIDIPort::UnsetIPCPort() {
112 LOG("MIDIPort::UnsetIPCPort (%s, %s)",
113 NS_ConvertUTF16toUTF8(Port()->Name()).get(),
114 GetEnumString(Port()->Type()).get());
115 mPortHolder.Clear();
118 void MIDIPort::GetId(nsString& aRetVal) const {
119 MOZ_ASSERT(Port());
120 aRetVal = Port()->StableId();
123 void MIDIPort::GetManufacturer(nsString& aRetVal) const {
124 MOZ_ASSERT(Port());
125 aRetVal = Port()->Manufacturer();
128 void MIDIPort::GetName(nsString& aRetVal) const {
129 MOZ_ASSERT(Port());
130 aRetVal = Port()->Name();
133 void MIDIPort::GetVersion(nsString& aRetVal) const {
134 MOZ_ASSERT(Port());
135 aRetVal = Port()->Version();
138 MIDIPortType MIDIPort::Type() const {
139 MOZ_ASSERT(Port());
140 return Port()->Type();
143 MIDIPortConnectionState MIDIPort::Connection() const {
144 MOZ_ASSERT(Port());
145 return Port()->ConnectionState();
148 MIDIPortDeviceState MIDIPort::State() const {
149 MOZ_ASSERT(Port());
150 return Port()->DeviceState();
153 bool MIDIPort::SysexEnabled() const {
154 MOZ_ASSERT(Port());
155 return Port()->SysexEnabled();
158 already_AddRefed<Promise> MIDIPort::Open(ErrorResult& aError) {
159 LOG("MIDIPort::Open");
160 MOZ_ASSERT(Port());
161 RefPtr<Promise> p;
162 if (mOpeningPromise) {
163 p = mOpeningPromise;
164 return p.forget();
166 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
167 p = Promise::Create(go, aError);
168 if (aError.Failed()) {
169 return nullptr;
171 mOpeningPromise = p;
172 Port()->SendOpen();
173 return p.forget();
176 already_AddRefed<Promise> MIDIPort::Close(ErrorResult& aError) {
177 LOG("MIDIPort::Close");
178 MOZ_ASSERT(Port());
179 RefPtr<Promise> p;
180 if (mClosingPromise) {
181 p = mClosingPromise;
182 return p.forget();
184 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
185 p = Promise::Create(go, aError);
186 if (aError.Failed()) {
187 return nullptr;
189 mClosingPromise = p;
190 Port()->SendClose();
191 return p.forget();
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.
197 // Nullify our copy.
198 mMIDIAccessParent = nullptr;
201 void MIDIPort::FireStateChangeEvent() {
202 if (!GetOwner()) {
203 return; // Ignore changes once we've been disconnected from the owner
206 StateChange();
208 MOZ_ASSERT(Port());
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) {
228 Port()->SendOpen();
231 if (Port()->ConnectionState() == MIDIPortConnectionState::Open ||
232 (Port()->DeviceState() == MIDIPortDeviceState::Connected &&
233 Port()->ConnectionState() == MIDIPortConnectionState::Pending)) {
234 KeepAliveOnStatechange();
235 } else {
236 DontKeepAliveOnStatechange();
239 // Fire MIDIAccess events first so that the port is no longer in the port
240 // maps.
241 if (mMIDIAccessParent) {
242 mMIDIAccessParent->FireConnectionEvent(this);
245 MIDIConnectionEventInit init;
246 init.mPort = this;
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() {
259 if (Port()) {
260 Port()->SendClose();
262 DontKeepAliveOnStatechange();
264 DOMEventTargetHelper::DisconnectFromOwner();
267 void MIDIPort::KeepAliveOnStatechange() {
268 if (!mKeepAlive) {
269 mKeepAlive = true;
270 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
274 void MIDIPort::DontKeepAliveOnStatechange() {
275 if (mKeepAlive) {
276 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
277 mKeepAlive = false;
281 const nsString& MIDIPort::StableId() { return Port()->StableId(); }
283 } // namespace mozilla::dom