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