Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/midi/MIDIAccess.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
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/dom/MIDIAccess.h"
8
#include "mozilla/dom/MIDIAccessManager.h"
9
#include "mozilla/dom/MIDIPort.h"
10
#include "mozilla/dom/MIDIAccessBinding.h"
11
#include "mozilla/dom/MIDIConnectionEvent.h"
12
#include "mozilla/dom/MIDIOptionsBinding.h"
13
#include "mozilla/dom/MIDIOutputMapBinding.h"
14
#include "mozilla/dom/MIDIInputMapBinding.h"
15
#include "mozilla/dom/MIDIOutputMap.h"
16
#include "mozilla/dom/MIDIInputMap.h"
17
#include "mozilla/dom/MIDIOutput.h"
18
#include "mozilla/dom/MIDIInput.h"
19
#include "mozilla/dom/MIDITypes.h"
20
#include "mozilla/dom/Promise.h"
21
#include "mozilla/dom/PContent.h"
22
#include "nsIRunnable.h"
23
#include "nsIDocument.h"
24
#include "nsPIDOMWindow.h"
25
#include "nsContentPermissionHelper.h"
26
#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
27
#include "IPCMessageUtils.h"
28
29
namespace mozilla {
30
namespace dom {
31
32
NS_IMPL_CYCLE_COLLECTION_CLASS(MIDIAccess)
33
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MIDIAccess,
34
0
                                               DOMEventTargetHelper)
35
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
36
37
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MIDIAccess,
38
0
                                                  DOMEventTargetHelper)
39
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputMap)
40
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputMap)
41
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessPromise)
42
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
43
44
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MIDIAccess,
45
0
                                                DOMEventTargetHelper)
46
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputMap)
47
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputMap)
48
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessPromise)
49
0
  tmp->Shutdown();
50
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
51
52
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIAccess)
53
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
54
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
55
56
NS_IMPL_ADDREF_INHERITED(MIDIAccess, DOMEventTargetHelper)
57
NS_IMPL_RELEASE_INHERITED(MIDIAccess, DOMEventTargetHelper)
58
59
MIDIAccess::MIDIAccess(nsPIDOMWindowInner* aWindow, bool aSysexEnabled,
60
                       Promise* aPromise) :
61
  DOMEventTargetHelper(aWindow),
62
  mInputMap(new MIDIInputMap(aWindow)),
63
  mOutputMap(new MIDIOutputMap(aWindow)),
64
  mSysexEnabled(aSysexEnabled),
65
  mAccessPromise(aPromise),
66
  mHasShutdown(false)
67
0
{
68
0
  MOZ_ASSERT(aWindow);
69
0
  MOZ_ASSERT(aPromise);
70
0
}
71
72
MIDIAccess::~MIDIAccess()
73
0
{
74
0
  Shutdown();
75
0
}
76
77
void
78
MIDIAccess::Shutdown()
79
0
{
80
0
  if (mHasShutdown) {
81
0
    return;
82
0
  }
83
0
  mDestructionObservers.Broadcast(void_t());
84
0
  if (MIDIAccessManager::IsRunning()) {
85
0
    MIDIAccessManager::Get()->RemoveObserver(this);
86
0
  }
87
0
  mHasShutdown = true;
88
0
}
89
90
void
91
MIDIAccess::FireConnectionEvent(MIDIPort* aPort)
92
0
{
93
0
  MOZ_ASSERT(aPort);
94
0
  MIDIConnectionEventInit init;
95
0
  init.mPort = aPort;
96
0
  nsAutoString id;
97
0
  aPort->GetId(id);
98
0
  ErrorResult rv;
99
0
  if (aPort->State() == MIDIPortDeviceState::Disconnected) {
100
0
    if (aPort->Type() == MIDIPortType::Input &&
101
0
        MIDIInputMap_Binding::MaplikeHelpers::Has(mInputMap, id, rv)) {
102
0
      MIDIInputMap_Binding::MaplikeHelpers::Delete(mInputMap, id, rv);
103
0
    } else if (aPort->Type() == MIDIPortType::Output &&
104
0
               MIDIOutputMap_Binding::MaplikeHelpers::Has(mOutputMap, id, rv)) {
105
0
      MIDIOutputMap_Binding::MaplikeHelpers::Delete(mOutputMap, id, rv);
106
0
    }
107
0
    // Check to make sure Has()/Delete() calls haven't failed.
108
0
    if(NS_WARN_IF(rv.Failed())) {
109
0
      return;
110
0
    }
111
0
  } else {
112
0
    // If we receive an event from a port that is not in one of our port maps,
113
0
    // this means a port that was disconnected has been reconnected, with the port
114
0
    // owner holding the object during that time, and we should add that port
115
0
    // object to our maps again.
116
0
    if (aPort->Type() == MIDIPortType::Input &&
117
0
        !MIDIInputMap_Binding::MaplikeHelpers::Has(mInputMap, id, rv)) {
118
0
      if(NS_WARN_IF(rv.Failed())) {
119
0
        return;
120
0
      }
121
0
      MIDIInputMap_Binding::MaplikeHelpers::Set(mInputMap,
122
0
                                               id,
123
0
                                               *(static_cast<MIDIInput*>(aPort)),
124
0
                                               rv);
125
0
      if(NS_WARN_IF(rv.Failed())) {
126
0
        return;
127
0
      }
128
0
    } else if (aPort->Type() == MIDIPortType::Output &&
129
0
               !MIDIOutputMap_Binding::MaplikeHelpers::Has(mOutputMap, id, rv)) {
130
0
      if(NS_WARN_IF(rv.Failed())) {
131
0
        return;
132
0
      }
133
0
      MIDIOutputMap_Binding::MaplikeHelpers::Set(mOutputMap,
134
0
                                                id,
135
0
                                                *(static_cast<MIDIOutput*>(aPort)),
136
0
                                                rv);
137
0
      if(NS_WARN_IF(rv.Failed())) {
138
0
        return;
139
0
      }
140
0
    }
141
0
  }
142
0
  RefPtr<MIDIConnectionEvent> event =
143
0
    MIDIConnectionEvent::Constructor(this, NS_LITERAL_STRING("statechange"), init);
144
0
  DispatchTrustedEvent(event);
145
0
}
146
147
void
148
MIDIAccess::MaybeCreateMIDIPort(const MIDIPortInfo& aInfo, ErrorResult& aRv)
149
0
{
150
0
  nsAutoString id(aInfo.id());
151
0
  MIDIPortType type = static_cast<MIDIPortType>(aInfo.type());
152
0
  RefPtr<MIDIPort> port;
153
0
  if (type == MIDIPortType::Input) {
154
0
    bool hasPort = MIDIInputMap_Binding::MaplikeHelpers::Has(mInputMap, id, aRv);
155
0
    if (hasPort || NS_WARN_IF(aRv.Failed())) {
156
0
      // We already have the port in our map.
157
0
      return;
158
0
    }
159
0
    port = MIDIInput::Create(GetOwner(), this, aInfo, mSysexEnabled);
160
0
    if (NS_WARN_IF(!port)) {
161
0
      aRv.Throw(NS_ERROR_FAILURE);
162
0
      return;
163
0
    }
164
0
    MIDIInputMap_Binding::MaplikeHelpers::Set(mInputMap,
165
0
                                             id,
166
0
                                             *(static_cast<MIDIInput*>(port.get())),
167
0
                                             aRv);
168
0
    if (NS_WARN_IF(aRv.Failed())) {
169
0
      return;
170
0
    }
171
0
  } else if (type == MIDIPortType::Output) {
172
0
    bool hasPort = MIDIOutputMap_Binding::MaplikeHelpers::Has(mOutputMap, id, aRv);
173
0
    if (hasPort || NS_WARN_IF(aRv.Failed())) {
174
0
      // We already have the port in our map.
175
0
      return;
176
0
    }
177
0
    port = MIDIOutput::Create(GetOwner(), this, aInfo, mSysexEnabled);
178
0
    if (NS_WARN_IF(!port)) {
179
0
      aRv.Throw(NS_ERROR_FAILURE);
180
0
      return;
181
0
    }
182
0
    MIDIOutputMap_Binding::MaplikeHelpers::Set(mOutputMap,
183
0
                                              id,
184
0
                                              *(static_cast<MIDIOutput*>(port.get())),
185
0
                                              aRv);
186
0
    if (NS_WARN_IF(aRv.Failed())) {
187
0
      return;
188
0
    }
189
0
  } else {
190
0
    // If we hit this, then we have some port that is neither input nor output.
191
0
    // That is bad.
192
0
    MOZ_CRASH("We shouldn't be here!");
193
0
  }
194
0
  // Set up port to listen for destruction of this access object.
195
0
  mDestructionObservers.AddObserver(port);
196
0
197
0
  // If we haven't resolved the promise for handing the MIDIAccess object to
198
0
  // content, this means we're still populating the list of already connected
199
0
  // devices. Don't fire events yet.
200
0
  if (!mAccessPromise) {
201
0
    FireConnectionEvent(port);
202
0
  }
203
0
}
204
205
// For the MIDIAccess object, only worry about new connections, where we create
206
// MIDIPort objects. When a port is removed and the MIDIPortRemove event is
207
// received, that will be handled by the MIDIPort object itself, and it will
208
// request removal from MIDIAccess's maps.
209
void
210
MIDIAccess::Notify(const MIDIPortList& aEvent)
211
0
{
212
0
  ErrorResult rv;
213
0
  for (auto& port : aEvent.ports()) {
214
0
    // Something went very wrong. Warn and return.
215
0
    MaybeCreateMIDIPort(port, rv);
216
0
    if (rv.Failed()) {
217
0
      if (!mAccessPromise) {
218
0
        return;
219
0
      }
220
0
      mAccessPromise->MaybeReject(rv);
221
0
      mAccessPromise = nullptr;
222
0
    }
223
0
  }
224
0
  if (!mAccessPromise) {
225
0
    return;
226
0
  }
227
0
  mAccessPromise->MaybeResolve(this);
228
0
  mAccessPromise = nullptr;
229
0
}
230
231
JSObject*
232
MIDIAccess::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
233
0
{
234
0
  return MIDIAccess_Binding::Wrap(aCx, this, aGivenProto);
235
0
}
236
237
void
238
MIDIAccess::RemovePortListener(MIDIAccessDestructionObserver* aObs)
239
0
{
240
0
  mDestructionObservers.RemoveObserver(aObs);
241
0
}
242
243
} // namespace dom
244
} // namespace mozilla