Home | History | Annotate | Download | only in renderer
      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