Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webspeech/synth/test/nsFakeSynthServices.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 "nsISupports.h"
8
#include "nsFakeSynthServices.h"
9
#include "nsPrintfCString.h"
10
#include "nsIWeakReferenceUtils.h"
11
#include "SharedBuffer.h"
12
#include "nsISimpleEnumerator.h"
13
14
#include "mozilla/dom/nsSynthVoiceRegistry.h"
15
#include "mozilla/dom/nsSpeechTask.h"
16
17
#include "nsThreadUtils.h"
18
#include "prenv.h"
19
#include "mozilla/Preferences.h"
20
#include "mozilla/DebugOnly.h"
21
22
#define CHANNELS 1
23
#define SAMPLERATE 1600
24
25
namespace mozilla {
26
namespace dom {
27
28
StaticRefPtr<nsFakeSynthServices> nsFakeSynthServices::sSingleton;
29
30
enum VoiceFlags
31
{
32
  eSuppressEvents = 1,
33
  eSuppressEnd = 2,
34
  eFailAtStart = 4,
35
  eFail = 8
36
};
37
38
struct VoiceDetails
39
{
40
  const char* uri;
41
  const char* name;
42
  const char* lang;
43
  bool defaultVoice;
44
  uint32_t flags;
45
};
46
47
static const VoiceDetails sVoices[] = {
48
  {"urn:moz-tts:fake:bob", "Bob Marley", "en-JM", true, 0},
49
  {"urn:moz-tts:fake:amy", "Amy Winehouse", "en-GB", false, 0},
50
  {"urn:moz-tts:fake:lenny", "Leonard Cohen", "en-CA", false, 0},
51
  {"urn:moz-tts:fake:celine", "Celine Dion", "fr-CA", false, 0},
52
  {"urn:moz-tts:fake:julie", "Julieta Venegas", "es-MX", false, },
53
  {"urn:moz-tts:fake:zanetta", "Zanetta Farussi", "it-IT", false, 0},
54
  {"urn:moz-tts:fake:margherita", "Margherita Durastanti", "it-IT-noevents-noend", false, eSuppressEvents | eSuppressEnd},
55
  {"urn:moz-tts:fake:teresa", "Teresa Cornelys", "it-IT-noend", false, eSuppressEnd},
56
  {"urn:moz-tts:fake:cecilia", "Cecilia Bartoli", "it-IT-failatstart", false, eFailAtStart},
57
  {"urn:moz-tts:fake:gottardo", "Gottardo Aldighieri", "it-IT-fail", false, eFail},
58
};
59
60
// FakeSynthCallback
61
class FakeSynthCallback : public nsISpeechTaskCallback
62
{
63
public:
64
0
  explicit FakeSynthCallback(nsISpeechTask* aTask) : mTask(aTask) { }
65
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
66
  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(FakeSynthCallback, nsISpeechTaskCallback)
67
68
  NS_IMETHOD OnPause() override
69
0
  {
70
0
    if (mTask) {
71
0
      mTask->DispatchPause(1.5, 1);
72
0
    }
73
0
74
0
    return NS_OK;
75
0
  }
76
77
  NS_IMETHOD OnResume() override
78
0
  {
79
0
    if (mTask) {
80
0
      mTask->DispatchResume(1.5, 1);
81
0
    }
82
0
83
0
    return NS_OK;
84
0
  }
85
86
  NS_IMETHOD OnCancel() override
87
0
  {
88
0
    if (mTask) {
89
0
      mTask->DispatchEnd(1.5, 1);
90
0
    }
91
0
92
0
    return NS_OK;
93
0
  }
94
95
  NS_IMETHOD OnVolumeChanged(float aVolume) override
96
0
  {
97
0
    return NS_OK;
98
0
  }
99
100
private:
101
0
  virtual ~FakeSynthCallback() = default;
102
103
  nsCOMPtr<nsISpeechTask> mTask;
104
};
105
106
NS_IMPL_CYCLE_COLLECTION(FakeSynthCallback, mTask);
107
108
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FakeSynthCallback)
109
0
  NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback)
110
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback)
111
0
NS_INTERFACE_MAP_END
112
113
NS_IMPL_CYCLE_COLLECTING_ADDREF(FakeSynthCallback)
114
NS_IMPL_CYCLE_COLLECTING_RELEASE(FakeSynthCallback)
115
116
// FakeSpeechSynth
117
118
class FakeSpeechSynth : public nsISpeechService
119
{
120
121
public:
122
0
  FakeSpeechSynth() = default;
123
124
  NS_DECL_ISUPPORTS
125
  NS_DECL_NSISPEECHSERVICE
126
127
private:
128
0
  virtual ~FakeSpeechSynth() = default;
129
};
130
131
NS_IMPL_ISUPPORTS(FakeSpeechSynth, nsISpeechService)
132
133
NS_IMETHODIMP
134
FakeSpeechSynth::Speak(const nsAString& aText, const nsAString& aUri,
135
                              float aVolume, float aRate, float aPitch,
136
                              nsISpeechTask* aTask)
137
0
{
138
0
  class DispatchStart final : public Runnable
139
0
  {
140
0
  public:
141
0
    explicit DispatchStart(nsISpeechTask* aTask)
142
0
      : mozilla::Runnable("DispatchStart")
143
0
      , mTask(aTask)
144
0
    {
145
0
    }
146
0
147
0
    NS_IMETHOD Run() override
148
0
    {
149
0
      mTask->DispatchStart();
150
0
151
0
      return NS_OK;
152
0
    }
153
0
154
0
  private:
155
0
    nsCOMPtr<nsISpeechTask> mTask;
156
0
  };
157
0
158
0
  class DispatchEnd final : public Runnable
159
0
  {
160
0
  public:
161
0
    DispatchEnd(nsISpeechTask* aTask, const nsAString& aText)
162
0
      : mozilla::Runnable("DispatchEnd")
163
0
      , mTask(aTask)
164
0
      , mText(aText)
165
0
    {
166
0
    }
167
0
168
0
    NS_IMETHOD Run() override
169
0
    {
170
0
      mTask->DispatchEnd(mText.Length()/2, mText.Length());
171
0
172
0
      return NS_OK;
173
0
    }
174
0
175
0
  private:
176
0
    nsCOMPtr<nsISpeechTask> mTask;
177
0
    nsString mText;
178
0
  };
179
0
180
0
  class DispatchError final : public Runnable
181
0
  {
182
0
  public:
183
0
    DispatchError(nsISpeechTask* aTask, const nsAString& aText)
184
0
      : mozilla::Runnable("DispatchError")
185
0
      , mTask(aTask)
186
0
      , mText(aText)
187
0
    {
188
0
    }
189
0
190
0
    NS_IMETHOD Run() override
191
0
    {
192
0
      mTask->DispatchError(mText.Length()/2, mText.Length());
193
0
194
0
      return NS_OK;
195
0
    }
196
0
197
0
  private:
198
0
    nsCOMPtr<nsISpeechTask> mTask;
199
0
    nsString mText;
200
0
  };
201
0
202
0
  uint32_t flags = 0;
203
0
  for (VoiceDetails voice : sVoices) {
204
0
    if (aUri.EqualsASCII(voice.uri)) {
205
0
      flags = voice.flags;
206
0
      break;
207
0
    }
208
0
  }
209
0
210
0
  if (flags & eFailAtStart) {
211
0
    return NS_ERROR_FAILURE;
212
0
  }
213
0
214
0
  RefPtr<FakeSynthCallback> cb = new FakeSynthCallback(
215
0
    (flags & eSuppressEvents) ? nullptr : aTask);
216
0
217
0
  aTask->Setup(cb);
218
0
219
0
  nsCOMPtr<nsIRunnable> runnable = new DispatchStart(aTask);
220
0
  NS_DispatchToMainThread(runnable);
221
0
222
0
  if (flags & eFail) {
223
0
    runnable = new DispatchError(aTask, aText);
224
0
    NS_DispatchToMainThread(runnable);
225
0
  } else if ((flags & eSuppressEnd) == 0) {
226
0
    runnable = new DispatchEnd(aTask, aText);
227
0
    NS_DispatchToMainThread(runnable);
228
0
  }
229
0
230
0
  return NS_OK;
231
0
}
232
233
// nsFakeSynthService
234
235
0
NS_INTERFACE_MAP_BEGIN(nsFakeSynthServices)
236
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
237
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
238
0
NS_INTERFACE_MAP_END
239
240
NS_IMPL_ADDREF(nsFakeSynthServices)
241
NS_IMPL_RELEASE(nsFakeSynthServices)
242
243
static void
244
AddVoices(nsISpeechService* aService, const VoiceDetails* aVoices, uint32_t aLength)
245
0
{
246
0
  RefPtr<nsSynthVoiceRegistry> registry = nsSynthVoiceRegistry::GetInstance();
247
0
  for (uint32_t i = 0; i < aLength; i++) {
248
0
    NS_ConvertUTF8toUTF16 name(aVoices[i].name);
249
0
    NS_ConvertUTF8toUTF16 uri(aVoices[i].uri);
250
0
    NS_ConvertUTF8toUTF16 lang(aVoices[i].lang);
251
0
    // These services can handle more than one utterance at a time and have
252
0
    // several speaking simultaniously. So, aQueuesUtterances == false
253
0
    registry->AddVoice(aService, uri, name, lang, true, false);
254
0
    if (aVoices[i].defaultVoice) {
255
0
      registry->SetDefaultVoice(uri, true);
256
0
    }
257
0
  }
258
0
259
0
  registry->NotifyVoicesChanged();
260
0
}
261
262
void
263
nsFakeSynthServices::Init()
264
0
{
265
0
  mSynthService = new FakeSpeechSynth();
266
0
  AddVoices(mSynthService, sVoices, ArrayLength(sVoices));
267
0
}
268
269
// nsIObserver
270
271
NS_IMETHODIMP
272
nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic,
273
                             const char16_t* aData)
274
0
{
275
0
  MOZ_ASSERT(NS_IsMainThread());
276
0
  if(NS_WARN_IF(!(!strcmp(aTopic, "speech-synth-started")))) {
277
0
    return NS_ERROR_UNEXPECTED;
278
0
  }
279
0
280
0
  if (Preferences::GetBool("media.webspeech.synth.test")) {
281
0
    NS_DispatchToMainThread(NewRunnableMethod(
282
0
      "dom::nsFakeSynthServices::Init", this, &nsFakeSynthServices::Init));
283
0
  }
284
0
285
0
  return NS_OK;
286
0
}
287
288
// static methods
289
290
nsFakeSynthServices*
291
nsFakeSynthServices::GetInstance()
292
0
{
293
0
  MOZ_ASSERT(NS_IsMainThread());
294
0
  if (!XRE_IsParentProcess()) {
295
0
    MOZ_ASSERT(false, "nsFakeSynthServices can only be started on main gecko process");
296
0
    return nullptr;
297
0
  }
298
0
299
0
  if (!sSingleton) {
300
0
    sSingleton = new nsFakeSynthServices();
301
0
  }
302
0
303
0
  return sSingleton;
304
0
}
305
306
already_AddRefed<nsFakeSynthServices>
307
nsFakeSynthServices::GetInstanceForService()
308
0
{
309
0
  RefPtr<nsFakeSynthServices> picoService = GetInstance();
310
0
  return picoService.forget();
311
0
}
312
313
void
314
nsFakeSynthServices::Shutdown()
315
0
{
316
0
  if (!sSingleton) {
317
0
    return;
318
0
  }
319
0
320
0
  sSingleton = nullptr;
321
0
}
322
323
} // namespace dom
324
} // namespace mozilla