michael@0: var gSpeechRegistry = SpecialPowers.Cc["@mozilla.org/synth-voice-registry;1"] michael@0: .getService(SpecialPowers.Ci.nsISynthVoiceRegistry); michael@0: michael@0: var gAddedVoices = []; michael@0: michael@0: function SpeechTaskCallback(onpause, onresume, oncancel) { michael@0: this.onpause = onpause; michael@0: this.onresume = onresume; michael@0: this.oncancel = oncancel; michael@0: } michael@0: michael@0: SpeechTaskCallback.prototype = { michael@0: QueryInterface: function(iid) { michael@0: return this; michael@0: }, michael@0: michael@0: getInterfaces: function(c) {}, michael@0: michael@0: getHelperForLanguage: function() {}, michael@0: michael@0: onPause: function onPause() { michael@0: if (this.onpause) michael@0: this.onpause(); michael@0: }, michael@0: michael@0: onResume: function onResume() { michael@0: if (this.onresume) michael@0: this.onresume(); michael@0: }, michael@0: michael@0: onCancel: function onCancel() { michael@0: if (this.oncancel) michael@0: this.oncancel(); michael@0: } michael@0: }; michael@0: michael@0: var TestSpeechServiceWithAudio = SpecialPowers.wrapCallbackObject({ michael@0: CHANNELS: 1, michael@0: SAMPLE_RATE: 16000, michael@0: michael@0: serviceType: SpecialPowers.Ci.nsISpeechService.SERVICETYPE_DIRECT_AUDIO, michael@0: michael@0: speak: function speak(aText, aUri, aRate, aPitch, aTask) { michael@0: var task = SpecialPowers.wrap(aTask); michael@0: michael@0: window.setTimeout( michael@0: function () { michael@0: task.setup(SpecialPowers.wrapCallbackObject(new SpeechTaskCallback()), this.CHANNELS, this.SAMPLE_RATE); michael@0: // 0.025 seconds per character. michael@0: task.sendAudio(new Int16Array((this.SAMPLE_RATE/40)*aText.length), []); michael@0: task.sendAudio(new Int16Array(0), []); michael@0: }.bind(this), 0); michael@0: }, michael@0: michael@0: QueryInterface: function(iid) { michael@0: return this; michael@0: }, michael@0: michael@0: getInterfaces: function(c) {}, michael@0: michael@0: getHelperForLanguage: function() {} michael@0: }); michael@0: michael@0: var TestSpeechServiceNoAudio = SpecialPowers.wrapCallbackObject({ michael@0: serviceType: SpecialPowers.Ci.nsISpeechService.SERVICETYPE_INDIRECT_AUDIO, michael@0: michael@0: speak: function speak(aText, aUri, aRate, aPitch, aTask) { michael@0: var pair = this.expectedSpeaks.shift(); michael@0: if (pair) { michael@0: // XXX: These tests do not happen in OOP michael@0: var utterance = pair[0]; michael@0: var expected = pair[1]; michael@0: michael@0: is(aText, utterance.text, "Speak text matches utterance text"); michael@0: michael@0: var args = {uri: aUri, rate: aRate, pitch: aPitch}; michael@0: michael@0: for (var attr in args) { michael@0: if (expected[attr] != undefined) michael@0: is(args[attr], expected[attr], "expected service arg " + attr); michael@0: } michael@0: } michael@0: michael@0: var task = SpecialPowers.wrap(aTask); michael@0: task.setup(SpecialPowers.wrapCallbackObject(new SpeechTaskCallback())); michael@0: setTimeout(function () { michael@0: task.dispatchStart(); michael@0: setTimeout(function () { michael@0: task.dispatchEnd(aText.length / 2.0, aText.length); michael@0: }, 0); michael@0: michael@0: }, 0); michael@0: }, michael@0: michael@0: QueryInterface: function(iid) { michael@0: return this; michael@0: }, michael@0: michael@0: getInterfaces: function(c) {}, michael@0: michael@0: getHelperForLanguage: function() {}, michael@0: michael@0: expectedSpeaks: [] michael@0: }); michael@0: michael@0: function synthAddVoice(aServiceName, aName, aLang, aIsLocal) { michael@0: if (SpecialPowers.isMainProcess()) { michael@0: var voicesBefore = speechSynthesis.getVoices().length; michael@0: var uri = "urn:moz-tts:mylittleservice:" + encodeURI(aName + '?' + aLang); michael@0: gSpeechRegistry.addVoice(window[aServiceName], uri, aName, aLang, aIsLocal); michael@0: michael@0: gAddedVoices.push([window[aServiceName], uri]); michael@0: var voicesAfter = speechSynthesis.getVoices().length; michael@0: michael@0: is(voicesBefore + 1, voicesAfter, "Voice added"); michael@0: var voice = speechSynthesis.getVoices()[voicesAfter - 1]; michael@0: is(voice.voiceURI, uri, "voice URI matches"); michael@0: is(voice.name, aName, "voice name matches"); michael@0: is(voice.lang, aLang, "voice lang matches"); michael@0: is(voice.localService, aIsLocal, "voice localService matches"); michael@0: michael@0: return uri; michael@0: } else { michael@0: // XXX: It would be nice to check here that the child gets the voice michael@0: // added update, but alas, it is aynchronous. michael@0: var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] michael@0: .getService(SpecialPowers.Ci.nsISyncMessageSender); michael@0: michael@0: return mm.sendSyncMessage( michael@0: 'test:SpeechSynthesis:ipcSynthAddVoice', michael@0: [aServiceName, aName, aLang, aIsLocal])[0]; michael@0: } michael@0: } michael@0: michael@0: function synthSetDefault(aUri, aIsDefault) { michael@0: if (SpecialPowers.isMainProcess()) { michael@0: gSpeechRegistry.setDefaultVoice(aUri, aIsDefault); michael@0: var voices = speechSynthesis.getVoices(); michael@0: for (var i in voices) { michael@0: if (voices[i].voiceURI == aUri) michael@0: ok(voices[i]['default'], "Voice set to default"); michael@0: } michael@0: } else { michael@0: // XXX: It would be nice to check here that the child gets the voice michael@0: // added update, but alas, it is aynchronous. michael@0: var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] michael@0: .getService(SpecialPowers.Ci.nsISyncMessageSender); michael@0: michael@0: return mm.sendSyncMessage( michael@0: 'test:SpeechSynthesis:ipcSynthSetDefault', [aUri, aIsDefault])[0]; michael@0: } michael@0: } michael@0: michael@0: function synthCleanup() { michael@0: if (SpecialPowers.isMainProcess()) { michael@0: var voicesBefore = speechSynthesis.getVoices().length; michael@0: var toRemove = gAddedVoices.length; michael@0: var removeArgs; michael@0: while ((removeArgs = gAddedVoices.shift())) michael@0: gSpeechRegistry.removeVoice.apply(gSpeechRegistry.removeVoice, removeArgs); michael@0: michael@0: var voicesAfter = speechSynthesis.getVoices().length; michael@0: is(voicesAfter, voicesBefore - toRemove, "Successfully removed test voices"); michael@0: } else { michael@0: // XXX: It would be nice to check here that the child gets the voice michael@0: // removed update, but alas, it is aynchronous. michael@0: var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] michael@0: .getService(SpecialPowers.Ci.nsISyncMessageSender); michael@0: mm.sendSyncMessage('test:SpeechSynthesis:ipcSynthCleanup'); michael@0: } michael@0: } michael@0: michael@0: function synthTestQueue(aTestArgs, aEndFunc) { michael@0: var utterances = []; michael@0: for (var i in aTestArgs) { michael@0: var uargs = aTestArgs[i][0]; michael@0: var u = new SpeechSynthesisUtterance(uargs.text); michael@0: michael@0: delete uargs.text; michael@0: michael@0: for (var attr in uargs) michael@0: u[attr] = uargs[attr]; michael@0: michael@0: function onend_handler(e) { michael@0: is(e.target, utterances.shift(), "Target matches utterances"); michael@0: ok(!speechSynthesis.speaking, "speechSynthesis is not speaking."); michael@0: michael@0: isnot(e.eventType, 'error', "Error in utterance"); michael@0: michael@0: if (utterances.length) { michael@0: ok(speechSynthesis.pending, "other utterances queued"); michael@0: } else { michael@0: ok(!speechSynthesis.pending, "queue is empty, nothing pending."); michael@0: if (aEndFunc) michael@0: aEndFunc(); michael@0: } michael@0: } michael@0: michael@0: u.addEventListener('end', onend_handler); michael@0: u.addEventListener('error', onend_handler); michael@0: michael@0: u.addEventListener( michael@0: 'error', function onerror_handler(e) { michael@0: ok(false, "Error in speech utterance '" + e.target.text + "'"); michael@0: }); michael@0: michael@0: utterances.push(u); michael@0: TestSpeechServiceNoAudio.expectedSpeaks.push([u, aTestArgs[i][1]]); michael@0: speechSynthesis.speak(u); michael@0: } michael@0: michael@0: ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet."); michael@0: ok(speechSynthesis.pending, "speechSynthesis has an utterance queued."); michael@0: }