Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / midi / MIDIPort.cpp
blobd8ae4a48ff6ddfa4023fa286c034dc497644dcbe
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
18 #include "MIDILog.h"
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),
40 mKeepAlive(false) {
41 MOZ_ASSERT(aWindow);
42 MOZ_ASSERT(aMIDIAccessParent);
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 (mPort) {
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.
61 mPort->Teardown();
65 bool MIDIPort::Initialize(const MIDIPortInfo& aPortInfo, bool aSysexEnabled) {
66 RefPtr<MIDIPortChild> port =
67 new MIDIPortChild(aPortInfo, aSysexEnabled, this);
68 PBackgroundChild* b = BackgroundChild::GetForCurrentThread();
69 MOZ_ASSERT(b,
70 "Should always have a valid BackgroundChild when creating a port "
71 "object!");
72 if (!b->SendPMIDIPortConstructor(port, aPortInfo, aSysexEnabled)) {
73 return false;
75 mPort = port;
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();
82 return true;
85 void MIDIPort::UnsetIPCPort() {
86 LOG("MIDIPort::UnsetIPCPort (%s, %s)",
87 NS_ConvertUTF16toUTF8(mPort->Name()).get(),
88 MIDIPortTypeValues::strings[uint32_t(mPort->Type())].value);
89 mPort = nullptr;
92 void MIDIPort::GetId(nsString& aRetVal) const {
93 MOZ_ASSERT(mPort);
94 aRetVal = mPort->MIDIPortInterface::Id();
97 void MIDIPort::GetManufacturer(nsString& aRetVal) const {
98 MOZ_ASSERT(mPort);
99 aRetVal = mPort->Manufacturer();
102 void MIDIPort::GetName(nsString& aRetVal) const {
103 MOZ_ASSERT(mPort);
104 aRetVal = mPort->Name();
107 void MIDIPort::GetVersion(nsString& aRetVal) const {
108 MOZ_ASSERT(mPort);
109 aRetVal = mPort->Version();
112 MIDIPortType MIDIPort::Type() const {
113 MOZ_ASSERT(mPort);
114 return mPort->Type();
117 MIDIPortConnectionState MIDIPort::Connection() const {
118 MOZ_ASSERT(mPort);
119 return mPort->ConnectionState();
122 MIDIPortDeviceState MIDIPort::State() const {
123 MOZ_ASSERT(mPort);
124 return mPort->DeviceState();
127 bool MIDIPort::SysexEnabled() const {
128 MOZ_ASSERT(mPort);
129 return mPort->SysexEnabled();
132 already_AddRefed<Promise> MIDIPort::Open() {
133 LOG("MIDIPort::Open");
134 MOZ_ASSERT(mPort);
135 RefPtr<Promise> p;
136 if (mOpeningPromise) {
137 p = mOpeningPromise;
138 return p.forget();
140 ErrorResult rv;
141 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
142 p = Promise::Create(go, rv);
143 if (rv.Failed()) {
144 return nullptr;
146 mOpeningPromise = p;
147 mPort->SendOpen();
148 return p.forget();
151 already_AddRefed<Promise> MIDIPort::Close() {
152 LOG("MIDIPort::Close");
153 MOZ_ASSERT(mPort);
154 RefPtr<Promise> p;
155 if (mClosingPromise) {
156 p = mClosingPromise;
157 return p.forget();
159 ErrorResult rv;
160 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
161 p = Promise::Create(go, rv);
162 if (rv.Failed()) {
163 return nullptr;
165 mClosingPromise = p;
166 mPort->SendClose();
167 return p.forget();
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.
173 // Nullify our copy.
174 mMIDIAccessParent = nullptr;
177 void MIDIPort::FireStateChangeEvent() {
178 if (!GetOwner()) {
179 return; // Ignore changes once we've been disconnected from the owner
182 StateChange();
184 MOZ_ASSERT(mPort);
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) {
204 mPort->SendOpen();
207 if (mPort->ConnectionState() == MIDIPortConnectionState::Open ||
208 (mPort->DeviceState() == MIDIPortDeviceState::Connected &&
209 mPort->ConnectionState() == MIDIPortConnectionState::Pending)) {
210 KeepAliveOnStatechange();
211 } else {
212 DontKeepAliveOnStatechange();
215 // Fire MIDIAccess events first so that the port is no longer in the port
216 // maps.
217 if (mMIDIAccessParent) {
218 mMIDIAccessParent->FireConnectionEvent(this);
221 MIDIConnectionEventInit init;
222 init.mPort = this;
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();
236 mPort->SendClose();
238 DOMEventTargetHelper::DisconnectFromOwner();
241 void MIDIPort::KeepAliveOnStatechange() {
242 if (!mKeepAlive) {
243 mKeepAlive = true;
244 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
248 void MIDIPort::DontKeepAliveOnStatechange() {
249 if (mKeepAlive) {
250 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
251 mKeepAlive = false;
255 } // namespace mozilla::dom