1 /* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "modules/speech/SpeechSynthesis.h" 28 29 #include "bindings/v8/ExceptionState.h" 30 #include "core/dom/ExecutionContext.h" 31 #include "modules/speech/SpeechSynthesisEvent.h" 32 #include "platform/speech/PlatformSpeechSynthesisVoice.h" 33 #include "wtf/CurrentTime.h" 34 35 namespace WebCore { 36 37 SpeechSynthesis* SpeechSynthesis::create(ExecutionContext* context) 38 { 39 return adoptRefCountedGarbageCollectedWillBeNoop(new SpeechSynthesis(context)); 40 } 41 42 SpeechSynthesis::SpeechSynthesis(ExecutionContext* context) 43 : ContextLifecycleObserver(context) 44 , m_platformSpeechSynthesizer(PlatformSpeechSynthesizer::create(this)) 45 , m_isPaused(false) 46 { 47 ScriptWrappable::init(this); 48 } 49 50 void SpeechSynthesis::setPlatformSynthesizer(PlatformSpeechSynthesizer* synthesizer) 51 { 52 m_platformSpeechSynthesizer = synthesizer; 53 } 54 55 ExecutionContext* SpeechSynthesis::executionContext() const 56 { 57 return ContextLifecycleObserver::executionContext(); 58 } 59 60 void SpeechSynthesis::voicesDidChange() 61 { 62 m_voiceList.clear(); 63 if (executionContext() && !executionContext()->activeDOMObjectsAreStopped()) 64 dispatchEvent(Event::create(EventTypeNames::voiceschanged)); 65 } 66 67 const HeapVector<Member<SpeechSynthesisVoice> >& SpeechSynthesis::getVoices() 68 { 69 if (m_voiceList.size()) 70 return m_voiceList; 71 72 // If the voiceList is empty, that's the cue to get the voices from the platform again. 73 const HeapVector<Member<PlatformSpeechSynthesisVoice> >& platformVoices = m_platformSpeechSynthesizer->voiceList(); 74 size_t voiceCount = platformVoices.size(); 75 for (size_t k = 0; k < voiceCount; k++) 76 m_voiceList.append(SpeechSynthesisVoice::create(platformVoices[k].get())); 77 78 return m_voiceList; 79 } 80 81 bool SpeechSynthesis::speaking() const 82 { 83 // If we have a current speech utterance, then that means we're assumed to be in a speaking state. 84 // This state is independent of whether the utterance happens to be paused. 85 return currentSpeechUtterance(); 86 } 87 88 bool SpeechSynthesis::pending() const 89 { 90 // This is true if there are any utterances that have not started. 91 // That means there will be more than one in the queue. 92 return m_utteranceQueue.size() > 1; 93 } 94 95 bool SpeechSynthesis::paused() const 96 { 97 return m_isPaused; 98 } 99 100 void SpeechSynthesis::startSpeakingImmediately() 101 { 102 SpeechSynthesisUtterance* utterance = currentSpeechUtterance(); 103 ASSERT(utterance); 104 105 utterance->setStartTime(monotonicallyIncreasingTime()); 106 m_isPaused = false; 107 m_platformSpeechSynthesizer->speak(utterance->platformUtterance()); 108 } 109 110 void SpeechSynthesis::speak(SpeechSynthesisUtterance* utterance, ExceptionState& exceptionState) 111 { 112 if (!utterance) { 113 exceptionState.throwTypeError("Invalid utterance argument"); 114 return; 115 } 116 117 m_utteranceQueue.append(utterance); 118 119 // If the queue was empty, speak this immediately. 120 if (m_utteranceQueue.size() == 1) 121 startSpeakingImmediately(); 122 } 123 124 void SpeechSynthesis::cancel() 125 { 126 // Remove all the items from the utterance queue. The platform 127 // may still have references to some of these utterances and may 128 // fire events on them asynchronously. 129 m_utteranceQueue.clear(); 130 m_platformSpeechSynthesizer->cancel(); 131 } 132 133 void SpeechSynthesis::pause() 134 { 135 if (!m_isPaused) 136 m_platformSpeechSynthesizer->pause(); 137 } 138 139 void SpeechSynthesis::resume() 140 { 141 if (!currentSpeechUtterance()) 142 return; 143 m_platformSpeechSynthesizer->resume(); 144 } 145 146 void SpeechSynthesis::fireEvent(const AtomicString& type, SpeechSynthesisUtterance* utterance, unsigned long charIndex, const String& name) 147 { 148 if (executionContext() && !executionContext()->activeDOMObjectsAreStopped()) 149 utterance->dispatchEvent(SpeechSynthesisEvent::create(type, charIndex, (currentTime() - utterance->startTime()), name)); 150 } 151 152 void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance* utterance, bool errorOccurred) 153 { 154 ASSERT(utterance); 155 156 bool didJustFinishCurrentUtterance = false; 157 // If the utterance that completed was the one we're currently speaking, 158 // remove it from the queue and start speaking the next one. 159 if (utterance == currentSpeechUtterance()) { 160 m_utteranceQueue.removeFirst(); 161 didJustFinishCurrentUtterance = true; 162 } 163 164 // Always fire the event, because the platform may have asynchronously 165 // sent an event on an utterance before it got the message that we 166 // canceled it, and we should always report to the user what actually 167 // happened. 168 fireEvent(errorOccurred ? EventTypeNames::error : EventTypeNames::end, utterance, 0, String()); 169 170 // Start the next utterance if we just finished one and one was pending. 171 if (didJustFinishCurrentUtterance && !m_utteranceQueue.isEmpty()) 172 startSpeakingImmediately(); 173 } 174 175 void SpeechSynthesis::boundaryEventOccurred(PlatformSpeechSynthesisUtterance* utterance, SpeechBoundary boundary, unsigned charIndex) 176 { 177 DEFINE_STATIC_LOCAL(const String, wordBoundaryString, ("word")); 178 DEFINE_STATIC_LOCAL(const String, sentenceBoundaryString, ("sentence")); 179 180 switch (boundary) { 181 case SpeechWordBoundary: 182 fireEvent(EventTypeNames::boundary, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, wordBoundaryString); 183 break; 184 case SpeechSentenceBoundary: 185 fireEvent(EventTypeNames::boundary, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, sentenceBoundaryString); 186 break; 187 default: 188 ASSERT_NOT_REACHED(); 189 } 190 } 191 192 void SpeechSynthesis::didStartSpeaking(PlatformSpeechSynthesisUtterance* utterance) 193 { 194 if (utterance->client()) 195 fireEvent(EventTypeNames::start, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String()); 196 } 197 198 void SpeechSynthesis::didPauseSpeaking(PlatformSpeechSynthesisUtterance* utterance) 199 { 200 m_isPaused = true; 201 if (utterance->client()) 202 fireEvent(EventTypeNames::pause, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String()); 203 } 204 205 void SpeechSynthesis::didResumeSpeaking(PlatformSpeechSynthesisUtterance* utterance) 206 { 207 m_isPaused = false; 208 if (utterance->client()) 209 fireEvent(EventTypeNames::resume, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String()); 210 } 211 212 void SpeechSynthesis::didFinishSpeaking(PlatformSpeechSynthesisUtterance* utterance) 213 { 214 if (utterance->client()) 215 handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), false); 216 } 217 218 void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance* utterance) 219 { 220 if (utterance->client()) 221 handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), true); 222 } 223 224 SpeechSynthesisUtterance* SpeechSynthesis::currentSpeechUtterance() const 225 { 226 if (!m_utteranceQueue.isEmpty()) 227 return m_utteranceQueue.first().get(); 228 return 0; 229 } 230 231 const AtomicString& SpeechSynthesis::interfaceName() const 232 { 233 return EventTargetNames::SpeechSynthesis; 234 } 235 236 void SpeechSynthesis::trace(Visitor* visitor) 237 { 238 visitor->trace(m_platformSpeechSynthesizer); 239 visitor->trace(m_voiceList); 240 visitor->trace(m_utteranceQueue); 241 PlatformSpeechSynthesizerClient::trace(visitor); 242 EventTargetWithInlineData::trace(visitor); 243 } 244 245 } // namespace WebCore 246