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.
5 #include "content/browser/speech/input_tag_speech_dispatcher_host.h"
8 #include "base/lazy_instance.h"
9 #include "content/browser/browser_plugin/browser_plugin_guest.h"
10 #include "content/browser/child_process_security_policy_impl.h"
11 #include "content/browser/renderer_host/render_view_host_impl.h"
12 #include "content/browser/speech/speech_recognition_manager_impl.h"
13 #include "content/browser/web_contents/web_contents_impl.h"
14 #include "content/common/speech_recognition_messages.h"
15 #include "content/public/browser/speech_recognition_manager_delegate.h"
16 #include "content/public/browser/speech_recognition_session_config.h"
17 #include "content/public/browser/speech_recognition_session_context.h"
20 const uint32 kMaxHypothesesForSpeechInputTag = 6;
25 InputTagSpeechDispatcherHost::InputTagSpeechDispatcherHost(
27 int render_process_id,
28 net::URLRequestContextGetter* url_request_context_getter)
29 : BrowserMessageFilter(SpeechRecognitionMsgStart),
31 render_process_id_(render_process_id),
32 url_request_context_getter_(url_request_context_getter),
34 // Do not add any non-trivial initialization here, instead do it lazily when
35 // required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or
36 // add an Init() method.
39 InputTagSpeechDispatcherHost::~InputTagSpeechDispatcherHost() {
40 SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderProcess(
44 base::WeakPtr<InputTagSpeechDispatcherHost>
45 InputTagSpeechDispatcherHost::AsWeakPtr() {
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
47 return weak_factory_.GetWeakPtr();
50 bool InputTagSpeechDispatcherHost::OnMessageReceived(
51 const IPC::Message& message, bool* message_was_ok) {
53 IPC_BEGIN_MESSAGE_MAP_EX(InputTagSpeechDispatcherHost, message,
55 IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StartRecognition,
57 IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_CancelRecognition,
59 IPC_MESSAGE_HANDLER(InputTagSpeechHostMsg_StopRecording,
61 IPC_MESSAGE_UNHANDLED(handled = false)
66 void InputTagSpeechDispatcherHost::OverrideThreadForMessage(
67 const IPC::Message& message,
68 BrowserThread::ID* thread) {
69 if (message.type() == InputTagSpeechHostMsg_StartRecognition::ID)
70 *thread = BrowserThread::UI;
73 void InputTagSpeechDispatcherHost::OnChannelClosing() {
74 weak_factory_.InvalidateWeakPtrs();
77 void InputTagSpeechDispatcherHost::OnStartRecognition(
78 const InputTagSpeechHostMsg_StartRecognition_Params& params) {
79 InputTagSpeechHostMsg_StartRecognition_Params input_params(params);
80 int render_process_id = render_process_id_;
82 // Check that the origin specified by the renderer process is one
83 // that it is allowed to access.
84 if (params.origin_url != "null" &&
85 !ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
86 render_process_id, GURL(params.origin_url))) {
87 LOG(ERROR) << "ITSDH::OnStartRecognition, disallowed origin: "
92 // The chrome layer is mostly oblivious to BrowserPlugin guests and so it
93 // cannot correctly place the speech bubble relative to a guest. Thus, we
94 // set up the speech recognition context relative to the embedder.
95 int guest_render_view_id = MSG_ROUTING_NONE;
97 RenderViewHostImpl* render_view_host =
98 RenderViewHostImpl::FromID(render_process_id_, params.render_view_id);
99 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
100 WebContents::FromRenderViewHost(render_view_host));
101 BrowserPluginGuest* guest = web_contents->GetBrowserPluginGuest();
102 input_params.element_rect.set_origin(
103 guest->GetScreenCoordinates(input_params.element_rect.origin()));
104 guest_render_view_id = params.render_view_id;
106 guest->embedder_web_contents()->GetRenderProcessHost()->GetID();
107 input_params.render_view_id =
108 guest->embedder_web_contents()->GetRoutingID();
110 bool filter_profanities =
111 SpeechRecognitionManagerImpl::GetInstance() &&
112 SpeechRecognitionManagerImpl::GetInstance()->delegate() &&
113 SpeechRecognitionManagerImpl::GetInstance()->delegate()->
114 FilterProfanities(render_process_id_);
116 BrowserThread::PostTask(
117 BrowserThread::IO, FROM_HERE,
119 &InputTagSpeechDispatcherHost::StartRecognitionOnIO,
122 guest_render_view_id,
124 filter_profanities));
127 void InputTagSpeechDispatcherHost::StartRecognitionOnIO(
128 int render_process_id,
129 int guest_render_view_id,
130 const InputTagSpeechHostMsg_StartRecognition_Params& params,
131 bool filter_profanities) {
132 SpeechRecognitionSessionContext context;
133 context.render_process_id = render_process_id;
134 context.render_view_id = params.render_view_id;
135 context.guest_render_view_id = guest_render_view_id;
136 // Keep context.embedder_render_process_id and context.embedder_render_view_id
138 context.request_id = params.request_id;
139 context.element_rect = params.element_rect;
141 SpeechRecognitionSessionConfig config;
142 config.language = params.language;
143 if (!params.grammar.empty()) {
144 config.grammars.push_back(SpeechRecognitionGrammar(params.grammar));
146 config.max_hypotheses = kMaxHypothesesForSpeechInputTag;
147 config.origin_url = params.origin_url;
148 config.initial_context = context;
149 config.url_request_context_getter = url_request_context_getter_.get();
150 config.filter_profanities = filter_profanities;
151 config.event_listener = AsWeakPtr();
153 int session_id = SpeechRecognitionManager::GetInstance()->CreateSession(
155 DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid);
156 SpeechRecognitionManager::GetInstance()->StartSession(session_id);
159 void InputTagSpeechDispatcherHost::OnCancelRecognition(int render_view_id,
161 int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
162 render_process_id_, render_view_id, request_id);
164 // The renderer might provide an invalid |request_id| if the session was not
165 // started as expected, e.g., due to unsatisfied security requirements.
166 if (session_id != SpeechRecognitionManager::kSessionIDInvalid)
167 SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
170 void InputTagSpeechDispatcherHost::OnStopRecording(int render_view_id,
172 int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
173 render_process_id_, render_view_id, request_id);
175 // The renderer might provide an invalid |request_id| if the session was not
176 // started as expected, e.g., due to unsatisfied security requirements.
177 if (session_id != SpeechRecognitionManager::kSessionIDInvalid) {
178 SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession(
183 // -------- SpeechRecognitionEventListener interface implementation -----------
184 void InputTagSpeechDispatcherHost::OnRecognitionResults(
186 const SpeechRecognitionResults& results) {
187 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionResults enter";
189 const SpeechRecognitionSessionContext& context =
190 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
193 context.guest_render_view_id == MSG_ROUTING_NONE ?
194 context.render_view_id : context.guest_render_view_id;
195 Send(new InputTagSpeechMsg_SetRecognitionResults(
199 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionResults exit";
202 void InputTagSpeechDispatcherHost::OnAudioEnd(int session_id) {
203 DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd enter";
205 const SpeechRecognitionSessionContext& context =
206 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
208 context.guest_render_view_id == MSG_ROUTING_NONE ?
209 context.render_view_id : context.guest_render_view_id;
210 Send(new InputTagSpeechMsg_RecordingComplete(render_view_id,
211 context.request_id));
212 DVLOG(1) << "InputTagSpeechDispatcherHost::OnAudioEnd exit";
215 void InputTagSpeechDispatcherHost::OnRecognitionEnd(int session_id) {
216 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd enter";
217 const SpeechRecognitionSessionContext& context =
218 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
220 context.guest_render_view_id == MSG_ROUTING_NONE ?
221 context.render_view_id : context.guest_render_view_id;
222 Send(new InputTagSpeechMsg_RecognitionComplete(render_view_id,
223 context.request_id));
224 DVLOG(1) << "InputTagSpeechDispatcherHost::OnRecognitionEnd exit";
227 // The events below are currently not used by x-webkit-speech implementation.
228 void InputTagSpeechDispatcherHost::OnRecognitionStart(int session_id) {}
229 void InputTagSpeechDispatcherHost::OnAudioStart(int session_id) {}
230 void InputTagSpeechDispatcherHost::OnSoundStart(int session_id) {}
231 void InputTagSpeechDispatcherHost::OnSoundEnd(int session_id) {}
232 void InputTagSpeechDispatcherHost::OnRecognitionError(
234 const SpeechRecognitionError& error) {}
235 void InputTagSpeechDispatcherHost::OnAudioLevelsChange(
236 int session_id, float volume, float noise_volume) {}
237 void InputTagSpeechDispatcherHost::OnEnvironmentEstimationComplete(
240 } // namespace content