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/browser/speech/input_tag_speech_dispatcher_host.h" 6 7#include "base/bind.h" 8#include "base/lazy_instance.h" 9#include "content/browser/browser_plugin/browser_plugin_guest.h" 10#include "content/browser/renderer_host/render_view_host_impl.h" 11#include "content/browser/speech/speech_recognition_manager_impl.h" 12#include "content/browser/web_contents/web_contents_impl.h" 13#include "content/common/speech_recognition_messages.h" 14#include "content/public/browser/speech_recognition_manager_delegate.h" 15#include "content/public/browser/speech_recognition_session_config.h" 16#include "content/public/browser/speech_recognition_session_context.h" 17 18namespace { 19const uint32 kMaxHypothesesForSpeechInputTag = 6; 20} 21 22namespace content { 23 24InputTagSpeechDispatcherHost::InputTagSpeechDispatcherHost( 25 bool is_guest, 26 int render_process_id, 27 net::URLRequestContextGetter* url_request_context_getter) 28 : is_guest_(is_guest), 29 render_process_id_(render_process_id), 30 url_request_context_getter_(url_request_context_getter), 31 weak_factory_(this) { 32 // Do not add any non-trivial initialization here, instead do it lazily when 33 // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or 34 // add an Init() method. 35} 36 37InputTagSpeechDispatcherHost::~InputTagSpeechDispatcherHost() { 38 SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderProcess( 39 render_process_id_); 40} 41 42base::WeakPtr<InputTagSpeechDispatcherHost> 43InputTagSpeechDispatcherHost::AsWeakPtr() { 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 45 return weak_factory_.GetWeakPtr(); 46} 47 48bool InputTagSpeechDispatcherHost::OnMessageReceived( 49 const IPC::Message& message, bool* message_was_ok) { 50 bool handled = true; 51 IPC_BEGIN_MESSAGE_MAP_EX(InputTagSpeechDispatcherHost, message, 52 *message_was_ok) 53 IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StartRecognition, 54 OnStartRecognition) 55 IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_CancelRecognition, 56 OnCancelRecognition) 57 IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StopRecording, 58 OnStopRecording) 59 IPC_MESSAGE_UNHANDLED(handled = false) 60 IPC_END_MESSAGE_MAP() 61 return handled; 62} 63 64void InputTagSpeechDispatcherHost::OverrideThreadForMessage( 65 const IPC::Message& message, 66 BrowserThread::ID* thread) { 67 if (message.type() == InputTagSpeechHostMsg_StartRecognition::ID) 68 *thread = BrowserThread::UI; 69} 70 71void InputTagSpeechDispatcherHost::OnChannelClosing() { 72 weak_factory_.InvalidateWeakPtrs(); 73} 74 75void InputTagSpeechDispatcherHost::OnStartRecognition( 76 const InputTagSpeechHostMsg_StartRecognition_Params& params) { 77 InputTagSpeechHostMsg_StartRecognition_Params input_params(params); 78 int render_process_id = render_process_id_; 79 // The chrome layer is mostly oblivious to BrowserPlugin guests and so it 80 // cannot correctly place the speech bubble relative to a guest. Thus, we 81 // set up the speech recognition context relative to the embedder. 82 int guest_render_view_id = MSG_ROUTING_NONE; 83 if (is_guest_) { 84 RenderViewHostImpl* render_view_host = 85 RenderViewHostImpl::FromID(render_process_id_, params.render_view_id); 86 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( 87 WebContents::FromRenderViewHost(render_view_host)); 88 BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest(); 89 input_params.element_rect.set_origin( 90 guest->GetScreenCoordinates(input_params.element_rect.origin())); 91 guest_render_view_id = params.render_view_id; 92 render_process_id = 93 guest->embedder_web_contents()->GetRenderProcessHost()->GetID(); 94 input_params.render_view_id = 95 guest->embedder_web_contents()->GetRoutingID(); 96 } 97 bool filter_profanities = 98 SpeechRecognitionManagerImpl::GetInstance() && 99 SpeechRecognitionManagerImpl::GetInstance()->delegate() && 100 SpeechRecognitionManagerImpl::GetInstance()->delegate()-> 101 FilterProfanities(render_process_id_); 102 103 BrowserThread::PostTask( 104 BrowserThread::IO, FROM_HERE, 105 base::Bind( 106 &InputTagSpeechDispatcherHost::StartRecognitionOnIO, 107 this, 108 render_process_id, 109 guest_render_view_id, 110 input_params, 111 filter_profanities)); 112} 113 114void InputTagSpeechDispatcherHost::StartRecognitionOnIO( 115 int render_process_id, 116 int guest_render_view_id, 117 const InputTagSpeechHostMsg_StartRecognition_Params& params, 118 bool filter_profanities) { 119 SpeechRecognitionSessionContext context; 120 context.render_process_id = render_process_id; 121 context.render_view_id = params.render_view_id; 122 context.guest_render_view_id = guest_render_view_id; 123 // Keep context.embedder_render_process_id and context.embedder_render_view_id 124 // unset. 125 context.request_id = params.request_id; 126 context.element_rect = params.element_rect; 127 128 SpeechRecognitionSessionConfig config; 129 config.language = params.language; 130 if (!params.grammar.empty()) { 131 config.grammars.push_back(SpeechRecognitionGrammar(params.grammar)); 132 } 133 config.max_hypotheses = kMaxHypothesesForSpeechInputTag; 134 config.origin_url = params.origin_url; 135 config.initial_context = context; 136 config.url_request_context_getter = url_request_context_getter_.get(); 137 config.filter_profanities = filter_profanities; 138 config.event_listener = AsWeakPtr(); 139 140 int session_id = SpeechRecognitionManager::GetInstance()->CreateSession( 141 config); 142 DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid); 143 SpeechRecognitionManager::GetInstance()->StartSession(session_id); 144} 145 146void InputTagSpeechDispatcherHost::OnCancelRecognition(int render_view_id, 147 int request_id) { 148 int session_id = SpeechRecognitionManager::GetInstance()->GetSession( 149 render_process_id_, render_view_id, request_id); 150 151 // The renderer might provide an invalid |request_id| if the session was not 152 // started as expected, e.g., due to unsatisfied security requirements. 153 if (session_id != SpeechRecognitionManager::kSessionIDInvalid) 154 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); 155} 156 157void InputTagSpeechDispatcherHost::OnStopRecording(int render_view_id, 158 int request_id) { 159 int session_id = SpeechRecognitionManager::GetInstance()->GetSession( 160 render_process_id_, render_view_id, request_id); 161 162 // The renderer might provide an invalid |request_id| if the session was not 163 // started as expected, e.g., due to unsatisfied security requirements. 164 if (session_id != SpeechRecognitionManager::kSessionIDInvalid) { 165 SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession( 166 session_id); 167 } 168} 169 170// -------- SpeechRecognitionEventListener interface implementation ----------- 171void InputTagSpeechDispatcherHost::OnRecognitionResults( 172 int session_id, 173 const SpeechRecognitionResults& results) { 174 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionResults enter"; 175 176 const SpeechRecognitionSessionContext& context = 177 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 178 179 int render_view_id = 180 context.guest_render_view_id == MSG_ROUTING_NONE ? 181 context.render_view_id : context.guest_render_view_id; 182 Send(new InputTagSpeechMsg_SetRecognitionResults( 183 render_view_id, 184 context.request_id, 185 results)); 186 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionResults exit"; 187} 188 189void InputTagSpeechDispatcherHost::OnAudioEnd(int session_id) { 190 DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd enter"; 191 192 const SpeechRecognitionSessionContext& context = 193 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 194 int render_view_id = 195 context.guest_render_view_id == MSG_ROUTING_NONE ? 196 context.render_view_id : context.guest_render_view_id; 197 Send(new InputTagSpeechMsg_RecordingComplete(render_view_id, 198 context.request_id)); 199 DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd exit"; 200} 201 202void InputTagSpeechDispatcherHost::OnRecognitionEnd(int session_id) { 203 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd enter"; 204 const SpeechRecognitionSessionContext& context = 205 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 206 int render_view_id = 207 context.guest_render_view_id == MSG_ROUTING_NONE ? 208 context.render_view_id : context.guest_render_view_id; 209 Send(new InputTagSpeechMsg_RecognitionComplete(render_view_id, 210 context.request_id)); 211 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd exit"; 212} 213 214// The events below are currently not used by x-webkit-speech implementation. 215void InputTagSpeechDispatcherHost::OnRecognitionStart(int session_id) {} 216void InputTagSpeechDispatcherHost::OnAudioStart(int session_id) {} 217void InputTagSpeechDispatcherHost::OnSoundStart(int session_id) {} 218void InputTagSpeechDispatcherHost::OnSoundEnd(int session_id) {} 219void InputTagSpeechDispatcherHost::OnRecognitionError( 220 int session_id, 221 const SpeechRecognitionError& error) {} 222void InputTagSpeechDispatcherHost::OnAudioLevelsChange( 223 int session_id, float volume, float noise_volume) {} 224void InputTagSpeechDispatcherHost::OnEnvironmentEstimationComplete( 225 int session_id) {} 226 227} // namespace content 228