Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / midi / MIDIAccessManager.cpp
blob65c5588cd4b39c7162ef120a139b71907fca2d11
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/MIDIAccessManager.h"
8 #include "mozilla/dom/Document.h"
9 #include "mozilla/dom/MIDIAccess.h"
10 #include "mozilla/dom/MIDIManagerChild.h"
11 #include "mozilla/dom/MIDIPermissionRequest.h"
12 #include "mozilla/dom/FeaturePolicyUtils.h"
13 #include "mozilla/dom/Promise.h"
14 #include "nsIGlobalObject.h"
15 #include "mozilla/ClearOnShutdown.h"
16 #include "mozilla/ipc/PBackgroundChild.h"
17 #include "mozilla/ipc/BackgroundChild.h"
18 #include "mozilla/Preferences.h"
20 using namespace mozilla::ipc;
22 namespace mozilla::dom {
24 namespace {
25 // Singleton object for MIDIAccessManager
26 StaticRefPtr<MIDIAccessManager> gMIDIAccessManager;
27 } // namespace
29 MIDIAccessManager::MIDIAccessManager() : mHasPortList(false), mChild(nullptr) {}
31 MIDIAccessManager::~MIDIAccessManager() = default;
33 // static
34 MIDIAccessManager* MIDIAccessManager::Get() {
35 if (!gMIDIAccessManager) {
36 gMIDIAccessManager = new MIDIAccessManager();
37 ClearOnShutdown(&gMIDIAccessManager);
39 return gMIDIAccessManager;
42 // static
43 bool MIDIAccessManager::IsRunning() { return !!gMIDIAccessManager; }
45 already_AddRefed<Promise> MIDIAccessManager::RequestMIDIAccess(
46 nsPIDOMWindowInner* aWindow, const MIDIOptions& aOptions,
47 ErrorResult& aRv) {
48 MOZ_ASSERT(NS_IsMainThread());
49 MOZ_ASSERT(aWindow);
50 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aWindow);
51 RefPtr<Promise> p = Promise::Create(go, aRv);
52 if (NS_WARN_IF(aRv.Failed())) {
53 return nullptr;
55 nsCOMPtr<Document> doc = aWindow->GetDoc();
56 if (NS_WARN_IF(!doc)) {
57 aRv.Throw(NS_ERROR_FAILURE);
58 return nullptr;
61 if (!FeaturePolicyUtils::IsFeatureAllowed(doc, u"midi"_ns)) {
62 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
63 return nullptr;
66 nsCOMPtr<nsIRunnable> permRunnable =
67 new MIDIPermissionRequest(aWindow, p, aOptions);
68 aRv = NS_DispatchToMainThread(permRunnable);
69 if (NS_WARN_IF(aRv.Failed())) {
70 return nullptr;
72 return p.forget();
75 bool MIDIAccessManager::AddObserver(Observer<MIDIPortList>* aObserver) {
76 // Add observer before we start the service, otherwise we can end up with
77 // device lists being received before we have observers to send them to.
78 mChangeObservers.AddObserver(aObserver);
79 // If we don't currently have a port list, that means this is a new
80 // AccessManager and we possibly need to start the MIDI Service.
81 if (!mChild) {
82 // Otherwise we must begin the PBackground initialization process and
83 // wait for the async ActorCreated() callback.
84 MOZ_ASSERT(NS_IsMainThread());
85 ::mozilla::ipc::PBackgroundChild* actor =
86 BackgroundChild::GetOrCreateForCurrentThread();
87 if (NS_WARN_IF(!actor)) {
88 return false;
90 RefPtr<MIDIManagerChild> mgr(new MIDIManagerChild());
91 PMIDIManagerChild* constructedMgr = actor->SendPMIDIManagerConstructor(mgr);
93 if (NS_WARN_IF(!constructedMgr)) {
94 return false;
96 MOZ_ASSERT(constructedMgr == mgr);
97 mChild = std::move(mgr);
98 // Add a ref to mChild here, that will be deref'd by
99 // BackgroundChildImpl::DeallocPMIDIManagerChild on IPC cleanup.
100 mChild->SetActorAlive();
102 return true;
105 void MIDIAccessManager::RemoveObserver(Observer<MIDIPortList>* aObserver) {
106 mChangeObservers.RemoveObserver(aObserver);
107 if (mChangeObservers.Length() == 0) {
108 // If we're out of listeners, go ahead and shut down. Make sure to cleanup
109 // the IPDL protocol also.
110 if (mChild) {
111 mChild->Shutdown();
112 mChild = nullptr;
114 gMIDIAccessManager = nullptr;
118 void MIDIAccessManager::CreateMIDIAccess(nsPIDOMWindowInner* aWindow,
119 bool aNeedsSysex, Promise* aPromise) {
120 MOZ_ASSERT(aWindow);
121 MOZ_ASSERT(aPromise);
122 RefPtr<MIDIAccess> a(new MIDIAccess(aWindow, aNeedsSysex, aPromise));
123 if (NS_WARN_IF(!AddObserver(a))) {
124 aPromise->MaybeReject(NS_ERROR_FAILURE);
125 return;
127 if (!mHasPortList) {
128 // Hold the access object until we get a connected device list.
129 mAccessHolder.AppendElement(a);
130 } else {
131 // If we already have a port list, just send it to the MIDIAccess object now
132 // so it can prepopulate its device list and resolve the promise.
133 a->Notify(mPortList);
137 void MIDIAccessManager::Update(const MIDIPortList& aPortList) {
138 mPortList = aPortList;
139 mChangeObservers.Broadcast(aPortList);
140 if (!mHasPortList) {
141 mHasPortList = true;
142 // Now that we've broadcast the already-connected port list, content
143 // should manage the lifetime of the MIDIAccess object, so we can clear the
144 // keep-alive array.
145 mAccessHolder.Clear();
149 } // namespace mozilla::dom