Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/midi/MIDIAccessManager.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "mozilla/dom/MIDIAccessManager.h"
8
#include "mozilla/dom/MIDIAccess.h"
9
#include "mozilla/dom/MIDIManagerChild.h"
10
#include "mozilla/dom/MIDIPermissionRequest.h"
11
#include "mozilla/dom/Promise.h"
12
#include "nsIGlobalObject.h"
13
#include "mozilla/ClearOnShutdown.h"
14
#include "mozilla/ipc/PBackgroundChild.h"
15
#include "mozilla/ipc/BackgroundChild.h"
16
#include "mozilla/Preferences.h"
17
18
using namespace mozilla::ipc;
19
20
namespace mozilla {
21
namespace dom {
22
23
namespace {
24
// Singleton object for MIDIAccessManager
25
StaticRefPtr<MIDIAccessManager> gMIDIAccessManager;
26
}
27
28
MIDIAccessManager::MIDIAccessManager() :
29
  mHasPortList(false),
30
  mChild(nullptr)
31
0
{
32
0
}
33
34
MIDIAccessManager::~MIDIAccessManager()
35
0
{
36
0
}
37
38
//static
39
MIDIAccessManager*
40
MIDIAccessManager::Get()
41
0
{
42
0
  if (!gMIDIAccessManager) {
43
0
    gMIDIAccessManager = new MIDIAccessManager();
44
0
    ClearOnShutdown(&gMIDIAccessManager);
45
0
  }
46
0
  return gMIDIAccessManager;
47
0
}
48
49
//static
50
bool
51
MIDIAccessManager::IsRunning()
52
0
{
53
0
  return !!gMIDIAccessManager;
54
0
}
55
56
already_AddRefed<Promise>
57
MIDIAccessManager::RequestMIDIAccess(nsPIDOMWindowInner* aWindow,
58
                                     const MIDIOptions& aOptions,
59
                                     ErrorResult& aRv)
60
0
{
61
0
  MOZ_ASSERT(NS_IsMainThread());
62
0
  MOZ_ASSERT(aWindow);
63
0
  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aWindow);
64
0
  RefPtr<Promise> p = Promise::Create(go, aRv);
65
0
  if (NS_WARN_IF(aRv.Failed())) {
66
0
    return nullptr;
67
0
  }
68
0
  nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
69
0
  if (NS_WARN_IF(!doc)) {
70
0
    aRv.Throw(NS_ERROR_FAILURE);
71
0
    return nullptr;
72
0
  }
73
0
  nsCOMPtr<nsIRunnable> permRunnable = new MIDIPermissionRequest(aWindow, p, aOptions);
74
0
  aRv = NS_DispatchToMainThread(permRunnable);
75
0
  if (NS_WARN_IF(aRv.Failed())) {
76
0
    return nullptr;
77
0
  }
78
0
  return p.forget();
79
0
}
80
81
bool
82
MIDIAccessManager::AddObserver(Observer<MIDIPortList>* aObserver)
83
0
{
84
0
  // Add observer before we start the service, otherwise we can end up with
85
0
  // device lists being received before we have observers to send them to.
86
0
  mChangeObservers.AddObserver(aObserver);
87
0
  // If we don't currently have a port list, that means this is a new
88
0
  // AccessManager and we possibly need to start the MIDI Service.
89
0
  if (!mChild) {
90
0
    // Otherwise we must begin the PBackground initialization process and
91
0
    // wait for the async ActorCreated() callback.
92
0
    MOZ_ASSERT(NS_IsMainThread());
93
0
    ::mozilla::ipc::PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
94
0
    if (NS_WARN_IF(!actor)) {
95
0
      return false;
96
0
    }
97
0
    RefPtr<MIDIManagerChild> mgr(new MIDIManagerChild());
98
0
    PMIDIManagerChild* constructedMgr =
99
0
      actor->SendPMIDIManagerConstructor(mgr);
100
0
101
0
    if (NS_WARN_IF(!constructedMgr)) {
102
0
      return false;
103
0
    }
104
0
    MOZ_ASSERT(constructedMgr == mgr);
105
0
    mChild = mgr.forget();
106
0
    // Add a ref to mChild here, that will be deref'd by
107
0
    // BackgroundChildImpl::DeallocPMIDIManagerChild on IPC cleanup.
108
0
    mChild->SetActorAlive();
109
0
  }
110
0
  return true;
111
0
}
112
113
void
114
MIDIAccessManager::RemoveObserver(Observer<MIDIPortList>* aObserver)
115
0
{
116
0
  mChangeObservers.RemoveObserver(aObserver);
117
0
  if (mChangeObservers.Length() == 0) {
118
0
    // If we're out of listeners, go ahead and shut down. Make sure to cleanup
119
0
    // the IPDL protocol also.
120
0
    if (mChild) {
121
0
      mChild->Shutdown();
122
0
      mChild = nullptr;
123
0
    }
124
0
    gMIDIAccessManager = nullptr;
125
0
  }
126
0
}
127
128
void
129
MIDIAccessManager::CreateMIDIAccess(nsPIDOMWindowInner* aWindow,
130
                                    bool aNeedsSysex,
131
                                    Promise* aPromise)
132
0
{
133
0
  MOZ_ASSERT(aWindow);
134
0
  MOZ_ASSERT(aPromise);
135
0
  RefPtr<MIDIAccess> a(new MIDIAccess(aWindow, aNeedsSysex, aPromise));
136
0
  if (NS_WARN_IF(!AddObserver(a))) {
137
0
    aPromise->MaybeReject(NS_ERROR_FAILURE);
138
0
    return;
139
0
  }
140
0
  if (!mHasPortList) {
141
0
    // Hold the access object until we get a connected device list.
142
0
    mAccessHolder.AppendElement(a);
143
0
  } else {
144
0
    // If we already have a port list, just send it to the MIDIAccess object now
145
0
    // so it can prepopulate its device list and resolve the promise.
146
0
    a->Notify(mPortList);
147
0
  }
148
0
}
149
150
void
151
MIDIAccessManager::Update(const MIDIPortList& aPortList)
152
0
{
153
0
  mPortList = aPortList;
154
0
  mChangeObservers.Broadcast(aPortList);
155
0
  if (!mHasPortList) {
156
0
    mHasPortList = true;
157
0
    // Now that we've broadcast the already-connected port list, content
158
0
    // should manage the lifetime of the MIDIAccess object, so we can clear the
159
0
    // keep-alive array.
160
0
    mAccessHolder.Clear();
161
0
  }
162
0
}
163
164
}
165
}