mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1162699 - Replace mochitest test synth services with global services to simplify tests. r=smaug
Also, remove ipc tests since we now can enable these tests in e10s. Also, make utterances very long in cancel test so that we actually interrupt them.
This commit is contained in:
parent
06998bb825
commit
72e93f95e2
@ -1,196 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for OOP TTS</title>
|
||||
<script type="application/javascript" src="../../test/common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
window.SimpleTest = parent.SimpleTest;
|
||||
window.ok = parent.ok;
|
||||
window.is = parent.is;
|
||||
window.info = parent.info;
|
||||
|
||||
// The crash observer registration functions are stubbed out here to
|
||||
// prevent the iframe test runner from breaking later crash-related tests.
|
||||
function iframeScriptFirst() {
|
||||
SpecialPowers.prototype.registerProcessCrashObservers = () => {};
|
||||
SpecialPowers.prototype.unregisterProcessCrashObservers = () => {};
|
||||
|
||||
content.wrappedJSObject.RunSet.reloadAndRunAll({
|
||||
preventDefault: function() { },
|
||||
__exposedProps__: { preventDefault: 'r' }
|
||||
});
|
||||
}
|
||||
|
||||
function iframeScriptSecond() {
|
||||
let TestRunner = content.wrappedJSObject.TestRunner;
|
||||
|
||||
let oldComplete = TestRunner.onComplete;
|
||||
|
||||
TestRunner.onComplete = function() {
|
||||
TestRunner.onComplete = oldComplete;
|
||||
|
||||
sendAsyncMessage("test:SpeechSynthesis:ipcTestComplete", {
|
||||
result: JSON.stringify(TestRunner._failedTests)
|
||||
});
|
||||
|
||||
if (oldComplete) {
|
||||
oldComplete();
|
||||
}
|
||||
};
|
||||
TestRunner.structuredLogger._dumpMessage = function(msg) {
|
||||
sendAsyncMessage("test:SpeechSynthesis:ipcTestMessage", { msg: msg });
|
||||
}
|
||||
}
|
||||
|
||||
let VALID_ACTIONS = ['suite_start', 'suite_end', 'test_start', 'test_end', 'test_status', 'process_output', 'log'];
|
||||
function validStructuredMessage(message) {
|
||||
return message.action !== undefined && VALID_ACTIONS.indexOf(message.action) >= 0;
|
||||
}
|
||||
function onTestMessage(data) {
|
||||
let message = SpecialPowers.wrap(data).data.msg;
|
||||
|
||||
if (validStructuredMessage(message)) {
|
||||
switch (message.action) {
|
||||
case "test_status":
|
||||
case "test_end":
|
||||
let test_tokens = message.test.split("/");
|
||||
let test_name = test_tokens[test_tokens.length - 1];
|
||||
if (message.subtest) {
|
||||
test_name += " | " + message.subtest;
|
||||
}
|
||||
ok(message.expected === undefined, test_name, message.message);
|
||||
break;
|
||||
case "log":
|
||||
info(message.message);
|
||||
break;
|
||||
default:
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onTestComplete() {
|
||||
let comp = SpecialPowers.wrap(SpecialPowers.Components);
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
let spObserver = comp.classes["@mozilla.org/special-powers-observer;1"]
|
||||
.getService(comp.interfaces.nsIMessageListener);
|
||||
|
||||
mm.removeMessageListener("SPPrefService", spObserver);
|
||||
mm.removeMessageListener("SPProcessCrashService", spObserver);
|
||||
mm.removeMessageListener("SPPingService", spObserver);
|
||||
mm.removeMessageListener("SpecialPowers.Quit", spObserver);
|
||||
mm.removeMessageListener("SPPermissionManager", spObserver);
|
||||
|
||||
mm.removeMessageListener("test:SpeechSynthesis:ipcTestMessage", onTestMessage);
|
||||
mm.removeMessageListener("test:SpeechSynthesis:ipcTestComplete", onTestComplete);
|
||||
|
||||
let ppmm = SpecialPowers.Cc["@mozilla.org/parentprocessmessagemanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIMessageBroadcaster);
|
||||
ppmm.removeMessageListener("test:SpeechSynthesis:ipcSynthAddVoice", onSynthAddVoice);
|
||||
ppmm.removeMessageListener("test:SpeechSynthesis:ipcSynthSetDefault", onSynthSetDefault);
|
||||
ppmm.removeMessageListener("test:SpeechSynthesis:ipcSynthCleanup", onSynthCleanup);
|
||||
|
||||
SimpleTest.executeSoon(function () { SimpleTest.finish(); });
|
||||
}
|
||||
|
||||
function onSynthAddVoice(data) {
|
||||
let message = SpecialPowers.wrap(data).json;
|
||||
return synthAddVoice.apply(synthAddVoice, message);
|
||||
}
|
||||
|
||||
function onSynthSetDefault(data) {
|
||||
let message = SpecialPowers.wrap(data).json;
|
||||
synthSetDefault.apply(synthSetDefault, message);
|
||||
}
|
||||
|
||||
function onSynthCleanup(data) {
|
||||
synthCleanup();
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
let iframe = document.createElement("iframe");
|
||||
SpecialPowers.wrap(iframe).mozbrowser = true;
|
||||
iframe.id = "iframe";
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "1000px";
|
||||
|
||||
function iframeLoadSecond() {
|
||||
ok(true, "Got second iframe load event.");
|
||||
iframe.removeEventListener("mozbrowserloadend", iframeLoadSecond);
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
mm.loadFrameScript("data:,(" + iframeScriptSecond.toString() + ")();",
|
||||
false);
|
||||
}
|
||||
|
||||
function iframeLoadFirst() {
|
||||
ok(true, "Got first iframe load event.");
|
||||
iframe.removeEventListener("mozbrowserloadend", iframeLoadFirst);
|
||||
iframe.addEventListener("mozbrowserloadend", iframeLoadSecond);
|
||||
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
|
||||
|
||||
let comp = SpecialPowers.wrap(SpecialPowers.Components);
|
||||
|
||||
let spObserver =
|
||||
comp.classes["@mozilla.org/special-powers-observer;1"]
|
||||
.getService(comp.interfaces.nsIMessageListener);
|
||||
|
||||
mm.addMessageListener("SPPrefService", spObserver);
|
||||
mm.addMessageListener("SPProcessCrashService", spObserver);
|
||||
mm.addMessageListener("SPPingService", spObserver);
|
||||
mm.addMessageListener("SpecialPowers.Quit", spObserver);
|
||||
mm.addMessageListener("SPPermissionManager", spObserver);
|
||||
|
||||
mm.addMessageListener("test:SpeechSynthesis:ipcTestMessage", onTestMessage);
|
||||
mm.addMessageListener("test:SpeechSynthesis:ipcTestComplete", onTestComplete);
|
||||
|
||||
let specialPowersBase = "chrome://specialpowers/content/";
|
||||
mm.loadFrameScript(specialPowersBase + "MozillaLogger.js", false);
|
||||
mm.loadFrameScript(specialPowersBase + "specialpowersAPI.js", false);
|
||||
mm.loadFrameScript(specialPowersBase + "specialpowers.js", false);
|
||||
|
||||
mm.loadFrameScript("data:,(" + iframeScriptFirst.toString() + ")();", false);
|
||||
|
||||
let ppmm = SpecialPowers.Cc["@mozilla.org/parentprocessmessagemanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIMessageBroadcaster);
|
||||
ppmm.addMessageListener("test:SpeechSynthesis:ipcSynthAddVoice", onSynthAddVoice);
|
||||
ppmm.addMessageListener("test:SpeechSynthesis:ipcSynthSetDefault", onSynthSetDefault);
|
||||
ppmm.addMessageListener("test:SpeechSynthesis:ipcSynthCleanup", onSynthCleanup);
|
||||
}
|
||||
|
||||
iframe.addEventListener("mozbrowserloadend", iframeLoadFirst);
|
||||
|
||||
// Strip this filename and one directory level and then add "/test".
|
||||
let href = window.location.href;
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
href = href.substring(0, href.lastIndexOf('/'));
|
||||
iframe.src = href + "/test?consoleLevel=INFO";
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
addEventListener("load", function() {
|
||||
|
||||
SpecialPowers.addPermission("browser", true, document);
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
// TODO: remove this as part of bug 820712
|
||||
["network.disable.ipc.security", true],
|
||||
|
||||
["dom.ipc.browser_frames.oop_by_default", true],
|
||||
["dom.mozBrowserFramesEnabled", true],
|
||||
["browser.pagethumbnails.capturing_disabled", true]
|
||||
]
|
||||
}, runTests);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s
|
||||
support-files =
|
||||
file_ipc.html
|
||||
|
||||
[test_ipc.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 857673 # b2g(comp.classes['@mozilla.org/special-powers-observer;1'] is undefined) b2g-debug(comp.classes['@mozilla.org/special-powers-observer;1'] is undefined) b2g-desktop(comp.classes['@mozilla.org/special-powers-observer;1'] is undefined)
|
@ -1,17 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for OOP TTS</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="testFrame"></iframe>
|
||||
<script type="application/javascript;version=1.7">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
|
||||
function() { document.getElementById("testFrame").src = "file_ipc.html"; });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -5,7 +5,6 @@
|
||||
|
||||
if CONFIG['MOZ_WEBSPEECH']:
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'ipc/test/mochitest.ini',
|
||||
'test/mochitest.ini',
|
||||
]
|
||||
|
||||
@ -34,6 +33,8 @@ if CONFIG['MOZ_WEBSPEECH']:
|
||||
'SpeechSynthesis.cpp',
|
||||
'SpeechSynthesisUtterance.cpp',
|
||||
'SpeechSynthesisVoice.cpp',
|
||||
'test/FakeSynthModule.cpp',
|
||||
'test/nsFakeSynthServices.cpp'
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_SYNTH_PICO']:
|
||||
|
@ -84,7 +84,7 @@ private:
|
||||
|
||||
// nsSpeechTask
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(nsSpeechTask, mSpeechSynthesis, mUtterance);
|
||||
NS_IMPL_CYCLE_COLLECTION(nsSpeechTask, mSpeechSynthesis, mUtterance, mCallback);
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSpeechTask)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISpeechTask)
|
||||
|
55
dom/media/webspeech/synth/test/FakeSynthModule.cpp
Normal file
55
dom/media/webspeech/synth/test/FakeSynthModule.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
|
||||
#include "nsFakeSynthServices.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
#define FAKESYNTHSERVICE_CID \
|
||||
{0xe7d52d9e, 0xc148, 0x47d8, {0xab, 0x2a, 0x95, 0xd7, 0xf4, 0x0e, 0xa5, 0x3d}}
|
||||
|
||||
#define FAKESYNTHSERVICE_CONTRACTID "@mozilla.org/fakesynth;1"
|
||||
|
||||
// Defines nsFakeSynthServicesConstructor
|
||||
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsFakeSynthServices,
|
||||
nsFakeSynthServices::GetInstanceForService)
|
||||
|
||||
// Defines kFAKESYNTHSERVICE_CID
|
||||
NS_DEFINE_NAMED_CID(FAKESYNTHSERVICE_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kCIDs[] = {
|
||||
{ &kFAKESYNTHSERVICE_CID, true, nullptr, nsFakeSynthServicesConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kContracts[] = {
|
||||
{ FAKESYNTHSERVICE_CONTRACTID, &kFAKESYNTHSERVICE_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::CategoryEntry kCategories[] = {
|
||||
{ "profile-after-change", "Fake Speech Synth", FAKESYNTHSERVICE_CONTRACTID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static void
|
||||
UnloadFakeSynthmodule()
|
||||
{
|
||||
nsFakeSynthServices::Shutdown();
|
||||
}
|
||||
|
||||
static const mozilla::Module kModule = {
|
||||
mozilla::Module::kVersion,
|
||||
kCIDs,
|
||||
kContracts,
|
||||
kCategories,
|
||||
nullptr,
|
||||
nullptr,
|
||||
UnloadFakeSynthmodule
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(fakesynth) = &kModule;
|
@ -1,199 +1,3 @@
|
||||
var gSpeechRegistry = SpecialPowers.Cc["@mozilla.org/synth-voice-registry;1"]
|
||||
.getService(SpecialPowers.Ci.nsISynthVoiceRegistry);
|
||||
|
||||
var gAddedVoices = [];
|
||||
|
||||
function SpeechTaskCallback(onpause, onresume, oncancel) {
|
||||
this.onpause = onpause;
|
||||
this.onresume = onresume;
|
||||
this.oncancel = oncancel;
|
||||
}
|
||||
|
||||
SpeechTaskCallback.prototype = {
|
||||
QueryInterface: function(iid) {
|
||||
return this;
|
||||
},
|
||||
|
||||
getInterfaces: function(c) {},
|
||||
|
||||
getScriptableHelper: function() {},
|
||||
|
||||
onPause: function onPause() {
|
||||
if (this.onpause)
|
||||
this.onpause();
|
||||
},
|
||||
|
||||
onResume: function onResume() {
|
||||
if (this.onresume)
|
||||
this.onresume();
|
||||
},
|
||||
|
||||
onCancel: function onCancel() {
|
||||
if (this.oncancel)
|
||||
this.oncancel();
|
||||
}
|
||||
};
|
||||
|
||||
var TestSpeechServiceWithAudio = SpecialPowers.wrapCallbackObject({
|
||||
CHANNELS: 1,
|
||||
SAMPLE_RATE: 16000,
|
||||
|
||||
serviceType: SpecialPowers.Ci.nsISpeechService.SERVICETYPE_DIRECT_AUDIO,
|
||||
|
||||
speak: function speak(aText, aUri, aVolume, aRate, aPitch, aTask) {
|
||||
var task = SpecialPowers.wrap(aTask);
|
||||
|
||||
window.setTimeout(
|
||||
function () {
|
||||
task.setup(SpecialPowers.wrapCallbackObject(new SpeechTaskCallback()), this.CHANNELS, this.SAMPLE_RATE);
|
||||
// 0.025 seconds per character.
|
||||
task.sendAudio(new Int16Array((this.SAMPLE_RATE/40)*aText.length), []);
|
||||
task.sendAudio(new Int16Array(0), []);
|
||||
}.bind(this), 0);
|
||||
},
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
return this;
|
||||
},
|
||||
|
||||
getInterfaces: function(c) {},
|
||||
|
||||
getScriptableHelper: function() {}
|
||||
});
|
||||
|
||||
var TestSpeechServiceNoAudio = SpecialPowers.wrapCallbackObject({
|
||||
serviceType: SpecialPowers.Ci.nsISpeechService.SERVICETYPE_INDIRECT_AUDIO,
|
||||
|
||||
speak: function speak(aText, aUri, aVolume, aRate, aPitch, aTask) {
|
||||
var pair = this.expectedSpeaks.shift();
|
||||
if (pair) {
|
||||
// XXX: These tests do not happen in OOP
|
||||
var utterance = pair[0];
|
||||
var expected = pair[1];
|
||||
|
||||
is(aText, utterance.text, "Speak text matches utterance text");
|
||||
|
||||
var args = {uri: aUri, rate: aRate, pitch: aPitch};
|
||||
|
||||
for (var attr in args) {
|
||||
if (expected[attr] != undefined)
|
||||
is(args[attr], expected[attr], "expected service arg " + attr);
|
||||
}
|
||||
}
|
||||
|
||||
// If the utterance contains the phrase 'callback events', we will dispatch
|
||||
// an appropriate event for each callback method.
|
||||
var no_events = (aText.indexOf('callback events') < 0);
|
||||
// If the utterance contains the phrase 'never end', we don't immediately
|
||||
// end the 'synthesis' of the utterance.
|
||||
var end_utterance = (aText.indexOf('never end') < 0);
|
||||
|
||||
var task = SpecialPowers.wrap(aTask);
|
||||
task.setup(SpecialPowers.wrapCallbackObject(new SpeechTaskCallback(
|
||||
function() {
|
||||
if (!no_events) {
|
||||
task.dispatchPause(1, 1.23);
|
||||
}
|
||||
},
|
||||
function() {
|
||||
if (!no_events) {
|
||||
task.dispatchResume(1, 1.23);
|
||||
}
|
||||
},
|
||||
function() {
|
||||
if (!no_events) {
|
||||
task.dispatchEnd(1, 1.23);
|
||||
}
|
||||
})));
|
||||
setTimeout(function () {
|
||||
task.dispatchStart();
|
||||
if (end_utterance) {
|
||||
setTimeout(function () {
|
||||
task.dispatchEnd(
|
||||
aText.length / 2.0, aText.length);
|
||||
}, 0);
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
return this;
|
||||
},
|
||||
|
||||
getInterfaces: function(c) {},
|
||||
|
||||
getScriptableHelper: function() {},
|
||||
|
||||
expectedSpeaks: []
|
||||
});
|
||||
|
||||
function synthAddVoice(aServiceName, aName, aLang, aIsLocal) {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
var voicesBefore = speechSynthesis.getVoices().length;
|
||||
var uri = "urn:moz-tts:mylittleservice:" + encodeURI(aName + '?' + aLang);
|
||||
gSpeechRegistry.addVoice(window[aServiceName], uri, aName, aLang, aIsLocal);
|
||||
|
||||
gAddedVoices.push([window[aServiceName], uri]);
|
||||
var voicesAfter = speechSynthesis.getVoices().length;
|
||||
|
||||
is(voicesBefore + 1, voicesAfter, "Voice added");
|
||||
var voice = speechSynthesis.getVoices()[voicesAfter - 1];
|
||||
is(voice.voiceURI, uri, "voice URI matches");
|
||||
is(voice.name, aName, "voice name matches");
|
||||
is(voice.lang, aLang, "voice lang matches");
|
||||
is(voice.localService, aIsLocal, "voice localService matches");
|
||||
|
||||
return uri;
|
||||
} else {
|
||||
// XXX: It would be nice to check here that the child gets the voice
|
||||
// added update, but alas, it is aynchronous.
|
||||
var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsISyncMessageSender);
|
||||
|
||||
return mm.sendSyncMessage(
|
||||
'test:SpeechSynthesis:ipcSynthAddVoice',
|
||||
[aServiceName, aName, aLang, aIsLocal])[0];
|
||||
}
|
||||
}
|
||||
|
||||
function synthSetDefault(aUri, aIsDefault) {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
gSpeechRegistry.setDefaultVoice(aUri, aIsDefault);
|
||||
var voices = speechSynthesis.getVoices();
|
||||
for (var i in voices) {
|
||||
if (voices[i].voiceURI == aUri)
|
||||
ok(voices[i]['default'], "Voice set to default");
|
||||
}
|
||||
} else {
|
||||
// XXX: It would be nice to check here that the child gets the voice
|
||||
// added update, but alas, it is aynchronous.
|
||||
var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsISyncMessageSender);
|
||||
|
||||
return mm.sendSyncMessage(
|
||||
'test:SpeechSynthesis:ipcSynthSetDefault', [aUri, aIsDefault])[0];
|
||||
}
|
||||
}
|
||||
|
||||
function synthCleanup() {
|
||||
if (SpecialPowers.isMainProcess()) {
|
||||
var voicesBefore = speechSynthesis.getVoices().length;
|
||||
var toRemove = gAddedVoices.length;
|
||||
var removeArgs;
|
||||
while ((removeArgs = gAddedVoices.shift()))
|
||||
gSpeechRegistry.removeVoice.apply(gSpeechRegistry.removeVoice, removeArgs);
|
||||
|
||||
var voicesAfter = speechSynthesis.getVoices().length;
|
||||
is(voicesAfter, voicesBefore - toRemove, "Successfully removed test voices");
|
||||
} else {
|
||||
// XXX: It would be nice to check here that the child gets the voice
|
||||
// removed update, but alas, it is aynchronous.
|
||||
var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsISyncMessageSender);
|
||||
mm.sendSyncMessage('test:SpeechSynthesis:ipcSynthCleanup');
|
||||
}
|
||||
}
|
||||
|
||||
function synthTestQueue(aTestArgs, aEndFunc) {
|
||||
var utterances = [];
|
||||
for (var i in aTestArgs) {
|
||||
@ -220,6 +24,16 @@ function synthTestQueue(aTestArgs, aEndFunc) {
|
||||
}
|
||||
}
|
||||
|
||||
u.addEventListener('start',
|
||||
(function (expectedUri) {
|
||||
return function (e) {
|
||||
if (expectedUri) {
|
||||
var chosenVoice = SpecialPowers.wrap(e).target.chosenVoiceURI;
|
||||
is(chosenVoice, expectedUri, "Incorrect URI is used");
|
||||
}
|
||||
};
|
||||
})(aTestArgs[i][1] ? aTestArgs[i][1].uri : null));
|
||||
|
||||
u.addEventListener('end', onend_handler);
|
||||
u.addEventListener('error', onend_handler);
|
||||
|
||||
@ -229,7 +43,6 @@ function synthTestQueue(aTestArgs, aEndFunc) {
|
||||
});
|
||||
|
||||
utterances.push(u);
|
||||
TestSpeechServiceNoAudio.expectedSpeaks.push([u, aTestArgs[i][1]]);
|
||||
speechSynthesis.speak(u);
|
||||
}
|
||||
|
||||
|
@ -26,11 +26,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1155034
|
||||
|
||||
/** Test for Bug 1155034 **/
|
||||
|
||||
synthAddVoice('TestSpeechServiceNoAudio', 'Female 1', 'en-GB', true);
|
||||
|
||||
function test_with_events() {
|
||||
info('test_with_events');
|
||||
var utterance = new SpeechSynthesisUtterance("never end, callback events");
|
||||
utterance.lang = 'it-IT-noend';
|
||||
|
||||
utterance.addEventListener('start', function(e) {
|
||||
speechSynthesis.pause();
|
||||
@ -38,20 +37,20 @@ function test_with_events() {
|
||||
});
|
||||
|
||||
utterance.addEventListener('pause', function(e) {
|
||||
ok(e.charIndex, 1, 'pause event charIndex matches service arguments');
|
||||
ok(e.elapsedTime, 1.23, 'pause event elapsedTime matches service arguments');
|
||||
is(e.charIndex, 1, 'pause event charIndex matches service arguments');
|
||||
is(e.elapsedTime, 1.5, 'pause event elapsedTime matches service arguments');
|
||||
speechSynthesis.resume();
|
||||
});
|
||||
|
||||
utterance.addEventListener('resume', function(e) {
|
||||
ok(e.charIndex, 1, 'resume event charIndex matches service arguments');
|
||||
ok(e.elapsedTime, 1.23, 'resume event elapsedTime matches service arguments');
|
||||
is(e.charIndex, 1, 'resume event charIndex matches service arguments');
|
||||
is(e.elapsedTime, 1.5, 'resume event elapsedTime matches service arguments');
|
||||
speechSynthesis.cancel();
|
||||
});
|
||||
|
||||
utterance.addEventListener('end', function(e) {
|
||||
ok(e.charIndex, 1, 'resume event charIndex matches service arguments');
|
||||
ok(e.elapsedTime, 1.23, 'end event elapsedTime matches service arguments');
|
||||
ok(e.elapsedTime, 1.5, 'end event elapsedTime matches service arguments');
|
||||
test_no_events();
|
||||
});
|
||||
|
||||
@ -60,12 +59,11 @@ function test_with_events() {
|
||||
|
||||
function test_no_events() {
|
||||
var utterance = new SpeechSynthesisUtterance("never end");
|
||||
|
||||
utterance.lang = "it-IT-noevents-noend";
|
||||
utterance.addEventListener('start', function(e) {
|
||||
speechSynthesis.pause();
|
||||
// Wait to see if we get some bad events we didn't expect.
|
||||
setTimeout(function() {
|
||||
synthCleanup();
|
||||
SimpleTest.finish();
|
||||
}, 1000);
|
||||
});
|
||||
|
@ -25,12 +25,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=525444
|
||||
|
||||
/** Test for Bug 525444 **/
|
||||
|
||||
synthAddVoice('TestSpeechServiceNoAudio', 'Bob Marley', 'en-JM', true);
|
||||
synthAddVoice('TestSpeechServiceNoAudio', 'Amy Winehouse', 'en-GB', true);
|
||||
synthAddVoice('TestSpeechServiceNoAudio', 'Leonard Cohen', 'en-CA', true);
|
||||
synthAddVoice('TestSpeechServiceNoAudio', 'Celine Dion', 'fr-CA', true);
|
||||
synthAddVoice('TestSpeechServiceNoAudio', 'Julieta Venegas', 'es-MX', true);
|
||||
|
||||
ok(SpeechSynthesis, "SpeechSynthesis exists in global scope");
|
||||
ok(SpeechSynthesisVoice, "SpeechSynthesisVoice exists in global scope");
|
||||
ok(SpeechSynthesisEvent, "SpeechSynthesisEvent exists in global scope");
|
||||
@ -75,8 +69,6 @@ for (var i in voices1) {
|
||||
ok(voices1[i] == voices2[i], "Voice instance matches");
|
||||
}
|
||||
|
||||
synthCleanup();
|
||||
|
||||
SimpleTest.finish();
|
||||
</script>
|
||||
</pre>
|
||||
|
@ -26,16 +26,35 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1150315
|
||||
|
||||
/** Test for Bug 1150315 **/
|
||||
|
||||
synthAddVoice('TestSpeechServiceWithAudio', 'Male 1', 'en-GB', true);
|
||||
|
||||
var gotEndEvent = false;
|
||||
var utterance = new SpeechSynthesisUtterance("Hello, world!");
|
||||
// A long utterance that we will interrupt.
|
||||
var utterance = new SpeechSynthesisUtterance("Donec ac nunc feugiat, posuere " +
|
||||
"mauris id, pharetra velit. Donec fermentum orci nunc, sit amet maximus" +
|
||||
"dui tincidunt ut. Sed ultricies ac nisi a laoreet. Proin interdum," +
|
||||
"libero maximus hendrerit posuere, lorem risus egestas nisl, a" +
|
||||
"ultricies massa justo eu nisi. Duis mattis nibh a ligula tincidunt" +
|
||||
"tincidunt non eu erat. Sed bibendum varius vulputate. Cras leo magna," +
|
||||
"ornare ac posuere vel, luctus id metus. Mauris nec quam ac augue" +
|
||||
"consectetur bibendum. Integer a commodo tortor. Duis semper dolor eu" +
|
||||
"facilisis facilisis. Etiam venenatis turpis est, quis tincidunt velit" +
|
||||
"suscipit a. Cras semper orci in sapien rhoncus bibendum. Suspendisse" +
|
||||
"eu ex lobortis, finibus enim in, condimentum quam. Maecenas eget dui" +
|
||||
"ipsum. Aliquam tortor leo, interdum eget congue ut, tempor id elit.");
|
||||
utterance.addEventListener('start', function(e) {
|
||||
ok(true, 'start utterance 1');
|
||||
speechSynthesis.cancel();
|
||||
speechSynthesis.speak(utterance2);
|
||||
});
|
||||
|
||||
var utterance2 = new SpeechSynthesisUtterance("Hello, world 2!");
|
||||
var utterance2 = new SpeechSynthesisUtterance("Proin ornare neque vitae " +
|
||||
"risus mattis rutrum. Suspendisse a velit ut est convallis aliquet." +
|
||||
"Nullam ante elit, malesuada vel luctus rutrum, ultricies nec libero." +
|
||||
"Praesent eu iaculis orci. Sed nisl diam, sodales ac purus et," +
|
||||
"volutpat interdum tortor. Nullam aliquam porta elit et maximus. Cras" +
|
||||
"risus lectus, elementum vel sodales vel, ultricies eget lectus." +
|
||||
"Curabitur velit lacus, mollis vel finibus et, molestie sit amet" +
|
||||
"sapien. Proin vitae dolor ac augue posuere efficitur ac scelerisque" +
|
||||
"diam. Nulla sed odio elit.");
|
||||
utterance2.addEventListener('start', function() {
|
||||
speechSynthesis.cancel();
|
||||
speechSynthesis.speak(utterance3);
|
||||
@ -46,10 +65,9 @@ utterance2.addEventListener('end', function(e) {
|
||||
|
||||
var utterance3 = new SpeechSynthesisUtterance("Hello, world 3!");
|
||||
utterance3.addEventListener('start', function() {
|
||||
ok(gotEndEvent, "didn't get start event for this utterance")
|
||||
ok(gotEndEvent, "didn't get start event for this utterance");
|
||||
});
|
||||
utterance3.addEventListener('end', function(e) {
|
||||
synthCleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
@ -18,48 +18,54 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=525444
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 525444 **/
|
||||
|
||||
var englishJamaican = synthAddVoice('TestSpeechServiceNoAudio',
|
||||
'Bob Marley', 'en-JM', true);
|
||||
var englishBritish = synthAddVoice('TestSpeechServiceNoAudio',
|
||||
'Amy Winehouse', 'en-GB', true);
|
||||
var englishCanadian = synthAddVoice('TestSpeechServiceNoAudio',
|
||||
'Leonard Cohen', 'en-CA', true);
|
||||
var frenchCanadian = synthAddVoice('TestSpeechServiceNoAudio',
|
||||
'Celine Dion', 'fr-CA', true);
|
||||
var spanishMexican = synthAddVoice('TestSpeechServiceNoAudio',
|
||||
'Julieta Venegas', 'es-MX', true);
|
||||
// XXX: Rate and pitch are not tested.
|
||||
|
||||
synthSetDefault(englishBritish, true);
|
||||
var langUriMap = {};
|
||||
|
||||
for (var voice of speechSynthesis.getVoices()) {
|
||||
if (voice.voiceURI.indexOf('urn:moz-tts:fake-direct') < 0) {
|
||||
continue;
|
||||
}
|
||||
langUriMap[voice.lang] = voice.voiceURI;
|
||||
ok(true, voice.lang + ' ' + voice.voiceURI + ' ' + voice.default);
|
||||
is(voice.default, voice.lang == 'en-JM', 'Only Jamaican voice should be default');
|
||||
}
|
||||
|
||||
ok(langUriMap['en-JM'], 'No English-Jamaican voice');
|
||||
ok(langUriMap['en-GB'], 'No English-British voice');
|
||||
ok(langUriMap['en-CA'], 'No English-Canadian voice');
|
||||
ok(langUriMap['fr-CA'], 'No French-Canadian voice');
|
||||
ok(langUriMap['es-MX'], 'No Spanish-Mexican voice');
|
||||
|
||||
synthTestQueue(
|
||||
[[{text: "Hello, world."},
|
||||
{ uri: englishBritish }],
|
||||
{ uri: langUriMap['en-JM'] }],
|
||||
[{text: "Bonjour tout le monde .", lang: "fr", rate: 0.5, pitch: 0.75},
|
||||
{ uri: frenchCanadian, rate: 0.5, pitch: 0.75}],
|
||||
{ uri: langUriMap['fr-CA'], rate: 0.5, pitch: 0.75}],
|
||||
[{text: "How are you doing?", lang: "en-GB"},
|
||||
{ rate: 1, pitch: 1, uri: englishBritish}],
|
||||
[{text: "¡hasta mañana", lang: "es-ES"},
|
||||
{ uri: spanishMexican }]],
|
||||
{ rate: 1, pitch: 1, uri: langUriMap['en-GB']}],
|
||||
[{text: "¡hasta mañana!", lang: "es-MX"},
|
||||
{ uri: langUriMap['es-MX'] }]],
|
||||
function () {
|
||||
synthSetDefault(englishJamaican, true);
|
||||
var test_data = [[{text: "I shot the sheriff."},
|
||||
{ uri: englishJamaican }]];
|
||||
var test_data = [];
|
||||
var voices = speechSynthesis.getVoices();
|
||||
for (var i in voices) {
|
||||
test_data.push([{text: "Hello world", voice: voices[i]},
|
||||
{uri: voices[i].voiceURI}]);
|
||||
for (var voice of voices) {
|
||||
if (voice.voiceURI.indexOf('urn:moz-tts:fake-direct') < 0) {
|
||||
continue;
|
||||
}
|
||||
test_data.push([{text: "Hello world", voice: voice},
|
||||
{uri: voice.voiceURI}]);
|
||||
}
|
||||
|
||||
synthTestQueue(test_data,
|
||||
function () {
|
||||
synthCleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
@ -26,8 +26,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=650295
|
||||
|
||||
/** Test for Bug 525444 **/
|
||||
|
||||
synthAddVoice('TestSpeechServiceWithAudio', 'Male 1', 'en-GB', true);
|
||||
|
||||
var gotStartEvent = false;
|
||||
var gotBoundaryEvent = false;
|
||||
var utterance = new SpeechSynthesisUtterance("Hello, world!");
|
||||
@ -42,7 +40,6 @@ utterance.addEventListener('end', function(e) {
|
||||
ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued.");
|
||||
ok(gotStartEvent, "Got 'start' event.");
|
||||
info('end ' + e.elapsedTime);
|
||||
synthCleanup();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s
|
||||
support-files =
|
||||
common.js
|
||||
file_setup.html
|
||||
@ -10,10 +9,6 @@ support-files =
|
||||
|
||||
[test_setup.html]
|
||||
[test_speech_queue.html]
|
||||
skip-if = buildapp == 'b2g' # b2g(Test timed out)
|
||||
[test_speech_simple.html]
|
||||
skip-if = buildapp == 'b2g' # b2g(Test timed out)
|
||||
[test_speech_cancel.html]
|
||||
skip-if = toolkit == 'gonk' # b2g(Test timed out)
|
||||
[test_indirect_service_events.html]
|
||||
skip-if = toolkit == 'gonk' # b2g(Test timed out)
|
||||
|
359
dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
Normal file
359
dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
Normal file
@ -0,0 +1,359 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.h"
|
||||
#include "nsFakeSynthServices.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "SharedBuffer.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
#include "mozilla/dom/nsSynthVoiceRegistry.h"
|
||||
#include "mozilla/dom/nsSpeechTask.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prenv.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#define CHANNELS 1
|
||||
#define SAMPLERATE 1600
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
StaticRefPtr<nsFakeSynthServices> nsFakeSynthServices::sSingleton;
|
||||
|
||||
enum VoiceFlags
|
||||
{
|
||||
eSuppressEvents = 1,
|
||||
eSuppressEnd = 2
|
||||
};
|
||||
|
||||
struct VoiceDetails
|
||||
{
|
||||
const char* uri;
|
||||
const char* name;
|
||||
const char* lang;
|
||||
bool defaultVoice;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static const VoiceDetails sDirectVoices[] = {
|
||||
{"urn:moz-tts:fake-direct:bob", "Bob Marley", "en-JM", true, 0},
|
||||
{"urn:moz-tts:fake-direct:amy", "Amy Winehouse", "en-GB", false, 0},
|
||||
{"urn:moz-tts:fake-direct:lenny", "Leonard Cohen", "en-CA", false, 0},
|
||||
{"urn:moz-tts:fake-direct:celine", "Celine Dion", "fr-CA", false, 0},
|
||||
{"urn:moz-tts:fake-direct:julie", "Julieta Venegas", "es-MX", false, },
|
||||
};
|
||||
|
||||
static const VoiceDetails sIndirectVoices[] = {
|
||||
{"urn:moz-tts:fake-indirect:zanetta", "Zanetta Farussi", "it-IT", false, 0},
|
||||
{"urn:moz-tts:fake-indirect:margherita", "Margherita Durastanti", "it-IT-noevents-noend", false, eSuppressEvents | eSuppressEnd},
|
||||
{"urn:moz-tts:fake-indirect:teresa", "Teresa Cornelys", "it-IT-noend", false, eSuppressEnd},
|
||||
};
|
||||
|
||||
// FakeSynthCallback
|
||||
class FakeSynthCallback : public nsISpeechTaskCallback
|
||||
{
|
||||
public:
|
||||
explicit FakeSynthCallback(nsISpeechTask* aTask) : mTask(aTask) { }
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(FakeSynthCallback, nsISpeechTaskCallback)
|
||||
|
||||
NS_IMETHOD OnPause()
|
||||
{
|
||||
if (mTask) {
|
||||
mTask->DispatchPause(1.5, 1);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnResume()
|
||||
{
|
||||
if (mTask) {
|
||||
mTask->DispatchResume(1.5, 1);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD OnCancel()
|
||||
{
|
||||
if (mTask) {
|
||||
mTask->DispatchEnd(1.5, 1);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~FakeSynthCallback() { }
|
||||
|
||||
nsCOMPtr<nsISpeechTask> mTask;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(FakeSynthCallback, mTask);
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FakeSynthCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISpeechTaskCallback)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpeechTaskCallback)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(FakeSynthCallback)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(FakeSynthCallback)
|
||||
|
||||
// FakeDirectAudioSynth
|
||||
|
||||
class FakeDirectAudioSynth : public nsISpeechService
|
||||
{
|
||||
|
||||
public:
|
||||
FakeDirectAudioSynth() { }
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISPEECHSERVICE
|
||||
|
||||
private:
|
||||
virtual ~FakeDirectAudioSynth() { }
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FakeDirectAudioSynth, nsISpeechService)
|
||||
|
||||
NS_IMETHODIMP
|
||||
FakeDirectAudioSynth::Speak(const nsAString& aText, const nsAString& aUri,
|
||||
float aVolume, float aRate, float aPitch,
|
||||
nsISpeechTask* aTask)
|
||||
{
|
||||
class Runnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
Runnable(nsISpeechTask* aTask, const nsAString& aText) :
|
||||
mTask(aTask), mText(aText)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
nsRefPtr<FakeSynthCallback> cb = new FakeSynthCallback(nullptr);
|
||||
mTask->Setup(cb, CHANNELS, SAMPLERATE, 2);
|
||||
|
||||
// Just an arbitrary multiplier. Pretend that each character is
|
||||
// synthesized to 40 frames.
|
||||
uint32_t frames_length = 40 * mText.Length();
|
||||
nsAutoArrayPtr<int16_t> frames(new int16_t[frames_length]());
|
||||
mTask->SendAudioNative(frames, frames_length);
|
||||
|
||||
mTask->SendAudioNative(nullptr, 0);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISpeechTask> mTask;
|
||||
nsString mText;
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new Runnable(aTask, aText);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FakeDirectAudioSynth::GetServiceType(SpeechServiceType* aServiceType)
|
||||
{
|
||||
*aServiceType = nsISpeechService::SERVICETYPE_DIRECT_AUDIO;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// FakeDirectAudioSynth
|
||||
|
||||
class FakeIndirectAudioSynth : public nsISpeechService
|
||||
{
|
||||
|
||||
public:
|
||||
FakeIndirectAudioSynth() {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISPEECHSERVICE
|
||||
|
||||
private:
|
||||
virtual ~FakeIndirectAudioSynth() { }
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FakeIndirectAudioSynth, nsISpeechService)
|
||||
|
||||
NS_IMETHODIMP
|
||||
FakeIndirectAudioSynth::Speak(const nsAString& aText, const nsAString& aUri,
|
||||
float aVolume, float aRate, float aPitch,
|
||||
nsISpeechTask* aTask)
|
||||
{
|
||||
class DispatchStart final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit DispatchStart(nsISpeechTask* aTask) :
|
||||
mTask(aTask)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mTask->DispatchStart();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISpeechTask> mTask;
|
||||
};
|
||||
|
||||
class DispatchEnd final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DispatchEnd(nsISpeechTask* aTask, const nsAString& aText) :
|
||||
mTask(aTask), mText(aText)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mTask->DispatchEnd(mText.Length()/2, mText.Length());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISpeechTask> mTask;
|
||||
nsString mText;
|
||||
};
|
||||
|
||||
uint32_t flags = 0;
|
||||
for (uint32_t i = 0; i < ArrayLength(sIndirectVoices); i++) {
|
||||
if (aUri.EqualsASCII(sIndirectVoices[i].uri)) {
|
||||
flags = sIndirectVoices[i].flags;
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<FakeSynthCallback> cb = new FakeSynthCallback(
|
||||
(flags & eSuppressEvents) ? nullptr : aTask);
|
||||
|
||||
aTask->Setup(cb, 0, 0, 0);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new DispatchStart(aTask);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
|
||||
if ((flags & eSuppressEnd) == 0) {
|
||||
runnable = new DispatchEnd(aTask, aText);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FakeIndirectAudioSynth::GetServiceType(SpeechServiceType* aServiceType)
|
||||
{
|
||||
*aServiceType = nsISpeechService::SERVICETYPE_INDIRECT_AUDIO;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsFakeSynthService
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(nsFakeSynthServices)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(nsFakeSynthServices)
|
||||
NS_IMPL_RELEASE(nsFakeSynthServices)
|
||||
|
||||
nsFakeSynthServices::nsFakeSynthServices()
|
||||
{
|
||||
}
|
||||
|
||||
nsFakeSynthServices::~nsFakeSynthServices()
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
AddVoices(nsISpeechService* aService, const VoiceDetails* aVoices, uint32_t aLength)
|
||||
{
|
||||
nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance();
|
||||
for (uint32_t i = 0; i < aLength; i++) {
|
||||
NS_ConvertUTF8toUTF16 name(aVoices[i].name);
|
||||
NS_ConvertUTF8toUTF16 uri(aVoices[i].uri);
|
||||
NS_ConvertUTF8toUTF16 lang(aVoices[i].lang);
|
||||
registry->AddVoice(aService, uri, name, lang, true);
|
||||
if (aVoices[i].defaultVoice) {
|
||||
registry->SetDefaultVoice(uri, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsFakeSynthServices::Init()
|
||||
{
|
||||
mDirectService = new FakeDirectAudioSynth();
|
||||
AddVoices(mDirectService, sDirectVoices, ArrayLength(sDirectVoices));
|
||||
|
||||
mIndirectService = new FakeIndirectAudioSynth();
|
||||
AddVoices(mIndirectService, sIndirectVoices, ArrayLength(sIndirectVoices));
|
||||
}
|
||||
|
||||
// nsIObserver
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (Preferences::GetBool("media.webspeech.synth.test")) {
|
||||
Init();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static methods
|
||||
|
||||
nsFakeSynthServices*
|
||||
nsFakeSynthServices::GetInstance()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
MOZ_ASSERT(false, "nsFakeSynthServices can only be started on main gecko process");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!sSingleton) {
|
||||
sSingleton = new nsFakeSynthServices();
|
||||
}
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
already_AddRefed<nsFakeSynthServices>
|
||||
nsFakeSynthServices::GetInstanceForService()
|
||||
{
|
||||
nsRefPtr<nsFakeSynthServices> picoService = GetInstance();
|
||||
return picoService.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsFakeSynthServices::Shutdown()
|
||||
{
|
||||
if (!sSingleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
sSingleton = nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
53
dom/media/webspeech/synth/test/nsFakeSynthServices.h
Normal file
53
dom/media/webspeech/synth/test/nsFakeSynthServices.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsFakeSynthServices_h
|
||||
#define nsFakeSynthServices_h
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsISpeechService.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class nsFakeSynthServices : public nsIObserver
|
||||
{
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsFakeSynthServices();
|
||||
|
||||
static nsFakeSynthServices* GetInstance();
|
||||
|
||||
static already_AddRefed<nsFakeSynthServices> GetInstanceForService();
|
||||
|
||||
static void Shutdown();
|
||||
|
||||
private:
|
||||
|
||||
virtual ~nsFakeSynthServices();
|
||||
|
||||
void Init();
|
||||
|
||||
nsCOMPtr<nsISpeechService> mDirectService;
|
||||
|
||||
nsCOMPtr<nsISpeechService> mIndirectService;
|
||||
|
||||
static StaticRefPtr<nsFakeSynthServices> sSingleton;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user