blob: 818ba8324483c3778e236f3fe335c5d675de90df [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/speech/speech_recognition_dispatcher_host.h"
#include <memory>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/render_frame_host_manager.h"
#include "content/browser/speech/speech_recognition_manager_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/speech_recognition_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/speech_recognition_manager_delegate.h"
#include "content/public/browser/speech_recognition_session_config.h"
#include "content/public/browser/speech_recognition_session_context.h"
#include "content/public/common/content_switches.h"
namespace content {
SpeechRecognitionDispatcherHost::SpeechRecognitionDispatcherHost(
int render_process_id,
net::URLRequestContextGetter* context_getter)
: BrowserMessageFilter(SpeechRecognitionMsgStart),
render_process_id_(render_process_id),
context_getter_(context_getter),
weak_factory_(this) {
// Do not add any non-trivial initialization here, instead do it lazily when
// required (e.g. see the method |SpeechRecognitionManager::GetInstance()|) or
// add an Init() method.
}
SpeechRecognitionDispatcherHost::~SpeechRecognitionDispatcherHost() {
}
base::WeakPtr<SpeechRecognitionDispatcherHost>
SpeechRecognitionDispatcherHost::AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void SpeechRecognitionDispatcherHost::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
bool SpeechRecognitionDispatcherHost::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SpeechRecognitionDispatcherHost, message)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StartRequest,
OnStartRequest)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortRequest,
OnAbortRequest)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_StopCaptureRequest,
OnStopCaptureRequest)
IPC_MESSAGE_HANDLER(SpeechRecognitionHostMsg_AbortAllRequests,
OnAbortAllRequests)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void SpeechRecognitionDispatcherHost::OverrideThreadForMessage(
const IPC::Message& message,
BrowserThread::ID* thread) {
if (message.type() == SpeechRecognitionHostMsg_StartRequest::ID)
*thread = BrowserThread::UI;
}
void SpeechRecognitionDispatcherHost::OnChannelClosing() {
weak_factory_.InvalidateWeakPtrs();
}
void SpeechRecognitionDispatcherHost::OnStartRequest(
const SpeechRecognitionHostMsg_StartRequest_Params& params) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Check that the origin specified by the renderer process is one
// that it is allowed to access.
if (params.origin_url != "null" &&
!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
render_process_id_, GURL(params.origin_url))) {
LOG(ERROR) << "SRDH::OnStartRequest, disallowed origin: "
<< params.origin_url;
return;
}
int embedder_render_process_id = 0;
int embedder_render_frame_id = MSG_ROUTING_NONE;
WebContentsImpl* web_contents =
static_cast<WebContentsImpl*>(WebContentsImpl::FromRenderFrameHostID(
render_process_id_, params.render_frame_id));
if (!web_contents) {
// The render frame id is renderer-provided. If it's invalid, don't crash.
DLOG(ERROR) << "SRDH::OnStartRequest, invalid frame";
return;
}
// If the speech API request was from an inner WebContents or a guest, save
// the context of the outer WebContents or the embedder since we will use it
// to decide permission.
WebContents* outer_web_contents = web_contents->GetOuterWebContents();
if (outer_web_contents) {
RenderFrameHost* embedder_frame = nullptr;
FrameTreeNode* embedder_frame_node = web_contents->GetMainFrame()
->frame_tree_node()
->render_manager()
->GetOuterDelegateNode();
if (embedder_frame_node) {
embedder_frame = embedder_frame_node->current_frame_host();
} else {
// The outer web contents is embedded using the browser plugin. Fall back
// to a simple lookup of the main frame. TODO(avi): When the browser
// plugin is retired, remove this code.
embedder_frame = outer_web_contents->GetMainFrame();
}
embedder_render_process_id = embedder_frame->GetProcess()->GetID();
DCHECK_NE(embedder_render_process_id, 0);
embedder_render_frame_id = embedder_frame->GetRoutingID();
DCHECK_NE(embedder_render_frame_id, MSG_ROUTING_NONE);
}
bool filter_profanities =
SpeechRecognitionManagerImpl::GetInstance() &&
SpeechRecognitionManagerImpl::GetInstance()->delegate() &&
SpeechRecognitionManagerImpl::GetInstance()->delegate()->
FilterProfanities(render_process_id_);
SpeechRecognitionSessionContext context;
context.context_name = params.origin_url;
context.render_process_id = render_process_id_;
context.render_frame_id = params.render_frame_id;
context.embedder_render_process_id = embedder_render_process_id;
context.embedder_render_frame_id = embedder_render_frame_id;
context.request_id = params.request_id;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&SpeechRecognitionDispatcherHost::StartSession,
base::Unretained(this), params, context,
filter_profanities));
}
void SpeechRecognitionDispatcherHost::StartSession(
const SpeechRecognitionHostMsg_StartRequest_Params& params,
const SpeechRecognitionSessionContext& context,
bool filter_profanities) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
SpeechRecognitionSessionConfig config;
config.language = params.language;
config.grammars = params.grammars;
config.max_hypotheses = params.max_hypotheses;
config.origin_url = params.origin_url;
config.initial_context = context;
config.url_request_context_getter = context_getter_.get();
config.filter_profanities = filter_profanities;
config.continuous = params.continuous;
config.interim_results = params.interim_results;
config.event_listener = AsWeakPtr();
int session_id = SpeechRecognitionManager::GetInstance()->CreateSession(
config);
DCHECK_NE(session_id, SpeechRecognitionManager::kSessionIDInvalid);
SpeechRecognitionManager::GetInstance()->StartSession(session_id);
}
void SpeechRecognitionDispatcherHost::OnAbortRequest(int render_frame_id,
int request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
render_process_id_, render_frame_id, request_id);
// The renderer might provide an invalid |request_id| if the session was not
// started as expected, e.g., due to unsatisfied security requirements.
if (session_id != SpeechRecognitionManager::kSessionIDInvalid)
SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
}
void SpeechRecognitionDispatcherHost::OnAbortAllRequests(int render_frame_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
SpeechRecognitionManager::GetInstance()->AbortAllSessionsForRenderFrame(
render_process_id_, render_frame_id);
}
void SpeechRecognitionDispatcherHost::OnStopCaptureRequest(int render_frame_id,
int request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int session_id = SpeechRecognitionManager::GetInstance()->GetSession(
render_process_id_, render_frame_id, request_id);
// The renderer might provide an invalid |request_id| if the session was not
// started as expected, e.g., due to unsatisfied security requirements.
if (session_id != SpeechRecognitionManager::kSessionIDInvalid) {
SpeechRecognitionManager::GetInstance()->StopAudioCaptureForSession(
session_id);
}
}
// -------- SpeechRecognitionEventListener interface implementation -----------
void SpeechRecognitionDispatcherHost::OnRecognitionStart(int session_id) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_Started(context.render_frame_id,
context.request_id));
}
void SpeechRecognitionDispatcherHost::OnAudioStart(int session_id) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_AudioStarted(context.render_frame_id,
context.request_id));
}
void SpeechRecognitionDispatcherHost::OnSoundStart(int session_id) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_SoundStarted(context.render_frame_id,
context.request_id));
}
void SpeechRecognitionDispatcherHost::OnSoundEnd(int session_id) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_SoundEnded(context.render_frame_id,
context.request_id));
}
void SpeechRecognitionDispatcherHost::OnAudioEnd(int session_id) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_AudioEnded(context.render_frame_id,
context.request_id));
}
void SpeechRecognitionDispatcherHost::OnRecognitionEnd(int session_id) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_Ended(context.render_frame_id,
context.request_id));
}
void SpeechRecognitionDispatcherHost::OnRecognitionResults(
int session_id,
const SpeechRecognitionResults& results) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_ResultRetrieved(context.render_frame_id,
context.request_id, results));
}
void SpeechRecognitionDispatcherHost::OnRecognitionError(
int session_id,
const SpeechRecognitionError& error) {
const SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
Send(new SpeechRecognitionMsg_ErrorOccurred(context.render_frame_id,
context.request_id, error));
}
// The events below are currently not used by speech JS APIs implementation.
void SpeechRecognitionDispatcherHost::OnAudioLevelsChange(int session_id,
float volume,
float noise_volume) {
}
void SpeechRecognitionDispatcherHost::OnEnvironmentEstimationComplete(
int session_id) {
}
} // namespace content