diff options
Diffstat (limited to 'dom/media/webspeech/synth/test')
28 files changed, 1852 insertions, 0 deletions
diff --git a/dom/media/webspeech/synth/test/FakeSynthModule.cpp b/dom/media/webspeech/synth/test/FakeSynthModule.cpp new file mode 100644 index 000000000..5621ab78a --- /dev/null +++ b/dom/media/webspeech/synth/test/FakeSynthModule.cpp @@ -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[] = { + { "speech-synth-started", "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; diff --git a/dom/media/webspeech/synth/test/common.js b/dom/media/webspeech/synth/test/common.js new file mode 100644 index 000000000..0ce9ec51b --- /dev/null +++ b/dom/media/webspeech/synth/test/common.js @@ -0,0 +1,91 @@ +function synthTestQueue(aTestArgs, aEndFunc) { + var utterances = []; + for (var i in aTestArgs) { + var uargs = aTestArgs[i][0]; + var win = uargs.win || window; + var u = new win.SpeechSynthesisUtterance(uargs.text); + + if (uargs.args) { + for (var attr in uargs.args) + u[attr] = uargs.args[attr]; + } + + function onend_handler(e) { + is(e.target, utterances.shift(), "Target matches utterances"); + ok(!speechSynthesis.speaking, "speechSynthesis is not speaking."); + + if (utterances.length) { + ok(speechSynthesis.pending, "other utterances queued"); + } else { + ok(!speechSynthesis.pending, "queue is empty, nothing pending."); + if (aEndFunc) + 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); + + u.addEventListener('error', + (function (expectedError) { + return function onerror_handler(e) { + ok(expectedError, "Error in speech utterance '" + e.target.text + "'"); + }; + })(aTestArgs[i][1] ? aTestArgs[i][1].err : false)); + + utterances.push(u); + win.speechSynthesis.speak(u); + } + + ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet."); + ok(speechSynthesis.pending, "speechSynthesis has an utterance queued."); +} + +function loadFrame(frameId) { + return new Promise(function(resolve, reject) { + var frame = document.getElementById(frameId); + frame.addEventListener('load', function (e) { + frame.contentWindow.document.title = frameId; + resolve(frame); + }); + frame.src = 'data:text/html,' + encodeURI('<html><head></head><body></body></html>'); + }); +} + +function waitForVoices(win) { + return new Promise(resolve => { + function resolver() { + if (win.speechSynthesis.getVoices().length) { + win.speechSynthesis.removeEventListener('voiceschanged', resolver); + resolve(); + } + } + + win.speechSynthesis.addEventListener('voiceschanged', resolver); + resolver(); + }); +} + +function loadSpeechTest(fileName, prefs, frameId="testFrame") { + loadFrame(frameId).then(frame => { + waitForVoices(frame.contentWindow).then( + () => document.getElementById("testFrame").src = fileName); + }); +} + +function testSynthState(win, expectedState) { + for (var attr in expectedState) { + is(win.speechSynthesis[attr], expectedState[attr], + win.document.title + ": '" + attr + '" does not match'); + } +}
\ No newline at end of file diff --git a/dom/media/webspeech/synth/test/file_bfcache_frame.html b/dom/media/webspeech/synth/test/file_bfcache_frame.html new file mode 100644 index 000000000..c8663b7fb --- /dev/null +++ b/dom/media/webspeech/synth/test/file_bfcache_frame.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script type="application/javascript"> + var frameUnloaded = function() { + var u = new SpeechSynthesisUtterance('hi'); + u.addEventListener('end', function () { + parent.ok(true, 'Successfully spoke utterance from new frame.'); + parent.onDone(); + }); + speechSynthesis.speak(u); + } + addEventListener('pageshow', function onshow(evt) { + var u = new SpeechSynthesisUtterance('hello'); + u.lang = 'it-IT-noend'; + u.addEventListener('start', function() { + location = + 'data:text/html,<html><body onpageshow="' + + frameUnloaded.toSource() + '()"></body></html>'; + }); + speechSynthesis.speak(u); + }); + </script> +</head> +<body> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_global_queue.html b/dom/media/webspeech/synth/test/file_global_queue.html new file mode 100644 index 000000000..5d762c0d5 --- /dev/null +++ b/dom/media/webspeech/synth/test/file_global_queue.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1188099 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1188099: Global queue should correctly schedule utterances</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + window.todo = parent.todo; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a> +<iframe id="frame1"></iframe> +<iframe id="frame2"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + Promise.all([loadFrame('frame1'), loadFrame('frame2')]).then(function ([frame1, frame2]) { + var win1 = frame1.contentWindow; + var win2 = frame2.contentWindow; + var utterance1 = new win1.SpeechSynthesisUtterance("hello, losers"); + var utterance2 = new win1.SpeechSynthesisUtterance("hello, losers three"); + var utterance3 = new win2.SpeechSynthesisUtterance("hello, losers too"); + var eventOrder = ['start1', 'end1', 'start3', 'end3', 'start2', 'end2']; + utterance1.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start1', 'start1'); + testSynthState(win1, { speaking: true, pending: true }); + testSynthState(win2, { speaking: true, pending: true }); + }); + utterance1.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end1', 'end1'); + }); + utterance3.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start3', 'start3'); + testSynthState(win1, { speaking: true, pending: true }); + testSynthState(win2, { speaking: true, pending: false }); + }); + utterance3.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end3', 'end3'); + }); + utterance2.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start2', 'start2'); + testSynthState(win1, { speaking: true, pending: false }); + testSynthState(win2, { speaking: true, pending: false }); + }); + utterance2.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end2', 'end2'); + testSynthState(win1, { speaking: false, pending: false }); + testSynthState(win2, { speaking: false, pending: false }); + SimpleTest.finish(); + }); + win1.speechSynthesis.speak(utterance1); + win1.speechSynthesis.speak(utterance2); + win2.speechSynthesis.speak(utterance3); + }); +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_global_queue_cancel.html b/dom/media/webspeech/synth/test/file_global_queue_cancel.html new file mode 100644 index 000000000..03b77ba2f --- /dev/null +++ b/dom/media/webspeech/synth/test/file_global_queue_cancel.html @@ -0,0 +1,88 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1188099 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1188099: Calling cancel() should work correctly with global queue</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + window.todo = parent.todo; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a> +<iframe id="frame1"></iframe> +<iframe id="frame2"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + Promise.all([loadFrame('frame1'), loadFrame('frame2')]).then(function ([frame1, frame2]) { + var win1 = frame1.contentWindow; + var win2 = frame2.contentWindow; + + var utterance1 = new win1.SpeechSynthesisUtterance( + "u1: Donec ac nunc feugiat, posuere"); + utterance1.lang = 'it-IT-noend'; + var utterance2 = new win1.SpeechSynthesisUtterance("u2: hello, losers too"); + utterance2.lang = 'it-IT-noend'; + var utterance3 = new win1.SpeechSynthesisUtterance("u3: hello, losers three"); + + var utterance4 = new win2.SpeechSynthesisUtterance("u4: hello, losers same!"); + utterance4.lang = 'it-IT-noend'; + var utterance5 = new win2.SpeechSynthesisUtterance("u5: hello, losers too"); + utterance5.lang = 'it-IT-noend'; + + var eventOrder = ['start1', 'end1', 'start2', 'end2']; + utterance1.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start1', 'start1'); + testSynthState(win1, { speaking: true, pending: true }); + testSynthState(win2, { speaking: true, pending: true }); + win2.speechSynthesis.cancel(); + SpecialPowers.wrap(win1.speechSynthesis).forceEnd(); + + }); + utterance1.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end1', 'end1'); + testSynthState(win1, { pending: true }); + testSynthState(win2, { pending: false }); + }); + utterance2.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start2', 'start2'); + testSynthState(win1, { speaking: true, pending: true }); + testSynthState(win2, { speaking: true, pending: false }); + win1.speechSynthesis.cancel(); + }); + utterance2.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end2', 'end2'); + testSynthState(win1, { speaking: false, pending: false }); + testSynthState(win2, { speaking: false, pending: false }); + SimpleTest.finish(); + }); + + function wrongUtterance(e) { + ok(false, 'This shall not be uttered: "' + e.target.text + '"'); + } + + utterance3.addEventListener('start', wrongUtterance); + utterance4.addEventListener('start', wrongUtterance); + utterance5.addEventListener('start', wrongUtterance); + + win1.speechSynthesis.speak(utterance1); + win1.speechSynthesis.speak(utterance2); + win1.speechSynthesis.speak(utterance3); + win2.speechSynthesis.speak(utterance4); + win2.speechSynthesis.speak(utterance5); + }); +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_global_queue_pause.html b/dom/media/webspeech/synth/test/file_global_queue_pause.html new file mode 100644 index 000000000..7fd562133 --- /dev/null +++ b/dom/media/webspeech/synth/test/file_global_queue_pause.html @@ -0,0 +1,131 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1188099 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1188099: Calling pause() should work correctly with global queue</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + window.todo = parent.todo; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a> +<iframe id="frame1"></iframe> +<iframe id="frame2"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + Promise.all([loadFrame('frame1'), loadFrame('frame2')]).then(function ([frame1, frame2]) { + var win1 = frame1.contentWindow; + var win2 = frame2.contentWindow; + + var utterance1 = new win1.SpeechSynthesisUtterance("Speak utterance 1."); + utterance1.lang = 'it-IT-noend'; + var utterance2 = new win2.SpeechSynthesisUtterance("Speak utterance 2."); + var utterance3 = new win1.SpeechSynthesisUtterance("Speak utterance 3."); + var utterance4 = new win2.SpeechSynthesisUtterance("Speak utterance 4."); + var eventOrder = ['start1', 'pause1', 'resume1', 'end1', 'start2', 'end2', + 'start4', 'end4', 'start3', 'end3']; + + utterance1.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start1', 'start1'); + win1.speechSynthesis.pause(); + }); + utterance1.addEventListener('pause', function(e) { + var expectedEvent = eventOrder.shift() + is(expectedEvent, 'pause1', 'pause1'); + testSynthState(win1, { speaking: true, pending: false, paused: true}); + testSynthState(win2, { speaking: true, pending: true, paused: false}); + + if (expectedEvent == 'pause1') { + win1.speechSynthesis.resume(); + } + }); + utterance1.addEventListener('resume', function(e) { + is(eventOrder.shift(), 'resume1', 'resume1'); + testSynthState(win1, { speaking: true, pending: false, paused: false}); + testSynthState(win2, { speaking: true, pending: true, paused: false}); + + win2.speechSynthesis.pause(); + + testSynthState(win1, { speaking: true, pending: false, paused: false}); + // 1188099: currently, paused state is not gaurenteed to be immediate. + testSynthState(win2, { speaking: true, pending: true }); + + // We now make the utterance end. + SpecialPowers.wrap(win1.speechSynthesis).forceEnd(); + }); + utterance1.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end1', 'end1'); + testSynthState(win1, { speaking: false, pending: false, paused: false}); + testSynthState(win2, { speaking: false, pending: true, paused: true}); + + win2.speechSynthesis.resume(); + }); + + utterance2.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start2', 'start2'); + testSynthState(win1, { speaking: true, pending: false, paused: false}); + testSynthState(win2, { speaking: true, pending: false, paused: false}); + }); + utterance2.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end2', 'end2'); + testSynthState(win1, { speaking: false, pending: false, paused: false}); + testSynthState(win2, { speaking: false, pending: false, paused: false}); + + win1.speechSynthesis.pause(); + + testSynthState(win1, { speaking: false, pending: false, paused: true}); + testSynthState(win2, { speaking: false, pending: false, paused: false}); + + win1.speechSynthesis.speak(utterance3); + win2.speechSynthesis.speak(utterance4); + + testSynthState(win1, { speaking: false, pending: true, paused: true}); + testSynthState(win2, { speaking: false, pending: true, paused: false}); + }); + + utterance4.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start4', 'start4'); + testSynthState(win1, { speaking: true, pending: true, paused: true}); + testSynthState(win2, { speaking: true, pending: false, paused: false}); + + win1.speechSynthesis.resume(); + }); + utterance4.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end4', 'end4'); + testSynthState(win1, { speaking: false, pending: true, paused: false}); + testSynthState(win2, { speaking: false, pending: false, paused: false}); + }); + + utterance3.addEventListener('start', function(e) { + is(eventOrder.shift(), 'start3', 'start3'); + testSynthState(win1, { speaking: true, pending: false, paused: false}); + testSynthState(win2, { speaking: true, pending: false, paused: false}); + }); + + utterance3.addEventListener('end', function(e) { + is(eventOrder.shift(), 'end3', 'end3'); + testSynthState(win1, { speaking: false, pending: false, paused: false}); + testSynthState(win2, { speaking: false, pending: false, paused: false}); + + SimpleTest.finish(); + }); + + win1.speechSynthesis.speak(utterance1); + win2.speechSynthesis.speak(utterance2); + }); +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_indirect_service_events.html b/dom/media/webspeech/synth/test/file_indirect_service_events.html new file mode 100644 index 000000000..fb4e31244 --- /dev/null +++ b/dom/media/webspeech/synth/test/file_indirect_service_events.html @@ -0,0 +1,102 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1155034 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1155034: Check that indirect audio services dispatch their own events</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1155034">Mozilla Bug 1155034</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1155034 **/ + +function testFunc(done_cb) { + 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) { + info('start test_with_events'); + speechSynthesis.pause(); + // Wait to see if we get some bad events we didn't expect. + }); + + utterance.addEventListener('pause', function(e) { + 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) { + 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.5, 'end event elapsedTime matches service arguments'); + test_no_events(); + }); + + info('start speak'); + speechSynthesis.speak(utterance); + } + + function forbiddenEvent(e) { + ok(false, 'no "' + e.type + '" event was explicitly dispatched from the service') + } + + function test_no_events() { + info('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() { + ok(true, 'didn\'t get any unwanted events'); + utterance.removeEventListener('end', forbiddenEvent); + SpecialPowers.wrap(speechSynthesis).forceEnd(); + done_cb(); + }, 1000); + }); + + utterance.addEventListener('pause', forbiddenEvent); + utterance.addEventListener('end', forbiddenEvent); + + speechSynthesis.speak(utterance); + } + + test_with_events(); +} + +// Run test with no global queue, and then run it with a global queue. +testFunc(function() { + SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.force_global_queue', true]] }, function() { + testFunc(SimpleTest.finish) + }); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_setup.html b/dom/media/webspeech/synth/test/file_setup.html new file mode 100644 index 000000000..4c1020505 --- /dev/null +++ b/dom/media/webspeech/synth/test/file_setup.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=525444 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 525444: Web Speech API check all classes are present</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +ok(SpeechSynthesis, "SpeechSynthesis exists in global scope"); +ok(SpeechSynthesisVoice, "SpeechSynthesisVoice exists in global scope"); +ok(SpeechSynthesisErrorEvent, "SpeechSynthesisErrorEvent exists in global scope"); +ok(SpeechSynthesisEvent, "SpeechSynthesisEvent exists in global scope"); + +// SpeechSynthesisUtterance is the only type that has a constructor +// and writable properties +ok(SpeechSynthesisUtterance, "SpeechSynthesisUtterance exists in global scope"); +var ssu = new SpeechSynthesisUtterance("hello world"); +is(typeof ssu, "object", "SpeechSynthesisUtterance instance is an object"); +is(ssu.text, "hello world", "SpeechSynthesisUtterance.text is correct"); +is(ssu.volume, 1, "SpeechSynthesisUtterance.volume default is correct"); +is(ssu.rate, 1, "SpeechSynthesisUtterance.rate default is correct"); +is(ssu.pitch, 1, "SpeechSynthesisUtterance.pitch default is correct"); +ssu.lang = "he-IL"; +ssu.volume = 0.5; +ssu.rate = 2.0; +ssu.pitch = 1.5; +is(ssu.lang, "he-IL", "SpeechSynthesisUtterance.lang is correct"); +is(ssu.volume, 0.5, "SpeechSynthesisUtterance.volume is correct"); +is(ssu.rate, 2.0, "SpeechSynthesisUtterance.rate is correct"); +is(ssu.pitch, 1.5, "SpeechSynthesisUtterance.pitch is correct"); + +// Assign a rate that is out of bounds +ssu.rate = 20; +is(ssu.rate, 10, "SpeechSynthesisUtterance.rate enforces max of 10"); +ssu.rate = 0; +is(ssu.rate.toPrecision(1), "0.1", "SpeechSynthesisUtterance.rate enforces min of 0.1"); + +// Assign a volume which is out of bounds +ssu.volume = 2; +is(ssu.volume, 1, "SpeechSynthesisUtterance.volume enforces max of 1"); +ssu.volume = -1; +is(ssu.volume, 0, "SpeechSynthesisUtterance.volume enforces min of 0"); + +// Assign a pitch which is out of bounds +ssu.pitch = 2.1; +is(ssu.pitch, 2, "SpeechSynthesisUtterance.pitch enforces max of 2"); +ssu.pitch = -1; +is(ssu.pitch, 0, "SpeechSynthesisUtterance.pitch enforces min of 0"); + +// Test for singleton instance hanging off of window. +ok(speechSynthesis, "speechSynthesis exists in global scope"); +is(typeof speechSynthesis, "object", "speechSynthesis instance is an object"); +is(typeof speechSynthesis.speak, "function", "speechSynthesis.speak is a function"); +is(typeof speechSynthesis.cancel, "function", "speechSynthesis.cancel is a function"); +is(typeof speechSynthesis.pause, "function", "speechSynthesis.pause is a function"); +is(typeof speechSynthesis.resume, "function", "speechSynthesis.resume is a function"); +is(typeof speechSynthesis.getVoices, "function", "speechSynthesis.getVoices is a function"); + +is(typeof speechSynthesis.pending, "boolean", "speechSynthesis.pending is a boolean"); +is(typeof speechSynthesis.speaking, "boolean", "speechSynthesis.speaking is a boolean"); +is(typeof speechSynthesis.paused, "boolean", "speechSynthesis.paused is a boolean"); + +var voices1 = speechSynthesis.getVoices(); +var voices2 = speechSynthesis.getVoices(); + +ok(voices1.length == voices2.length, "Voice count matches"); + +for (var i in voices1) { + ok(voices1[i] == voices2[i], "Voice instance matches"); +} + +SimpleTest.finish(); +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_speech_cancel.html b/dom/media/webspeech/synth/test/file_speech_cancel.html new file mode 100644 index 000000000..2ab0e1d0a --- /dev/null +++ b/dom/media/webspeech/synth/test/file_speech_cancel.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1150315 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1150315: Check that successive cancel/speak calls work</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1150315">Mozilla Bug 1150315</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1150315 **/ + +function testFunc(done_cb) { + var gotEndEvent = false; + // 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(); + info('cancel!'); + speechSynthesis.speak(utterance2); + info('speak??'); + }); + + 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() { + info('start'); + speechSynthesis.cancel(); + speechSynthesis.speak(utterance3); + }); + utterance2.addEventListener('end', function(e) { + gotEndEvent = true; + }); + + var utterance3 = new SpeechSynthesisUtterance("Hello, world 3!"); + utterance3.addEventListener('start', function() { + ok(gotEndEvent, "didn't get start event for this utterance"); + }); + utterance3.addEventListener('end', done_cb); + + // Speak/cancel while paused (Bug 1187105) + speechSynthesis.pause(); + speechSynthesis.speak(new SpeechSynthesisUtterance("hello.")); + ok(speechSynthesis.pending, "paused speechSynthesis has an utterance queued."); + speechSynthesis.cancel(); + ok(!speechSynthesis.pending, "paused speechSynthesis has no utterance queued."); + speechSynthesis.resume(); + + speechSynthesis.speak(utterance); + ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet."); + ok(speechSynthesis.pending, "speechSynthesis has an utterance queued."); +} + +// Run test with no global queue, and then run it with a global queue. +testFunc(function() { + SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.force_global_queue', true]] }, function() { + testFunc(SimpleTest.finish) + }); +}); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_speech_error.html b/dom/media/webspeech/synth/test/file_speech_error.html new file mode 100644 index 000000000..b98ec2fac --- /dev/null +++ b/dom/media/webspeech/synth/test/file_speech_error.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1226015 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1226015</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1226015">Mozilla Bug 1226015</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1226015 **/ + +function testFunc(done_cb) { + var utterance = new SpeechSynthesisUtterance(); + utterance.lang = 'it-IT-failatstart'; + + speechSynthesis.speak(utterance); + speechSynthesis.cancel(); + + ok(true, "we didn't crash, that is good.") + SimpleTest.finish(); +} + +// Run test with no global queue, and then run it with a global queue. +testFunc(); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_speech_queue.html b/dom/media/webspeech/synth/test/file_speech_queue.html new file mode 100644 index 000000000..e308f35e5 --- /dev/null +++ b/dom/media/webspeech/synth/test/file_speech_queue.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html lang="en-US"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=525444 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 525444: Web Speech API, check speech synth queue</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<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 **/ + +// XXX: Rate and pitch are not tested. + +var langUriMap = {}; + +for (var voice of speechSynthesis.getVoices()) { + 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'); +ok(langUriMap['it-IT-fail'], 'No Failing Italian voice'); + +function testFunc(done_cb) { + synthTestQueue( + [[{text: "Hello, world."}, + { uri: langUriMap['en-JM'] }], + [{text: "Bonjour tout le monde .", + args: { lang: "fr", rate: 0.5, pitch: 0.75 }}, + { uri: langUriMap['fr-CA'], rate: 0.5, pitch: 0.75}], + [{text: "How are you doing?", args: { lang: "en-GB" } }, + { rate: 1, pitch: 1, uri: langUriMap['en-GB']}], + [{text: "Come stai?", args: { lang: "it-IT-fail" } }, + { rate: 1, pitch: 1, uri: langUriMap['it-IT-fail'], err: true }], + [{text: "¡hasta mañana!", args: { lang: "es-MX" } }, + { uri: langUriMap['es-MX'] }]], + function () { + var test_data = []; + var voices = speechSynthesis.getVoices(); + for (var voice of voices) { + if (voice.voiceURI.indexOf('urn:moz-tts:fake-direct') < 0) { + continue; + } + test_data.push([{text: "Hello world", args: { voice: voice} }, + {uri: voice.voiceURI}]); + } + + synthTestQueue(test_data, done_cb); + }); +} + +// Run test with no global queue, and then run it with a global queue. +testFunc(function() { + SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.force_global_queue', true]] }, function() { + testFunc(SimpleTest.finish) + }); +}); + + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/file_speech_simple.html b/dom/media/webspeech/synth/test/file_speech_simple.html new file mode 100644 index 000000000..c3f240ccd --- /dev/null +++ b/dom/media/webspeech/synth/test/file_speech_simple.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=650295 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 650295: Web Speech API check all classes are present</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.info = parent.info; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> + <script type="application/javascript" src="common.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +var gotStartEvent = false; +var gotBoundaryEvent = false; +var utterance = new SpeechSynthesisUtterance("Hello, world!"); +utterance.addEventListener('start', function(e) { + ok(speechSynthesis.speaking, "speechSynthesis is speaking."); + ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued."); + gotStartEvent = true; +}); + +utterance.addEventListener('end', function(e) { + ok(!speechSynthesis.speaking, "speechSynthesis is not speaking."); + ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued."); + ok(gotStartEvent, "Got 'start' event."); + info('end ' + e.elapsedTime); + SimpleTest.finish(); +}); + +speechSynthesis.speak(utterance); +ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet."); +ok(speechSynthesis.pending, "speechSynthesis has an utterance queued."); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/mochitest.ini b/dom/media/webspeech/synth/test/mochitest.ini new file mode 100644 index 000000000..f27cd4e1b --- /dev/null +++ b/dom/media/webspeech/synth/test/mochitest.ini @@ -0,0 +1,26 @@ +[DEFAULT] +tags=msg +subsuite = media +support-files = + common.js + file_bfcache_frame.html + file_setup.html + file_speech_queue.html + file_speech_simple.html + file_speech_cancel.html + file_speech_error.html + file_indirect_service_events.html + file_global_queue.html + file_global_queue_cancel.html + file_global_queue_pause.html + +[test_setup.html] +[test_speech_queue.html] +[test_speech_simple.html] +[test_speech_cancel.html] +[test_speech_error.html] +[test_indirect_service_events.html] +[test_global_queue.html] +[test_global_queue_cancel.html] +[test_global_queue_pause.html] +[test_bfcache.html] diff --git a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp new file mode 100644 index 000000000..582ff3551 --- /dev/null +++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp @@ -0,0 +1,401 @@ +/* -*- 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, + eFailAtStart = 4, + eFail = 8 +}; + +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}, + {"urn:moz-tts:fake-indirect:cecilia", "Cecilia Bartoli", "it-IT-failatstart", false, eFailAtStart}, + {"urn:moz-tts:fake-indirect:gottardo", "Gottardo Aldighieri", "it-IT-fail", false, eFail}, +}; + +// 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() override + { + if (mTask) { + mTask->DispatchPause(1.5, 1); + } + + return NS_OK; + } + + NS_IMETHOD OnResume() override + { + if (mTask) { + mTask->DispatchResume(1.5, 1); + } + + return NS_OK; + } + + NS_IMETHOD OnCancel() override + { + if (mTask) { + mTask->DispatchEnd(1.5, 1); + } + + return NS_OK; + } + + NS_IMETHOD OnVolumeChanged(float aVolume) override + { + 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 mozilla::Runnable + { + public: + Runnable(nsISpeechTask* aTask, const nsAString& aText) : + mTask(aTask), mText(aText) + { + } + + NS_IMETHOD Run() override + { + RefPtr<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(); + auto frames = MakeUnique<int16_t[]>(frames_length); + mTask->SendAudioNative(frames.get(), 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 Runnable + { + public: + explicit DispatchStart(nsISpeechTask* aTask) : + mTask(aTask) + { + } + + NS_IMETHOD Run() override + { + mTask->DispatchStart(); + + return NS_OK; + } + + private: + nsCOMPtr<nsISpeechTask> mTask; + }; + + class DispatchEnd final : public Runnable + { + 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; + }; + + class DispatchError final : public Runnable + { + public: + DispatchError(nsISpeechTask* aTask, const nsAString& aText) : + mTask(aTask), mText(aText) + { + } + + NS_IMETHOD Run() override + { + mTask->DispatchError(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; + } + } + + if (flags & eFailAtStart) { + return NS_ERROR_FAILURE; + } + + RefPtr<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 & eFail) { + runnable = new DispatchError(aTask, aText); + NS_DispatchToMainThread(runnable); + } else 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) +{ + RefPtr<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); + // These services can handle more than one utterance at a time and have + // several speaking simultaniously. So, aQueuesUtterances == false + registry->AddVoice(aService, uri, name, lang, true, false); + if (aVoices[i].defaultVoice) { + registry->SetDefaultVoice(uri, true); + } + } + + registry->NotifyVoicesChanged(); +} + +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()); + if(NS_WARN_IF(!(!strcmp(aTopic, "speech-synth-started")))) { + return NS_ERROR_UNEXPECTED; + } + + if (Preferences::GetBool("media.webspeech.synth.test")) { + NS_DispatchToMainThread(NewRunnableMethod(this, &nsFakeSynthServices::Init)); + } + + return NS_OK; +} + +// static methods + +nsFakeSynthServices* +nsFakeSynthServices::GetInstance() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!XRE_IsParentProcess()) { + 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() +{ + RefPtr<nsFakeSynthServices> picoService = GetInstance(); + return picoService.forget(); +} + +void +nsFakeSynthServices::Shutdown() +{ + if (!sSingleton) { + return; + } + + sSingleton = nullptr; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/media/webspeech/synth/test/nsFakeSynthServices.h b/dom/media/webspeech/synth/test/nsFakeSynthServices.h new file mode 100644 index 000000000..bab93e779 --- /dev/null +++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.h @@ -0,0 +1,52 @@ +/* -*- 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 "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 diff --git a/dom/media/webspeech/synth/test/startup/file_voiceschanged.html b/dom/media/webspeech/synth/test/startup/file_voiceschanged.html new file mode 100644 index 000000000..6bb25462e --- /dev/null +++ b/dom/media/webspeech/synth/test/startup/file_voiceschanged.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1254378 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1254378: Web Speech API check all classes are present</title> + <script type="application/javascript"> + window.SimpleTest = parent.SimpleTest; + window.is = parent.is; + window.isnot = parent.isnot; + window.ok = parent.ok; + </script> +</head> +<body> +<script type="application/javascript"> + +/** Test for Bug 1254378 **/ + +function onVoicesChanged() { + isnot(speechSynthesis.getVoices().length, 0, "Voices added"); + speechSynthesis.removeEventListener("voiceschanged", onVoicesChanged); + SimpleTest.finish(); +} + +speechSynthesis.addEventListener("voiceschanged", onVoicesChanged); + +is(speechSynthesis.getVoices().length, 0, "No voices added initially"); +</script> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/startup/mochitest.ini b/dom/media/webspeech/synth/test/startup/mochitest.ini new file mode 100644 index 000000000..7312a71eb --- /dev/null +++ b/dom/media/webspeech/synth/test/startup/mochitest.ini @@ -0,0 +1,7 @@ +[DEFAULT] +tags=msg +subsuite = media +support-files = + file_voiceschanged.html + +[test_voiceschanged.html] diff --git a/dom/media/webspeech/synth/test/startup/test_voiceschanged.html b/dom/media/webspeech/synth/test/startup/test_voiceschanged.html new file mode 100644 index 000000000..079938c35 --- /dev/null +++ b/dom/media/webspeech/synth/test/startup/test_voiceschanged.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1254378 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1254378: Emit onvoiceschanged when voices first added</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1254378">Mozilla Bug 1254378</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1254378 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] }, + function() { document.getElementById("testFrame").src = "file_voiceschanged.html"; }); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_bfcache.html b/dom/media/webspeech/synth/test/test_bfcache.html new file mode 100644 index 000000000..8681def3f --- /dev/null +++ b/dom/media/webspeech/synth/test/test_bfcache.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1230533 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1230533: Test speech is stopped from a window when unloaded</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1230533">Mozilla Bug 1230533</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +var iframe; + +function onDone() { + SimpleTest.finish(); +} + +SpecialPowers.pushPrefEnv({ set: [ + ['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', true], + ['browser.sessionhistory.cache_subframes', true], + ['browser.sessionhistory.max_total_viewers', 10]] }, + function() { + loadSpeechTest("file_bfcache_frame.html"); + }); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_global_queue.html b/dom/media/webspeech/synth/test/test_global_queue.html new file mode 100644 index 000000000..34d3a4ed3 --- /dev/null +++ b/dom/media/webspeech/synth/test/test_global_queue.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1188099 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1188099: Global queue should correctly schedule utterances</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', true]] }, + function() { loadSpeechTest("file_global_queue.html"); }); + +</script> +</pre> +</body> +</html>
\ No newline at end of file diff --git a/dom/media/webspeech/synth/test/test_global_queue_cancel.html b/dom/media/webspeech/synth/test/test_global_queue_cancel.html new file mode 100644 index 000000000..4c5c11634 --- /dev/null +++ b/dom/media/webspeech/synth/test/test_global_queue_cancel.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1188099 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1188099: Calling cancel() should work correctly with global queue</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', true]] }, + function() { loadSpeechTest("file_global_queue_cancel.html"); }); + +</script> +</pre> +</body> +</html>
\ No newline at end of file diff --git a/dom/media/webspeech/synth/test/test_global_queue_pause.html b/dom/media/webspeech/synth/test/test_global_queue_pause.html new file mode 100644 index 000000000..f5ac1b98c --- /dev/null +++ b/dom/media/webspeech/synth/test/test_global_queue_pause.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1188099 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1188099: Calling pause() should work correctly with global queue</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188099">Mozilla Bug 1188099</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', true]] }, + function() { loadSpeechTest("file_global_queue_pause.html"); }); + +</script> +</pre> +</body> +</html>
\ No newline at end of file diff --git a/dom/media/webspeech/synth/test/test_indirect_service_events.html b/dom/media/webspeech/synth/test/test_indirect_service_events.html new file mode 100644 index 000000000..d7f5ec424 --- /dev/null +++ b/dom/media/webspeech/synth/test/test_indirect_service_events.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1155034 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1155034: Check that indirect audio services dispatch their own events</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1155034">Mozilla Bug 1155034</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1155034 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', false]] }, + function() { loadSpeechTest("file_indirect_service_events.html"); }); + + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_setup.html b/dom/media/webspeech/synth/test/test_setup.html new file mode 100644 index 000000000..6286b6c2e --- /dev/null +++ b/dom/media/webspeech/synth/test/test_setup.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=525444 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 525444: Web Speech API check all classes are present</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] }, + function() { document.getElementById("testFrame").src = "file_setup.html"; }); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_speech_cancel.html b/dom/media/webspeech/synth/test/test_speech_cancel.html new file mode 100644 index 000000000..e7cf051ef --- /dev/null +++ b/dom/media/webspeech/synth/test/test_speech_cancel.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1150315 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1150315: Web Speech API check all classes are present</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1150315">Mozilla Bug 1150315</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1150315 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', false]] }, + function() { loadSpeechTest("file_speech_cancel.html"); }); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_speech_error.html b/dom/media/webspeech/synth/test/test_speech_error.html new file mode 100644 index 000000000..c4bfdc6c4 --- /dev/null +++ b/dom/media/webspeech/synth/test/test_speech_error.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1226015 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 1150315: Web Speech API check all classes are present</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1226015">Mozilla Bug 1226015</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 1226015 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', false]] }, + function() { loadSpeechTest("file_speech_error.html"); }); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_speech_queue.html b/dom/media/webspeech/synth/test/test_speech_queue.html new file mode 100644 index 000000000..ca652b243 --- /dev/null +++ b/dom/media/webspeech/synth/test/test_speech_queue.html @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<html lang="en-US"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=525444 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 525444: Web Speech API, check speech synth queue</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true], + ['media.webspeech.synth.force_global_queue', false]] }, + function() { + loadSpeechTest("file_speech_queue.html"); + }); + +</script> +</pre> +</body> +</html> diff --git a/dom/media/webspeech/synth/test/test_speech_simple.html b/dom/media/webspeech/synth/test/test_speech_simple.html new file mode 100644 index 000000000..2eb75af43 --- /dev/null +++ b/dom/media/webspeech/synth/test/test_speech_simple.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=650295 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 650295: Web Speech API check all classes are present</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a> +<p id="display"></p> +<iframe id="testFrame"></iframe> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 525444 **/ + +SimpleTest.waitForExplicitFinish(); + +SpecialPowers.pushPrefEnv( + { set: [['media.webspeech.synth.enabled', true]] }, + function() { loadSpeechTest("file_speech_simple.html"); }); + +</script> +</pre> +</body> +</html> |