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/speech_recognition_dispatcher_host.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/lazy_instance.h" 10#include "content/browser/browser_plugin/browser_plugin_guest.h" 11#include "content/browser/child_process_security_policy_impl.h" 12#include "content/browser/renderer_host/render_view_host_impl.h" 13#include "content/browser/speech/speech_recognition_manager_impl.h" 14#include "content/browser/web_contents/web_contents_impl.h" 15#include "content/common/speech_recognition_messages.h" 16#include "content/public/browser/speech_recognition_manager_delegate.h" 17#include "content/public/browser/speech_recognition_session_config.h" 18#include "content/public/browser/speech_recognition_session_context.h" 19#include "content/public/common/content_switches.h" 20 21namespace content { 22 23SpeechRecognitionDispatcherHost::SpeechRecognitionDispatcherHost( 24 int render_process_id, 25 net::URLRequestContextGetter* context_getter) 26 : BrowserMessageFilter(SpeechRecognitionMsgStart), 27 render_process_id_(render_process_id), 28 context_getter_(context_getter), 29 weak_factory_(this) { 30 // Do not add any non-trivial initialization here, instead do it lazily when 31 // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or 32 // add an Init() method. 33} 34 35SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() { 36 SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderProcess( 37 render_process_id_); 38} 39 40base::WeakPtr<SpeechRecognitionDispatcherHost> 41SpeechRecognitionDispatcherHost::AsWeakPtr() { 42 return weak_factory_.GetWeakPtr(); 43} 44 45bool SpeechRecognitionDispatcherHost::OnMessageReceived( 46 const IPC::Message& message) { 47 bool handled = true; 48 IPC_BEGIN_MESSAGE_MAP(SpeechRecognitionDispatcherHost, message) 49 IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StartRequest, 50 OnStartRequest) 51 IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortRequest, 52 OnAbortRequest) 53 IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StopCaptureRequest, 54 OnStopCaptureRequest) 55 IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortAllRequests, 56 OnAbortAllRequests) 57 IPC_MESSAGE_UNHANDLED(handled = false) 58 IPC_END_MESSAGE_MAP() 59 return handled; 60} 61 62void SpeechRecognitionDispatcherHost::OverrideThreadForMessage( 63 const IPC::Message& message, 64 BrowserThread::ID* thread) { 65 if (message.type() == SpeechRecognitionHostMsg_StartRequest::ID) 66 *thread = BrowserThread::UI; 67} 68 69void SpeechRecognitionDispatcherHost::OnChannelClosing() { 70 weak_factory_.InvalidateWeakPtrs(); 71} 72 73void SpeechRecognitionDispatcherHost::OnStartRequest( 74 const SpeechRecognitionHostMsg_StartRequest_Params& params) { 75 SpeechRecognitionHostMsg_StartRequest_Params input_params(params); 76 77 // Check that the origin specified by the renderer process is one 78 // that it is allowed to access. 79 if (params.origin_url != "null" && 80 !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL( 81 render_process_id_, GURL(params.origin_url))) { 82 LOG(ERROR) << "SRDH::OnStartRequest, disallowed origin: " 83 << params.origin_url; 84 return; 85 } 86 87 int embedder_render_process_id = 0; 88 int embedder_render_view_id = MSG_ROUTING_NONE; 89 RenderViewHostImpl* render_view_host = 90 RenderViewHostImpl::FromID(render_process_id_, params.render_view_id); 91 if (!render_view_host) { 92 // RVH can be null if the tab was closed while continuous mode speech 93 // recognition was running. This seems to happen on mac. 94 LOG(WARNING) << "SRDH::OnStartRequest, RenderViewHost does not exist"; 95 return; 96 } 97 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( 98 WebContents::FromRenderViewHost(render_view_host)); 99 BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest(); 100 if (guest) { 101 // If the speech API request was from a guest, save the context of the 102 // embedder since we will use it to decide permission. 103 embedder_render_process_id = 104 guest->embedder_web_contents()->GetRenderProcessHost()->GetID(); 105 DCHECK_NE(embedder_render_process_id, 0); 106 embedder_render_view_id = 107 guest->embedder_web_contents()->GetRenderViewHost()->GetRoutingID(); 108 DCHECK_NE(embedder_render_view_id, MSG_ROUTING_NONE); 109 } 110 111 // TODO(lazyboy): Check if filter_profanities should use |render_process_id| 112 // instead of |render_process_id_|. 113 bool filter_profanities = 114 SpeechRecognitionManagerImpl::GetInstance() && 115 SpeechRecognitionManagerImpl::GetInstance()->delegate() && 116 SpeechRecognitionManagerImpl::GetInstance()->delegate()-> 117 FilterProfanities(render_process_id_); 118 119 // TODO(miu): This is a hack to allow SpeechRecognition to operate with the 120 // MediaStreamManager, which partitions requests per RenderFrame, not per 121 // RenderView. http://crbug.com/390749 122 const int params_render_frame_id = render_view_host ? 123 render_view_host->GetMainFrame()->GetRoutingID() : MSG_ROUTING_NONE; 124 125 BrowserThread::PostTask( 126 BrowserThread::IO, 127 FROM_HERE, 128 base::Bind(&SpeechRecognitionDispatcherHost::OnStartRequestOnIO, 129 this, 130 embedder_render_process_id, 131 embedder_render_view_id, 132 input_params, 133 params_render_frame_id, 134 filter_profanities)); 135} 136 137void SpeechRecognitionDispatcherHost::OnStartRequestOnIO( 138 int embedder_render_process_id, 139 int embedder_render_view_id, 140 const SpeechRecognitionHostMsg_StartRequest_Params& params, 141 int params_render_frame_id, 142 bool filter_profanities) { 143 SpeechRecognitionSessionContext context; 144 context.context_name = params.origin_url; 145 context.render_process_id = render_process_id_; 146 context.render_view_id = params.render_view_id; 147 context.render_frame_id = params_render_frame_id; 148 context.embedder_render_process_id = embedder_render_process_id; 149 context.embedder_render_view_id = embedder_render_view_id; 150 if (embedder_render_process_id) 151 context.guest_render_view_id = params.render_view_id; 152 context.request_id = params.request_id; 153 154 SpeechRecognitionSessionConfig config; 155 config.is_legacy_api = false; 156 config.language = params.language; 157 config.grammars = params.grammars; 158 config.max_hypotheses = params.max_hypotheses; 159 config.origin_url = params.origin_url; 160 config.initial_context = context; 161 config.url_request_context_getter = context_getter_.get(); 162 config.filter_profanities = filter_profanities; 163 config.continuous = params.continuous; 164 config.interim_results = params.interim_results; 165 config.event_listener = AsWeakPtr(); 166 167 int session_id = SpeechRecognitionManager::GetInstance()->CreateSession( 168 config); 169 DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid); 170 SpeechRecognitionManager::GetInstance()->StartSession(session_id); 171} 172 173void SpeechRecognitionDispatcherHost::OnAbortRequest(int render_view_id, 174 int request_id) { 175 int session_id = SpeechRecognitionManager::GetInstance()->GetSession( 176 render_process_id_, render_view_id, request_id); 177 178 // The renderer might provide an invalid |request_id| if the session was not 179 // started as expected, e.g., due to unsatisfied security requirements. 180 if (session_id != SpeechRecognitionManager::kSessionIDInvalid) 181 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); 182} 183 184void SpeechRecognitionDispatcherHost::OnAbortAllRequests(int render_view_id) { 185 SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderView( 186 render_process_id_, render_view_id); 187} 188 189void SpeechRecognitionDispatcherHost::OnStopCaptureRequest( 190 int render_view_id, int request_id) { 191 int session_id = SpeechRecognitionManager::GetInstance()->GetSession( 192 render_process_id_, render_view_id, request_id); 193 194 // The renderer might provide an invalid |request_id| if the session was not 195 // started as expected, e.g., due to unsatisfied security requirements. 196 if (session_id != SpeechRecognitionManager::kSessionIDInvalid) { 197 SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession( 198 session_id); 199 } 200} 201 202// -------- SpeechRecognitionEventListener interface implementation ----------- 203 204void SpeechRecognitionDispatcherHost::OnRecognitionStart(int session_id) { 205 const SpeechRecognitionSessionContext& context = 206 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 207 Send(new SpeechRecognitionMsg_Started(context.render_view_id, 208 context.request_id)); 209} 210 211void SpeechRecognitionDispatcherHost::OnAudioStart(int session_id) { 212 const SpeechRecognitionSessionContext& context = 213 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 214 Send(new SpeechRecognitionMsg_AudioStarted(context.render_view_id, 215 context.request_id)); 216} 217 218void SpeechRecognitionDispatcherHost::OnSoundStart(int session_id) { 219 const SpeechRecognitionSessionContext& context = 220 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 221 Send(new SpeechRecognitionMsg_SoundStarted(context.render_view_id, 222 context.request_id)); 223} 224 225void SpeechRecognitionDispatcherHost::OnSoundEnd(int session_id) { 226 const SpeechRecognitionSessionContext& context = 227 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 228 Send(new SpeechRecognitionMsg_SoundEnded(context.render_view_id, 229 context.request_id)); 230} 231 232void SpeechRecognitionDispatcherHost::OnAudioEnd(int session_id) { 233 const SpeechRecognitionSessionContext& context = 234 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 235 Send(new SpeechRecognitionMsg_AudioEnded(context.render_view_id, 236 context.request_id)); 237} 238 239void SpeechRecognitionDispatcherHost::OnRecognitionEnd(int session_id) { 240 const SpeechRecognitionSessionContext& context = 241 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 242 Send(new SpeechRecognitionMsg_Ended(context.render_view_id, 243 context.request_id)); 244} 245 246void SpeechRecognitionDispatcherHost::OnRecognitionResults( 247 int session_id, 248 const SpeechRecognitionResults& results) { 249 const SpeechRecognitionSessionContext& context = 250 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 251 Send(new SpeechRecognitionMsg_ResultRetrieved(context.render_view_id, 252 context.request_id, 253 results)); 254} 255 256void SpeechRecognitionDispatcherHost::OnRecognitionError( 257 int session_id, 258 const SpeechRecognitionError& error) { 259 const SpeechRecognitionSessionContext& context = 260 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); 261 Send(new SpeechRecognitionMsg_ErrorOccurred(context.render_view_id, 262 context.request_id, 263 error)); 264} 265 266// The events below are currently not used by speech JS APIs implementation. 267void SpeechRecognitionDispatcherHost::OnAudioLevelsChange(int session_id, 268 float volume, 269 float noise_volume) { 270} 271 272void SpeechRecognitionDispatcherHost::OnEnvironmentEstimationComplete( 273 int session_id) { 274} 275 276} // namespace content 277