Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | var gSpeechRegistry = SpecialPowers.Cc["@mozilla.org/synth-voice-registry;1"] |
michael@0 | 2 | .getService(SpecialPowers.Ci.nsISynthVoiceRegistry); |
michael@0 | 3 | |
michael@0 | 4 | var gAddedVoices = []; |
michael@0 | 5 | |
michael@0 | 6 | function SpeechTaskCallback(onpause, onresume, oncancel) { |
michael@0 | 7 | this.onpause = onpause; |
michael@0 | 8 | this.onresume = onresume; |
michael@0 | 9 | this.oncancel = oncancel; |
michael@0 | 10 | } |
michael@0 | 11 | |
michael@0 | 12 | SpeechTaskCallback.prototype = { |
michael@0 | 13 | QueryInterface: function(iid) { |
michael@0 | 14 | return this; |
michael@0 | 15 | }, |
michael@0 | 16 | |
michael@0 | 17 | getInterfaces: function(c) {}, |
michael@0 | 18 | |
michael@0 | 19 | getHelperForLanguage: function() {}, |
michael@0 | 20 | |
michael@0 | 21 | onPause: function onPause() { |
michael@0 | 22 | if (this.onpause) |
michael@0 | 23 | this.onpause(); |
michael@0 | 24 | }, |
michael@0 | 25 | |
michael@0 | 26 | onResume: function onResume() { |
michael@0 | 27 | if (this.onresume) |
michael@0 | 28 | this.onresume(); |
michael@0 | 29 | }, |
michael@0 | 30 | |
michael@0 | 31 | onCancel: function onCancel() { |
michael@0 | 32 | if (this.oncancel) |
michael@0 | 33 | this.oncancel(); |
michael@0 | 34 | } |
michael@0 | 35 | }; |
michael@0 | 36 | |
michael@0 | 37 | var TestSpeechServiceWithAudio = SpecialPowers.wrapCallbackObject({ |
michael@0 | 38 | CHANNELS: 1, |
michael@0 | 39 | SAMPLE_RATE: 16000, |
michael@0 | 40 | |
michael@0 | 41 | serviceType: SpecialPowers.Ci.nsISpeechService.SERVICETYPE_DIRECT_AUDIO, |
michael@0 | 42 | |
michael@0 | 43 | speak: function speak(aText, aUri, aRate, aPitch, aTask) { |
michael@0 | 44 | var task = SpecialPowers.wrap(aTask); |
michael@0 | 45 | |
michael@0 | 46 | window.setTimeout( |
michael@0 | 47 | function () { |
michael@0 | 48 | task.setup(SpecialPowers.wrapCallbackObject(new SpeechTaskCallback()), this.CHANNELS, this.SAMPLE_RATE); |
michael@0 | 49 | // 0.025 seconds per character. |
michael@0 | 50 | task.sendAudio(new Int16Array((this.SAMPLE_RATE/40)*aText.length), []); |
michael@0 | 51 | task.sendAudio(new Int16Array(0), []); |
michael@0 | 52 | }.bind(this), 0); |
michael@0 | 53 | }, |
michael@0 | 54 | |
michael@0 | 55 | QueryInterface: function(iid) { |
michael@0 | 56 | return this; |
michael@0 | 57 | }, |
michael@0 | 58 | |
michael@0 | 59 | getInterfaces: function(c) {}, |
michael@0 | 60 | |
michael@0 | 61 | getHelperForLanguage: function() {} |
michael@0 | 62 | }); |
michael@0 | 63 | |
michael@0 | 64 | var TestSpeechServiceNoAudio = SpecialPowers.wrapCallbackObject({ |
michael@0 | 65 | serviceType: SpecialPowers.Ci.nsISpeechService.SERVICETYPE_INDIRECT_AUDIO, |
michael@0 | 66 | |
michael@0 | 67 | speak: function speak(aText, aUri, aRate, aPitch, aTask) { |
michael@0 | 68 | var pair = this.expectedSpeaks.shift(); |
michael@0 | 69 | if (pair) { |
michael@0 | 70 | // XXX: These tests do not happen in OOP |
michael@0 | 71 | var utterance = pair[0]; |
michael@0 | 72 | var expected = pair[1]; |
michael@0 | 73 | |
michael@0 | 74 | is(aText, utterance.text, "Speak text matches utterance text"); |
michael@0 | 75 | |
michael@0 | 76 | var args = {uri: aUri, rate: aRate, pitch: aPitch}; |
michael@0 | 77 | |
michael@0 | 78 | for (var attr in args) { |
michael@0 | 79 | if (expected[attr] != undefined) |
michael@0 | 80 | is(args[attr], expected[attr], "expected service arg " + attr); |
michael@0 | 81 | } |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | var task = SpecialPowers.wrap(aTask); |
michael@0 | 85 | task.setup(SpecialPowers.wrapCallbackObject(new SpeechTaskCallback())); |
michael@0 | 86 | setTimeout(function () { |
michael@0 | 87 | task.dispatchStart(); |
michael@0 | 88 | setTimeout(function () { |
michael@0 | 89 | task.dispatchEnd(aText.length / 2.0, aText.length); |
michael@0 | 90 | }, 0); |
michael@0 | 91 | |
michael@0 | 92 | }, 0); |
michael@0 | 93 | }, |
michael@0 | 94 | |
michael@0 | 95 | QueryInterface: function(iid) { |
michael@0 | 96 | return this; |
michael@0 | 97 | }, |
michael@0 | 98 | |
michael@0 | 99 | getInterfaces: function(c) {}, |
michael@0 | 100 | |
michael@0 | 101 | getHelperForLanguage: function() {}, |
michael@0 | 102 | |
michael@0 | 103 | expectedSpeaks: [] |
michael@0 | 104 | }); |
michael@0 | 105 | |
michael@0 | 106 | function synthAddVoice(aServiceName, aName, aLang, aIsLocal) { |
michael@0 | 107 | if (SpecialPowers.isMainProcess()) { |
michael@0 | 108 | var voicesBefore = speechSynthesis.getVoices().length; |
michael@0 | 109 | var uri = "urn:moz-tts:mylittleservice:" + encodeURI(aName + '?' + aLang); |
michael@0 | 110 | gSpeechRegistry.addVoice(window[aServiceName], uri, aName, aLang, aIsLocal); |
michael@0 | 111 | |
michael@0 | 112 | gAddedVoices.push([window[aServiceName], uri]); |
michael@0 | 113 | var voicesAfter = speechSynthesis.getVoices().length; |
michael@0 | 114 | |
michael@0 | 115 | is(voicesBefore + 1, voicesAfter, "Voice added"); |
michael@0 | 116 | var voice = speechSynthesis.getVoices()[voicesAfter - 1]; |
michael@0 | 117 | is(voice.voiceURI, uri, "voice URI matches"); |
michael@0 | 118 | is(voice.name, aName, "voice name matches"); |
michael@0 | 119 | is(voice.lang, aLang, "voice lang matches"); |
michael@0 | 120 | is(voice.localService, aIsLocal, "voice localService matches"); |
michael@0 | 121 | |
michael@0 | 122 | return uri; |
michael@0 | 123 | } else { |
michael@0 | 124 | // XXX: It would be nice to check here that the child gets the voice |
michael@0 | 125 | // added update, but alas, it is aynchronous. |
michael@0 | 126 | var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] |
michael@0 | 127 | .getService(SpecialPowers.Ci.nsISyncMessageSender); |
michael@0 | 128 | |
michael@0 | 129 | return mm.sendSyncMessage( |
michael@0 | 130 | 'test:SpeechSynthesis:ipcSynthAddVoice', |
michael@0 | 131 | [aServiceName, aName, aLang, aIsLocal])[0]; |
michael@0 | 132 | } |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | function synthSetDefault(aUri, aIsDefault) { |
michael@0 | 136 | if (SpecialPowers.isMainProcess()) { |
michael@0 | 137 | gSpeechRegistry.setDefaultVoice(aUri, aIsDefault); |
michael@0 | 138 | var voices = speechSynthesis.getVoices(); |
michael@0 | 139 | for (var i in voices) { |
michael@0 | 140 | if (voices[i].voiceURI == aUri) |
michael@0 | 141 | ok(voices[i]['default'], "Voice set to default"); |
michael@0 | 142 | } |
michael@0 | 143 | } else { |
michael@0 | 144 | // XXX: It would be nice to check here that the child gets the voice |
michael@0 | 145 | // added update, but alas, it is aynchronous. |
michael@0 | 146 | var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] |
michael@0 | 147 | .getService(SpecialPowers.Ci.nsISyncMessageSender); |
michael@0 | 148 | |
michael@0 | 149 | return mm.sendSyncMessage( |
michael@0 | 150 | 'test:SpeechSynthesis:ipcSynthSetDefault', [aUri, aIsDefault])[0]; |
michael@0 | 151 | } |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | function synthCleanup() { |
michael@0 | 155 | if (SpecialPowers.isMainProcess()) { |
michael@0 | 156 | var voicesBefore = speechSynthesis.getVoices().length; |
michael@0 | 157 | var toRemove = gAddedVoices.length; |
michael@0 | 158 | var removeArgs; |
michael@0 | 159 | while ((removeArgs = gAddedVoices.shift())) |
michael@0 | 160 | gSpeechRegistry.removeVoice.apply(gSpeechRegistry.removeVoice, removeArgs); |
michael@0 | 161 | |
michael@0 | 162 | var voicesAfter = speechSynthesis.getVoices().length; |
michael@0 | 163 | is(voicesAfter, voicesBefore - toRemove, "Successfully removed test voices"); |
michael@0 | 164 | } else { |
michael@0 | 165 | // XXX: It would be nice to check here that the child gets the voice |
michael@0 | 166 | // removed update, but alas, it is aynchronous. |
michael@0 | 167 | var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"] |
michael@0 | 168 | .getService(SpecialPowers.Ci.nsISyncMessageSender); |
michael@0 | 169 | mm.sendSyncMessage('test:SpeechSynthesis:ipcSynthCleanup'); |
michael@0 | 170 | } |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | function synthTestQueue(aTestArgs, aEndFunc) { |
michael@0 | 174 | var utterances = []; |
michael@0 | 175 | for (var i in aTestArgs) { |
michael@0 | 176 | var uargs = aTestArgs[i][0]; |
michael@0 | 177 | var u = new SpeechSynthesisUtterance(uargs.text); |
michael@0 | 178 | |
michael@0 | 179 | delete uargs.text; |
michael@0 | 180 | |
michael@0 | 181 | for (var attr in uargs) |
michael@0 | 182 | u[attr] = uargs[attr]; |
michael@0 | 183 | |
michael@0 | 184 | function onend_handler(e) { |
michael@0 | 185 | is(e.target, utterances.shift(), "Target matches utterances"); |
michael@0 | 186 | ok(!speechSynthesis.speaking, "speechSynthesis is not speaking."); |
michael@0 | 187 | |
michael@0 | 188 | isnot(e.eventType, 'error', "Error in utterance"); |
michael@0 | 189 | |
michael@0 | 190 | if (utterances.length) { |
michael@0 | 191 | ok(speechSynthesis.pending, "other utterances queued"); |
michael@0 | 192 | } else { |
michael@0 | 193 | ok(!speechSynthesis.pending, "queue is empty, nothing pending."); |
michael@0 | 194 | if (aEndFunc) |
michael@0 | 195 | aEndFunc(); |
michael@0 | 196 | } |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | u.addEventListener('end', onend_handler); |
michael@0 | 200 | u.addEventListener('error', onend_handler); |
michael@0 | 201 | |
michael@0 | 202 | u.addEventListener( |
michael@0 | 203 | 'error', function onerror_handler(e) { |
michael@0 | 204 | ok(false, "Error in speech utterance '" + e.target.text + "'"); |
michael@0 | 205 | }); |
michael@0 | 206 | |
michael@0 | 207 | utterances.push(u); |
michael@0 | 208 | TestSpeechServiceNoAudio.expectedSpeaks.push([u, aTestArgs[i][1]]); |
michael@0 | 209 | speechSynthesis.speak(u); |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet."); |
michael@0 | 213 | ok(speechSynthesis.pending, "speechSynthesis has an utterance queued."); |
michael@0 | 214 | } |