diff --git a/cef.gyp b/cef.gyp index ba716b27b..9a4a21a86 100644 --- a/cef.gyp +++ b/cef.gyp @@ -894,6 +894,8 @@ 'libcef/browser/scheme_impl.cc', 'libcef/browser/scheme_registration.cc', 'libcef/browser/scheme_registration.h', + 'libcef/browser/speech_recognition_manager_delegate.cc', + 'libcef/browser/speech_recognition_manager_delegate.h', 'libcef/browser/stream_impl.cc', 'libcef/browser/stream_impl.h', 'libcef/browser/trace_impl.cc', diff --git a/libcef/browser/browser_context.cc b/libcef/browser/browser_context.cc index 661ba6844..76dd71534 100644 --- a/libcef/browser/browser_context.cc +++ b/libcef/browser/browser_context.cc @@ -11,8 +11,10 @@ #include "libcef/browser/download_manager_delegate.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/url_request_context_getter.h" +#include "libcef/common/cef_switches.h" #include "base/bind.h" +#include "base/command_line.h" #include "base/logging.h" #include "base/threading/thread.h" #include "content/public/browser/download_manager.h" @@ -160,14 +162,19 @@ class CefSpeechRecognitionPreferences : public content::SpeechRecognitionPreferences { public: CefSpeechRecognitionPreferences() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + filter_profanities_ = + command_line.HasSwitch(switches::kEnableProfanityFilter); } // Overridden from SpeechRecognitionPreferences: virtual bool FilterProfanities() const OVERRIDE { - return false; + return filter_profanities_; } private: + bool filter_profanities_; + DISALLOW_COPY_AND_ASSIGN(CefSpeechRecognitionPreferences); }; diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index 481ca24d9..886c96547 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -16,6 +16,7 @@ #include "libcef/browser/context.h" #include "libcef/browser/media_capture_devices_dispatcher.h" #include "libcef/browser/resource_dispatcher_host_delegate.h" +#include "libcef/browser/speech_recognition_manager_delegate.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/web_plugin_impl.h" #include "libcef/common/cef_switches.h" @@ -435,6 +436,7 @@ void CefContentBrowserClient::AppendExtraCommandLineSwitches( // any associated values) if present in the browser command line. static const char* const kSwitchNames[] = { switches::kContextSafetyImplementation, + switches::kEnableSpeechInput, switches::kProductVersion, switches::kUncaughtExceptionStackSize, }; @@ -464,6 +466,17 @@ content::MediaObserver* CefContentBrowserClient::GetMediaObserver() { return CefMediaCaptureDevicesDispatcher::GetInstance(); } +content::SpeechRecognitionManagerDelegate* + CefContentBrowserClient::GetSpeechRecognitionManagerDelegate() { +#if defined(ENABLE_INPUT_SPEECH) + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kEnableSpeechInput)) + return new CefSpeechRecognitionManagerDelegate(); +#endif + + return NULL; +} + content::AccessTokenStore* CefContentBrowserClient::CreateAccessTokenStore() { return new CefAccessTokenStore; } diff --git a/libcef/browser/content_browser_client.h b/libcef/browser/content_browser_client.h index b92149f25..6a891b7d1 100644 --- a/libcef/browser/content_browser_client.h +++ b/libcef/browser/content_browser_client.h @@ -97,6 +97,8 @@ class CefContentBrowserClient : public content::ContentBrowserClient { virtual content::QuotaPermissionContext* CreateQuotaPermissionContext() OVERRIDE; virtual content::MediaObserver* GetMediaObserver() OVERRIDE; + virtual content::SpeechRecognitionManagerDelegate* + GetSpeechRecognitionManagerDelegate() OVERRIDE; virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE; virtual bool CanCreateWindow(const GURL& opener_url, const GURL& origin, diff --git a/libcef/browser/speech_recognition_manager_delegate.cc b/libcef/browser/speech_recognition_manager_delegate.cc new file mode 100644 index 000000000..2baef9259 --- /dev/null +++ b/libcef/browser/speech_recognition_manager_delegate.cc @@ -0,0 +1,217 @@ +// 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 +#include + +#include "base/bind.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, + public content::NotificationObserver { + public: + typedef base::Callback + 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(web_contents)); + } + + // content::NotificationObserver implementation. + virtual 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(source).ptr(); + int render_process_id = web_contents->GetRenderProcessHost()->GetID(); + int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID(); + + registrar_->Remove(this, + content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, + content::Source(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; + + virtual ~WebContentsWatcher() { + // 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). + scoped_ptr registrar_; + + // Keeps track of which WebContent(s) have been registered, in order to avoid + // double registrations on |registrar_| + std::set 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() { +} + +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::GetDiagnosticInformation( + bool* can_report_metrics, + std::string* hardware_info) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); +} + +void CefSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed( + int session_id, + base::Callback 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); + + callback.Run(false, true); +} + +content::SpeechRecognitionEventListener* +CefSpeechRecognitionManagerDelegate::GetEventListener() { + return this; +} diff --git a/libcef/browser/speech_recognition_manager_delegate.h b/libcef/browser/speech_recognition_manager_delegate.h new file mode 100644 index 000000000..34cccc02f --- /dev/null +++ b/libcef/browser/speech_recognition_manager_delegate.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef CEF_LIBCEF_BROWSER_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ +#define CEF_LIBCEF_BROWSER_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/speech_recognition_event_listener.h" +#include "content/public/browser/speech_recognition_manager_delegate.h" + + +// This is CEF's implementation of the SpeechRecognitionManagerDelegate +// interface. Based on chrome/browser/speech/ +// chrome_speech_recognition_manager_delegate.[cc|h] +class CefSpeechRecognitionManagerDelegate + : NON_EXPORTED_BASE(public content::SpeechRecognitionManagerDelegate), + public content::SpeechRecognitionEventListener { + public: + CefSpeechRecognitionManagerDelegate(); + virtual ~CefSpeechRecognitionManagerDelegate(); + + protected: + // SpeechRecognitionEventListener methods. + virtual void OnRecognitionStart(int session_id) OVERRIDE; + virtual void OnAudioStart(int session_id) OVERRIDE; + virtual void OnEnvironmentEstimationComplete(int session_id) OVERRIDE; + virtual void OnSoundStart(int session_id) OVERRIDE; + virtual void OnSoundEnd(int session_id) OVERRIDE; + virtual void OnAudioEnd(int session_id) OVERRIDE; + virtual void OnRecognitionEnd(int session_id) OVERRIDE; + virtual void OnRecognitionResults( + int session_id, const content::SpeechRecognitionResults& result) OVERRIDE; + virtual void OnRecognitionError( + int session_id, const content::SpeechRecognitionError& error) OVERRIDE; + virtual void OnAudioLevelsChange(int session_id, float volume, + float noise_volume) OVERRIDE; + + // SpeechRecognitionManagerDelegate methods. + virtual void GetDiagnosticInformation(bool* can_report_metrics, + std::string* hardware_info) OVERRIDE; + virtual void CheckRecognitionIsAllowed( + int session_id, + base::Callback callback) OVERRIDE; + virtual content::SpeechRecognitionEventListener* GetEventListener() OVERRIDE; + + private: + class WebContentsWatcher; + + // Callback called by |web_contents_watcher_| on the IO thread to signal + // web contents closure. + void WebContentsClosedCallback(int render_process_id, int render_view_id); + + scoped_refptr web_contents_watcher_; + + DISALLOW_COPY_AND_ASSIGN(CefSpeechRecognitionManagerDelegate); +}; + +#endif // CEF_LIBCEF_BROWSER_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ diff --git a/libcef/common/cef_switches.cc b/libcef/common/cef_switches.cc index 20bb850c9..c696ce4df 100644 --- a/libcef/common/cef_switches.cc +++ b/libcef/common/cef_switches.cc @@ -89,4 +89,10 @@ const char kPersistSessionCookies[] = "persist-session-cookies"; // Enable media (WebRTC audio/video) streaming. const char kEnableMediaStream[] = "enable-media-stream"; +// Enable speech input (x-webkit-speech). +const char kEnableSpeechInput[] = "enable-speech-input"; + +// Enable the speech input profanity filter. +const char kEnableProfanityFilter[] = "enable-profanity-filter"; + } // namespace switches diff --git a/libcef/common/cef_switches.h b/libcef/common/cef_switches.h index 5863289c7..d76583efc 100644 --- a/libcef/common/cef_switches.h +++ b/libcef/common/cef_switches.h @@ -40,6 +40,8 @@ extern const char kDisableAuthorAndUserStyles[]; extern const char kDisableDeveloperTools[]; extern const char kPersistSessionCookies[]; extern const char kEnableMediaStream[]; +extern const char kEnableSpeechInput[]; +extern const char kEnableProfanityFilter[]; } // namespace switches diff --git a/libcef/renderer/content_renderer_client.cc b/libcef/renderer/content_renderer_client.cc index 24b64755b..b89e00e0b 100644 --- a/libcef/renderer/content_renderer_client.cc +++ b/libcef/renderer/content_renderer_client.cc @@ -272,12 +272,17 @@ void CefContentRendererClient::AddCustomScheme( } void CefContentRendererClient::WebKitInitialized() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + WebKit::WebRuntimeFeatures::enableMediaPlayer( media::IsMediaLibraryInitialized()); // TODO(cef): Enable these once the implementation supports it. WebKit::WebRuntimeFeatures::enableNotifications(false); + WebKit::WebRuntimeFeatures::enableSpeechInput( + command_line.HasSwitch(switches::kEnableSpeechInput)); + worker_script_observer_.reset(new CefWebWorkerScriptObserver()); WebKit::WebWorkerInfo::addScriptObserver(worker_script_observer_.get()); @@ -313,7 +318,6 @@ void CefContentRendererClient::WebKitInitialized() { } // The number of stack trace frames to capture for uncaught exceptions. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kUncaughtExceptionStackSize)) { int uncaught_exception_stack_size = 0; base::StringToInt(