1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/renderer/speech_recognition_dispatcher.h" 6 7 #include "base/basictypes.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "content/common/speech_recognition_messages.h" 10 #include "content/renderer/render_view_impl.h" 11 #include "third_party/WebKit/public/platform/WebString.h" 12 #include "third_party/WebKit/public/platform/WebVector.h" 13 #include "third_party/WebKit/public/web/WebSpeechGrammar.h" 14 #include "third_party/WebKit/public/web/WebSpeechRecognitionParams.h" 15 #include "third_party/WebKit/public/web/WebSpeechRecognitionResult.h" 16 #include "third_party/WebKit/public/web/WebSpeechRecognizerClient.h" 17 18 using blink::WebVector; 19 using blink::WebString; 20 using blink::WebSpeechGrammar; 21 using blink::WebSpeechRecognitionHandle; 22 using blink::WebSpeechRecognitionResult; 23 using blink::WebSpeechRecognitionParams; 24 using blink::WebSpeechRecognizerClient; 25 26 namespace content { 27 28 SpeechRecognitionDispatcher::SpeechRecognitionDispatcher( 29 RenderViewImpl* render_view) 30 : RenderViewObserver(render_view), 31 recognizer_client_(NULL), 32 next_id_(1) { 33 } 34 35 SpeechRecognitionDispatcher::~SpeechRecognitionDispatcher() { 36 } 37 38 void SpeechRecognitionDispatcher::AbortAllRecognitions() { 39 Send(new SpeechRecognitionHostMsg_AbortAllRequests( 40 routing_id())); 41 } 42 43 bool SpeechRecognitionDispatcher::OnMessageReceived( 44 const IPC::Message& message) { 45 bool handled = true; 46 IPC_BEGIN_MESSAGE_MAP(SpeechRecognitionDispatcher, message) 47 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_Started, OnRecognitionStarted) 48 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_AudioStarted, OnAudioStarted) 49 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_SoundStarted, OnSoundStarted) 50 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_SoundEnded, OnSoundEnded) 51 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_AudioEnded, OnAudioEnded) 52 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_ErrorOccurred, OnErrorOccurred) 53 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_Ended, OnRecognitionEnded) 54 IPC_MESSAGE_HANDLER(SpeechRecognitionMsg_ResultRetrieved, 55 OnResultsRetrieved) 56 IPC_MESSAGE_UNHANDLED(handled = false) 57 IPC_END_MESSAGE_MAP() 58 return handled; 59 } 60 61 void SpeechRecognitionDispatcher::start( 62 const WebSpeechRecognitionHandle& handle, 63 const WebSpeechRecognitionParams& params, 64 WebSpeechRecognizerClient* recognizer_client) { 65 DCHECK(!recognizer_client_ || recognizer_client_ == recognizer_client); 66 recognizer_client_ = recognizer_client; 67 68 SpeechRecognitionHostMsg_StartRequest_Params msg_params; 69 for (size_t i = 0; i < params.grammars().size(); ++i) { 70 const WebSpeechGrammar& grammar = params.grammars()[i]; 71 msg_params.grammars.push_back( 72 SpeechRecognitionGrammar(grammar.src().spec(), grammar.weight())); 73 } 74 msg_params.language = base::UTF16ToUTF8(params.language()); 75 msg_params.max_hypotheses = static_cast<uint32>(params.maxAlternatives()); 76 msg_params.continuous = params.continuous(); 77 msg_params.interim_results = params.interimResults(); 78 msg_params.origin_url = params.origin().toString().utf8(); 79 msg_params.render_view_id = routing_id(); 80 msg_params.request_id = GetOrCreateIDForHandle(handle); 81 // The handle mapping will be removed in |OnRecognitionEnd|. 82 Send(new SpeechRecognitionHostMsg_StartRequest(msg_params)); 83 } 84 85 void SpeechRecognitionDispatcher::stop( 86 const WebSpeechRecognitionHandle& handle, 87 WebSpeechRecognizerClient* recognizer_client) { 88 // Ignore a |stop| issued without a matching |start|. 89 if (recognizer_client_ != recognizer_client || !HandleExists(handle)) 90 return; 91 Send(new SpeechRecognitionHostMsg_StopCaptureRequest( 92 routing_id(), GetOrCreateIDForHandle(handle))); 93 } 94 95 void SpeechRecognitionDispatcher::abort( 96 const WebSpeechRecognitionHandle& handle, 97 WebSpeechRecognizerClient* recognizer_client) { 98 // Ignore an |abort| issued without a matching |start|. 99 if (recognizer_client_ != recognizer_client || !HandleExists(handle)) 100 return; 101 Send(new SpeechRecognitionHostMsg_AbortRequest( 102 routing_id(), GetOrCreateIDForHandle(handle))); 103 } 104 105 void SpeechRecognitionDispatcher::OnRecognitionStarted(int request_id) { 106 recognizer_client_->didStart(GetHandleFromID(request_id)); 107 } 108 109 void SpeechRecognitionDispatcher::OnAudioStarted(int request_id) { 110 recognizer_client_->didStartAudio(GetHandleFromID(request_id)); 111 } 112 113 void SpeechRecognitionDispatcher::OnSoundStarted(int request_id) { 114 recognizer_client_->didStartSound(GetHandleFromID(request_id)); 115 } 116 117 void SpeechRecognitionDispatcher::OnSoundEnded(int request_id) { 118 recognizer_client_->didEndSound(GetHandleFromID(request_id)); 119 } 120 121 void SpeechRecognitionDispatcher::OnAudioEnded(int request_id) { 122 recognizer_client_->didEndAudio(GetHandleFromID(request_id)); 123 } 124 125 static WebSpeechRecognizerClient::ErrorCode WebKitErrorCode( 126 SpeechRecognitionErrorCode e) { 127 switch (e) { 128 case SPEECH_RECOGNITION_ERROR_NONE: 129 NOTREACHED(); 130 return WebSpeechRecognizerClient::OtherError; 131 case SPEECH_RECOGNITION_ERROR_ABORTED: 132 return WebSpeechRecognizerClient::AbortedError; 133 case SPEECH_RECOGNITION_ERROR_AUDIO: 134 return WebSpeechRecognizerClient::AudioCaptureError; 135 case SPEECH_RECOGNITION_ERROR_NETWORK: 136 return WebSpeechRecognizerClient::NetworkError; 137 case SPEECH_RECOGNITION_ERROR_NOT_ALLOWED: 138 return WebSpeechRecognizerClient::NotAllowedError; 139 case SPEECH_RECOGNITION_ERROR_NO_SPEECH: 140 return WebSpeechRecognizerClient::NoSpeechError; 141 case SPEECH_RECOGNITION_ERROR_NO_MATCH: 142 NOTREACHED(); 143 return WebSpeechRecognizerClient::OtherError; 144 case SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR: 145 return WebSpeechRecognizerClient::BadGrammarError; 146 } 147 NOTREACHED(); 148 return WebSpeechRecognizerClient::OtherError; 149 } 150 151 void SpeechRecognitionDispatcher::OnErrorOccurred( 152 int request_id, const SpeechRecognitionError& error) { 153 if (error.code == SPEECH_RECOGNITION_ERROR_NO_MATCH) { 154 recognizer_client_->didReceiveNoMatch(GetHandleFromID(request_id), 155 WebSpeechRecognitionResult()); 156 } else { 157 recognizer_client_->didReceiveError( 158 GetHandleFromID(request_id), 159 WebString(), // TODO(primiano): message? 160 WebKitErrorCode(error.code)); 161 } 162 } 163 164 void SpeechRecognitionDispatcher::OnRecognitionEnded(int request_id) { 165 // TODO(tommi): It is possible that the handle isn't found in the array if 166 // the user just refreshed the page. It seems that we then get a notification 167 // for the previously loaded instance of the page. 168 HandleMap::iterator iter = handle_map_.find(request_id); 169 if (iter == handle_map_.end()) { 170 DLOG(ERROR) << "OnRecognitionEnded called for a handle that doesn't exist"; 171 } else { 172 WebSpeechRecognitionHandle handle = iter->second; 173 // Note: we need to erase the handle from the map *before* calling didEnd. 174 // didEnd may call back synchronously to start a new recognition session, 175 // and we don't want to delete the handle from the map after that happens. 176 handle_map_.erase(request_id); 177 recognizer_client_->didEnd(handle); 178 } 179 } 180 181 void SpeechRecognitionDispatcher::OnResultsRetrieved( 182 int request_id, const SpeechRecognitionResults& results) { 183 size_t provisional_count = 0; 184 SpeechRecognitionResults::const_iterator it = results.begin(); 185 for (; it != results.end(); ++it) { 186 if (it->is_provisional) 187 ++provisional_count; 188 } 189 190 WebVector<WebSpeechRecognitionResult> provisional(provisional_count); 191 WebVector<WebSpeechRecognitionResult> final( 192 results.size() - provisional_count); 193 194 int provisional_index = 0, final_index = 0; 195 for (it = results.begin(); it != results.end(); ++it) { 196 const SpeechRecognitionResult& result = (*it); 197 WebSpeechRecognitionResult* webkit_result = result.is_provisional ? 198 &provisional[provisional_index++] : &final[final_index++]; 199 200 const size_t num_hypotheses = result.hypotheses.size(); 201 WebVector<WebString> transcripts(num_hypotheses); 202 WebVector<float> confidences(num_hypotheses); 203 for (size_t i = 0; i < num_hypotheses; ++i) { 204 transcripts[i] = result.hypotheses[i].utterance; 205 confidences[i] = static_cast<float>(result.hypotheses[i].confidence); 206 } 207 webkit_result->assign(transcripts, confidences, !result.is_provisional); 208 } 209 210 recognizer_client_->didReceiveResults( 211 GetHandleFromID(request_id), final, provisional); 212 } 213 214 215 int SpeechRecognitionDispatcher::GetOrCreateIDForHandle( 216 const WebSpeechRecognitionHandle& handle) { 217 // Search first for an existing mapping. 218 for (HandleMap::iterator iter = handle_map_.begin(); 219 iter != handle_map_.end(); 220 ++iter) { 221 if (iter->second.equals(handle)) 222 return iter->first; 223 } 224 // If no existing mapping found, create a new one. 225 const int new_id = next_id_; 226 handle_map_[new_id] = handle; 227 ++next_id_; 228 return new_id; 229 } 230 231 bool SpeechRecognitionDispatcher::HandleExists( 232 const WebSpeechRecognitionHandle& handle) { 233 for (HandleMap::iterator iter = handle_map_.begin(); 234 iter != handle_map_.end(); 235 ++iter) { 236 if (iter->second.equals(handle)) 237 return true; 238 } 239 return false; 240 } 241 242 const WebSpeechRecognitionHandle& SpeechRecognitionDispatcher::GetHandleFromID( 243 int request_id) { 244 HandleMap::iterator iter = handle_map_.find(request_id); 245 DCHECK(iter != handle_map_.end()); 246 return iter->second; 247 } 248 249 } // namespace content 250