cef/libcef/browser/speech_recognition_manager_...

217 lines
8.4 KiB
C++

// 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 "libcef/browser/speech_recognition_manager_delegate.h"
#include <set>
#include <string>
#include "libcef/common/cef_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/speech_recognition_manager.h"
#include "content/public/browser/speech_recognition_session_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
using content::BrowserThread;
using content::SpeechRecognitionManager;
using content::WebContents;
// Simple utility to get notified when a WebContents is closed or crashes.
// Both the callback site and the callback thread are passed by the caller in
// the constructor. There is no restriction on the constructor, however this
// class must be destroyed on the UI thread, due to the NotificationRegistrar
// dependency.
class CefSpeechRecognitionManagerDelegate::WebContentsWatcher
: public base::RefCountedThreadSafe<WebContentsWatcher>,
public content::NotificationObserver {
public:
typedef base::Callback<void(int render_process_id, int render_view_id)>
WebContentsClosedCallback;
WebContentsWatcher(WebContentsClosedCallback web_contents_closed_callback,
BrowserThread::ID callback_thread)
: web_contents_closed_callback_(web_contents_closed_callback),
callback_thread_(callback_thread) {}
// Starts monitoring the WebContents corresponding to the given
// |render_process_id|, |render_view_id| pair, invoking
// |web_contents_closed_callback_| if closed/unloaded.
void Watch(int render_process_id, int render_view_id) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&WebContentsWatcher::Watch, this,
render_process_id, render_view_id));
return;
}
WebContents* web_contents = NULL;
content::RenderViewHost* render_view_host =
content::RenderViewHost::FromID(render_process_id, render_view_id);
if (render_view_host)
web_contents = WebContents::FromRenderViewHost(render_view_host);
DCHECK(web_contents);
// Avoid multiple registrations on |registrar_| for the same |web_contents|.
if (registered_web_contents_.find(web_contents) !=
registered_web_contents_.end()) {
return;
}
registered_web_contents_.insert(web_contents);
// Lazy initialize the registrar.
if (!registrar_.get())
registrar_.reset(new content::NotificationRegistrar());
registrar_->Add(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
content::Source<WebContents>(web_contents));
}
// content::NotificationObserver implementation.
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, type);
WebContents* web_contents = content::Source<WebContents>(source).ptr();
int render_process_id =
web_contents->GetRenderViewHost()->GetProcess()->GetID();
int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID();
registrar_->Remove(this, content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED,
content::Source<WebContents>(web_contents));
registered_web_contents_.erase(web_contents);
BrowserThread::PostTask(callback_thread_, FROM_HERE,
base::Bind(web_contents_closed_callback_,
render_process_id, render_view_id));
}
private:
friend class base::RefCountedThreadSafe<WebContentsWatcher>;
~WebContentsWatcher() override {
// Must be destroyed on the UI thread due to |registrar_| non thread-safety.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
// Lazy-initialized and used on the UI thread to handle web contents
// notifications (tab closing).
std::unique_ptr<content::NotificationRegistrar> registrar_;
// Keeps track of which WebContent(s) have been registered, in order to avoid
// double registrations on |registrar_|
std::set<content::WebContents*> registered_web_contents_;
// Callback used to notify, on the thread specified by |callback_thread_| the
// closure of a registered tab.
WebContentsClosedCallback web_contents_closed_callback_;
content::BrowserThread::ID callback_thread_;
DISALLOW_COPY_AND_ASSIGN(WebContentsWatcher);
};
CefSpeechRecognitionManagerDelegate ::CefSpeechRecognitionManagerDelegate() {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
filter_profanities_ =
command_line->HasSwitch(switches::kEnableProfanityFilter);
}
CefSpeechRecognitionManagerDelegate ::~CefSpeechRecognitionManagerDelegate() {}
void CefSpeechRecognitionManagerDelegate::WebContentsClosedCallback(
int render_process_id,
int render_view_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance();
// |manager| becomes NULL if a browser shutdown happens between the post of
// this task (from the UI thread) and this call (on the IO thread). In this
// case we just return.
if (!manager)
return;
manager->AbortAllSessionsForRenderView(render_process_id, render_view_id);
}
void CefSpeechRecognitionManagerDelegate::OnRecognitionStart(int session_id) {
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
// Register callback to auto abort session on tab closure.
// |web_contents_watcher_| is lazyly istantiated on the first call.
if (!web_contents_watcher_.get()) {
web_contents_watcher_ = new WebContentsWatcher(
base::Bind(
&CefSpeechRecognitionManagerDelegate::WebContentsClosedCallback,
base::Unretained(this)),
BrowserThread::IO);
}
web_contents_watcher_->Watch(context.render_process_id,
context.render_view_id);
}
void CefSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {}
void CefSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete(
int session_id) {}
void CefSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {}
void CefSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {}
void CefSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {}
void CefSpeechRecognitionManagerDelegate::OnRecognitionResults(
int session_id,
const content::SpeechRecognitionResults& result) {}
void CefSpeechRecognitionManagerDelegate::OnRecognitionError(
int session_id,
const content::SpeechRecognitionError& error) {}
void CefSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
int session_id,
float volume,
float noise_volume) {}
void CefSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {}
void CefSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
int session_id,
base::OnceCallback<void(bool ask_user, bool is_allowed)> callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
// Make sure that initiators properly set the |render_process_id| field.
DCHECK_NE(context.render_process_id, 0);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(std::move(callback), false, true));
}
content::SpeechRecognitionEventListener*
CefSpeechRecognitionManagerDelegate::GetEventListener() {
return this;
}
bool CefSpeechRecognitionManagerDelegate::FilterProfanities(
int render_process_id) {
return filter_profanities_;
}