Commit 011cfc7d authored by primiano@chromium.org's avatar primiano@chromium.org

SpeechInputExtensionManager now interface (exclusively) with...

SpeechInputExtensionManager now interface (exclusively) with SpeechRecognitionManagerDelegate (Speech CL1.11).

- The tray icon and balloon handing has been moved from speech_input_extensions_manager to chrome_speech_recognition_manager_delegate, since that code (tray icon) will be used also for continuous recognition.
- Removed the SpeechRecognizer interface from /content/public (thus de-virtualized and refcounted the SpeechRecognizerImpl)

BUG=116954
TEST=none

Review URL: https://chromiumcodereview.appspot.com/10377082

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138498 0039d316-1c4b-4281-b951-d872f2087c98
parent 8bf9ce70
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/speech/speech_recognition_tray_icon_controller.h"
#include "chrome/browser/tab_contents/tab_util.h" #include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/browser/view_type_utils.h" #include "chrome/browser/view_type_utils.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
...@@ -36,10 +37,20 @@ ...@@ -36,10 +37,20 @@
using content::BrowserThread; using content::BrowserThread;
using content::SpeechRecognitionManager; using content::SpeechRecognitionManager;
using content::WebContents; using content::WebContents;
using content::SpeechRecognitionSessionContext;
namespace { namespace {
const int kNoActiveBubble = const int kNoActiveBubble =
content::SpeechRecognitionManager::kSessionIDInvalid; content::SpeechRecognitionManager::kSessionIDInvalid;
bool RequiresBubble(int session_id) {
return SpeechRecognitionManager::GetInstance()->
GetSessionContext(session_id).requested_by_page_element;
}
bool RequiresTrayIcon(int session_id) {
return !RequiresBubble(session_id);
}
} // namespace } // namespace
namespace speech { namespace speech {
...@@ -108,13 +119,17 @@ class ChromeSpeechRecognitionManagerDelegate::OptionalRequestInfo ...@@ -108,13 +119,17 @@ class ChromeSpeechRecognitionManagerDelegate::OptionalRequestInfo
}; };
ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate() ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate()
: bubble_controller_(new SpeechRecognitionBubbleController( : active_bubble_session_id_(kNoActiveBubble) {
ALLOW_THIS_IN_INITIALIZER_LIST(this))),
active_bubble_session_id_(kNoActiveBubble) {
} }
ChromeSpeechRecognitionManagerDelegate:: ChromeSpeechRecognitionManagerDelegate::
~ChromeSpeechRecognitionManagerDelegate() { ~ChromeSpeechRecognitionManagerDelegate() {
if (tray_icon_controller_.get())
tray_icon_controller_->Hide();
if (active_bubble_session_id_ != kNoActiveBubble) {
DCHECK(bubble_controller_.get());
bubble_controller_->CloseBubble(active_bubble_session_id_);
}
} }
void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked( void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
...@@ -122,8 +137,11 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked( ...@@ -122,8 +137,11 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(active_bubble_session_id_, session_id); DCHECK_EQ(active_bubble_session_id_, session_id);
// Note, the session might have been destroyed, therefore avoid calls to the
// manager which imply its existance (e.g., GetSessionContext()).
if (button == SpeechRecognitionBubble::BUTTON_CANCEL) { if (button == SpeechRecognitionBubble::BUTTON_CANCEL) {
bubble_controller_->CloseBubble(session_id); GetBubbleController()->CloseBubble(session_id);
last_session_config_.reset(); last_session_config_.reset();
active_bubble_session_id_ = kNoActiveBubble; active_bubble_session_id_ = kNoActiveBubble;
...@@ -131,7 +149,7 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked( ...@@ -131,7 +149,7 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
// the manager's public methods are reliable and will handle it properly. // the manager's public methods are reliable and will handle it properly.
SpeechRecognitionManager::GetInstance()->AbortSession(session_id); SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
} else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) { } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) {
bubble_controller_->CloseBubble(session_id); GetBubbleController()->CloseBubble(session_id);
active_bubble_session_id_ = kNoActiveBubble; active_bubble_session_id_ = kNoActiveBubble;
RestartLastSession(); RestartLastSession();
} }
...@@ -142,7 +160,10 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged( ...@@ -142,7 +160,10 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(active_bubble_session_id_, session_id); DCHECK_EQ(active_bubble_session_id_, session_id);
bubble_controller_->CloseBubble(session_id); // Note, the session might have been destroyed, therefore avoid calls to the
// manager which imply its existance (e.g., GetSessionContext()).
GetBubbleController()->CloseBubble(session_id);
last_session_config_.reset(); last_session_config_.reset();
active_bubble_session_id_ = kNoActiveBubble; active_bubble_session_id_ = kNoActiveBubble;
...@@ -164,6 +185,10 @@ void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() { ...@@ -164,6 +185,10 @@ void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() {
void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart( void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
int session_id) { int session_id) {
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
if (RequiresBubble(session_id)) {
// Copy the configuration of the session (for the "try again" button). // Copy the configuration of the session (for the "try again" button).
last_session_config_.reset(new content::SpeechRecognitionSessionConfig( last_session_config_.reset(new content::SpeechRecognitionSessionConfig(
SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id))); SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id)));
...@@ -171,18 +196,25 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart( ...@@ -171,18 +196,25 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
// Create and show the bubble. // Create and show the bubble.
DCHECK_EQ(active_bubble_session_id_, kNoActiveBubble); DCHECK_EQ(active_bubble_session_id_, kNoActiveBubble);
active_bubble_session_id_ = session_id; active_bubble_session_id_ = session_id;
const content::SpeechRecognitionSessionContext& context = GetBubbleController()->CreateBubble(session_id,
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
bubble_controller_->CreateBubble(session_id,
context.render_process_id, context.render_process_id,
context.render_view_id, context.render_view_id,
context.element_rect); context.element_rect);
// TODO(primiano) Why not create directly the bubble in warmup mode?
bubble_controller_->SetBubbleWarmUpMode(session_id); // TODO(primiano) Why not creating directly the bubble in warmup mode?
GetBubbleController()->SetBubbleWarmUpMode(session_id);
}
} }
void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) { void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {
bubble_controller_->SetBubbleRecordingMode(session_id); if (RequiresBubble(session_id)) {
GetBubbleController()->SetBubbleRecordingMode(session_id);
} else if (RequiresTrayIcon(session_id)) {
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
GetTrayIconController()->Show(context.context_name,
context.is_first_request_for_context);
}
} }
void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete(
...@@ -198,16 +230,19 @@ void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) { ...@@ -198,16 +230,19 @@ void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {
void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) { void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {
// OnAudioEnd can be also raised after an abort, when the bubble has already // OnAudioEnd can be also raised after an abort, when the bubble has already
// been closed. // been closed.
if (active_bubble_session_id_ == session_id) if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
bubble_controller_->SetBubbleRecognizingMode(session_id); GetBubbleController()->SetBubbleRecognizingMode(session_id);
} else if (RequiresTrayIcon(session_id)) {
GetTrayIconController()->Hide();
}
} }
void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult( void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult(
int session_id, const content::SpeechRecognitionResult& result) { int session_id, const content::SpeechRecognitionResult& result) {
// A result can be dispatched when the bubble is not visible anymore (e.g., // A result can be dispatched when the bubble is not visible anymore (e.g.,
// lost focus while waiting for a result, thus continuing in background). // lost focus while waiting for a result, thus continuing in background).
if (active_bubble_session_id_ == session_id) { if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
bubble_controller_->CloseBubble(session_id); GetBubbleController()->CloseBubble(session_id);
last_session_config_.reset(); last_session_config_.reset();
active_bubble_session_id_ = kNoActiveBubble; active_bubble_session_id_ = kNoActiveBubble;
} }
...@@ -218,6 +253,7 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError( ...@@ -218,6 +253,7 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
// An error can be dispatched when the bubble is not visible anymore. // An error can be dispatched when the bubble is not visible anymore.
if (active_bubble_session_id_ != session_id) if (active_bubble_session_id_ != session_id)
return; return;
DCHECK(RequiresBubble(session_id));
int error_message_id = 0; int error_message_id = 0;
switch (error.code) { switch (error.code) {
...@@ -250,17 +286,29 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError( ...@@ -250,17 +286,29 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
NOTREACHED() << "unknown error " << error.code; NOTREACHED() << "unknown error " << error.code;
return; return;
} }
bubble_controller_->SetBubbleMessage( GetBubbleController()->SetBubbleMessage(
session_id, l10n_util::GetStringUTF16(error_message_id)); session_id, l10n_util::GetStringUTF16(error_message_id));
} }
void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange( void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
int session_id, float volume, float noise_volume) { int session_id, float volume, float noise_volume) {
if (active_bubble_session_id_ == session_id) if (active_bubble_session_id_ == session_id) {
bubble_controller_->SetBubbleInputVolume(session_id, volume, noise_volume); DCHECK(RequiresBubble(session_id));
GetBubbleController()->SetBubbleInputVolume(session_id,
volume, noise_volume);
} else if (RequiresTrayIcon(session_id)) {
GetTrayIconController()->SetVUMeterVolume(volume);
}
} }
void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) { void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {
// No need to remove the bubble here, since either one of the following events
// must have happened prior to this callback:
// - A previous OnRecognitionResult event already closed the bubble.
// - An error occurred, so the bubble is showing the error and will be closed
// when it will lose focus (by InfoBubbleFocusChanged()).
// - The bubble lost focus or the user pressed the Cancel button, thus it has
// been closed by InfoBubbleFocusChanged(), which triggered an AbortSession.
} }
void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation( void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation(
...@@ -286,11 +334,20 @@ void ChromeSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed( ...@@ -286,11 +334,20 @@ void ChromeSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
int session_id, int session_id,
base::Callback<void(int session_id, bool is_allowed)> callback) { base::Callback<void(int session_id, bool is_allowed)> callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
// We don't need any particular check for sessions not using a bubble. In such
// cases, we just notify it to the manager (calling-back synchronously, since
// we remain in the IO thread).
if (RequiresTrayIcon(session_id)) {
callback.Run(session_id, true /* is_allowed */);
return;
}
// Sessions using bubbles, conversely, need a check on the renderer view type.
// The check must be performed in the UI thread. We defer it posting to // The check must be performed in the UI thread. We defer it posting to
// CheckRenderViewType, which will issue the callback on our behalf. // CheckRenderViewType, which will issue the callback on our behalf.
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&CheckRenderViewType, base::Bind(&CheckRenderViewType,
session_id, session_id,
...@@ -330,4 +387,19 @@ void ChromeSpeechRecognitionManagerDelegate::CheckRenderViewType( ...@@ -330,4 +387,19 @@ void ChromeSpeechRecognitionManagerDelegate::CheckRenderViewType(
base::Bind(callback, session_id, allowed)); base::Bind(callback, session_id, allowed));
} }
SpeechRecognitionBubbleController*
ChromeSpeechRecognitionManagerDelegate::GetBubbleController() {
if (!bubble_controller_.get())
bubble_controller_ = new SpeechRecognitionBubbleController(this);
return bubble_controller_.get();
}
SpeechRecognitionTrayIconController*
ChromeSpeechRecognitionManagerDelegate::GetTrayIconController() {
if (!tray_icon_controller_.get())
tray_icon_controller_ = new SpeechRecognitionTrayIconController();
return tray_icon_controller_.get();
}
} // namespace speech } // namespace speech
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include "content/public/browser/speech_recognition_manager_delegate.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_config.h"
class SpeechRecognitionTrayIconController;
namespace speech { namespace speech {
// This is Chrome's implementation of the SpeechRecognitionManagerDelegate // This is Chrome's implementation of the SpeechRecognitionManagerDelegate
...@@ -69,7 +71,12 @@ class ChromeSpeechRecognitionManagerDelegate ...@@ -69,7 +71,12 @@ class ChromeSpeechRecognitionManagerDelegate
// (which is copied into |last_session_config_|). Used for "try again". // (which is copied into |last_session_config_|). Used for "try again".
void RestartLastSession(); void RestartLastSession();
// Lazy initializers for bubble and tray icon controller.
SpeechRecognitionBubbleController* GetBubbleController();
SpeechRecognitionTrayIconController* GetTrayIconController();
scoped_refptr<SpeechRecognitionBubbleController> bubble_controller_; scoped_refptr<SpeechRecognitionBubbleController> bubble_controller_;
scoped_refptr<SpeechRecognitionTrayIconController> tray_icon_controller_;
scoped_refptr<OptionalRequestInfo> optional_request_info_; scoped_refptr<OptionalRequestInfo> optional_request_info_;
scoped_ptr<content::SpeechRecognitionSessionConfig> last_session_config_; scoped_ptr<content::SpeechRecognitionSessionConfig> last_session_config_;
......
...@@ -23,6 +23,10 @@ namespace net { ...@@ -23,6 +23,10 @@ namespace net {
class URLRequestContextGetter; class URLRequestContextGetter;
} }
namespace {
const int kSessionIDForTests = 0;
}
// Mock class used to test the extension speech input API. // Mock class used to test the extension speech input API.
class SpeechInputExtensionApiTest : public ExtensionApiTest, class SpeechInputExtensionApiTest : public ExtensionApiTest,
public SpeechInputExtensionInterface { public SpeechInputExtensionInterface {
...@@ -77,10 +81,11 @@ class SpeechInputExtensionApiTest : public ExtensionApiTest, ...@@ -77,10 +81,11 @@ class SpeechInputExtensionApiTest : public ExtensionApiTest,
virtual void StartRecording( virtual void StartRecording(
content::SpeechRecognitionEventListener* listener, content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter, net::URLRequestContextGetter* context_getter,
int session_id, const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities) OVERRIDE; bool filter_profanities,
bool show_notification) OVERRIDE;
virtual void StopRecording(bool recognition_failed) OVERRIDE; virtual void StopRecording(bool recognition_failed) OVERRIDE;
...@@ -107,7 +112,7 @@ class SpeechInputExtensionApiTest : public ExtensionApiTest, ...@@ -107,7 +112,7 @@ class SpeechInputExtensionApiTest : public ExtensionApiTest,
}; };
private: private:
void ProvideResults(int session_id); void ProvideResults();
bool recording_devices_available_; bool recording_devices_available_;
bool recognizer_is_valid_; bool recognizer_is_valid_;
...@@ -131,10 +136,11 @@ SpeechInputExtensionApiTest::~SpeechInputExtensionApiTest() { ...@@ -131,10 +136,11 @@ SpeechInputExtensionApiTest::~SpeechInputExtensionApiTest() {
void SpeechInputExtensionApiTest::StartRecording( void SpeechInputExtensionApiTest::StartRecording(
content::SpeechRecognitionEventListener* listener, content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter, net::URLRequestContextGetter* context_getter,
int session_id, const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities) { bool filter_profanities,
bool show_notification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
recognizer_is_valid_ = true; recognizer_is_valid_ = true;
...@@ -143,7 +149,7 @@ void SpeechInputExtensionApiTest::StartRecording( ...@@ -143,7 +149,7 @@ void SpeechInputExtensionApiTest::StartRecording(
FROM_HERE, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::OnAudioStart, base::Bind(&SpeechInputExtensionManager::OnAudioStart,
GetManager(), GetManager(),
session_id), kSessionIDForTests),
base::TimeDelta()); base::TimeDelta());
// Notify sound start in the input device. // Notify sound start in the input device.
...@@ -151,16 +157,14 @@ void SpeechInputExtensionApiTest::StartRecording( ...@@ -151,16 +157,14 @@ void SpeechInputExtensionApiTest::StartRecording(
FROM_HERE, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::OnSoundStart, base::Bind(&SpeechInputExtensionManager::OnSoundStart,
GetManager(), GetManager(),
session_id), kSessionIDForTests),
base::TimeDelta()); base::TimeDelta());
if (result_delay_ms_ != kDontDispatchCall) { if (result_delay_ms_ != kDontDispatchCall) {
// Dispatch the recognition results. // Dispatch the recognition results.
MessageLoop::current()->PostDelayedTask( MessageLoop::current()->PostDelayedTask(
FROM_HERE, FROM_HERE,
base::Bind(&SpeechInputExtensionApiTest::ProvideResults, base::Bind(&SpeechInputExtensionApiTest::ProvideResults, this),
this,
session_id),
base::TimeDelta::FromMilliseconds(result_delay_ms_)); base::TimeDelta::FromMilliseconds(result_delay_ms_));
} }
} }
...@@ -170,18 +174,19 @@ void SpeechInputExtensionApiTest::StopRecording(bool recognition_failed) { ...@@ -170,18 +174,19 @@ void SpeechInputExtensionApiTest::StopRecording(bool recognition_failed) {
recognizer_is_valid_ = false; recognizer_is_valid_ = false;
} }
void SpeechInputExtensionApiTest::ProvideResults(int session_id) { void SpeechInputExtensionApiTest::ProvideResults() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (next_error_ != content::SPEECH_RECOGNITION_ERROR_NONE) { if (next_error_ != content::SPEECH_RECOGNITION_ERROR_NONE) {
GetManager()->OnRecognitionError( GetManager()->OnRecognitionError(
session_id, content::SpeechRecognitionError(next_error_)); kSessionIDForTests, content::SpeechRecognitionError(next_error_));
return; return;
} }
GetManager()->OnSoundEnd(session_id); GetManager()->OnSoundEnd(kSessionIDForTests);
GetManager()->OnAudioEnd(session_id); GetManager()->OnAudioEnd(kSessionIDForTests);
GetManager()->OnRecognitionResult(session_id, next_result_); GetManager()->OnRecognitionResult(kSessionIDForTests, next_result_);
GetManager()->OnRecognitionEnd(kSessionIDForTests);
} }
// Every test should leave the manager in the idle state when finished. // Every test should leave the manager in the idle state when finished.
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include "chrome/browser/profiles/profile_dependency_manager.h" #include "chrome/browser/profiles/profile_dependency_manager.h"
#include "chrome/browser/profiles/profile_keyed_service.h" #include "chrome/browser/profiles/profile_keyed_service.h"
#include "chrome/browser/profiles/profile_keyed_service_factory.h" #include "chrome/browser/profiles/profile_keyed_service_factory.h"
#include "chrome/browser/speech/speech_recognition_tray_icon_controller.h"
#include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
...@@ -23,9 +22,11 @@ ...@@ -23,9 +22,11 @@
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h" #include "content/public/browser/notification_service.h"
#include "content/public/browser/speech_recognition_manager.h" #include "content/public/browser/speech_recognition_manager.h"
#include "content/public/browser/speech_recognizer.h" #include "content/public/browser/speech_recognition_session_config.h"
#include "content/public/browser/speech_recognition_session_context.h"
#include "content/public/common/speech_recognition_error.h" #include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h" #include "content/public/common/speech_recognition_result.h"
#include "net/url_request/url_request_context_getter.h"
using content::BrowserThread; using content::BrowserThread;
using content::SpeechRecognitionHypothesis; using content::SpeechRecognitionHypothesis;
...@@ -55,12 +56,6 @@ const char kOnResultEvent[] = "experimental.speechInput.onResult"; ...@@ -55,12 +56,6 @@ const char kOnResultEvent[] = "experimental.speechInput.onResult";
const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart"; const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart";
const char kOnSoundEndEvent[] = "experimental.speechInput.onSoundEnd"; const char kOnSoundEndEvent[] = "experimental.speechInput.onSoundEnd";
// Session id provided to the speech recognizer. Since only one extension can
// be recording on the same time a constant value is enough as id.
// TODO(primiano) this will not be valid anymore once speech input extension
// will use the SpeechRecognitionManager and not the SpeechRecognizer directly.
static const int kSpeechInputSessionId = 1;
// Wrap an SpeechInputExtensionManager using scoped_refptr to avoid // Wrap an SpeechInputExtensionManager using scoped_refptr to avoid
// assertion failures on destruction because of not using release(). // assertion failures on destruction because of not using release().
class SpeechInputExtensionManagerWrapper : public ProfileKeyedService { class SpeechInputExtensionManagerWrapper : public ProfileKeyedService {
...@@ -157,7 +152,10 @@ SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile) ...@@ -157,7 +152,10 @@ SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile)
: profile_(profile), : profile_(profile),
state_(kIdle), state_(kIdle),
registrar_(new content::NotificationRegistrar), registrar_(new content::NotificationRegistrar),
speech_interface_(NULL) { speech_interface_(NULL),
is_recognition_in_progress_(false),
speech_recognition_session_id_(
SpeechRecognitionManager::kSessionIDInvalid) {
registrar_->Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, registrar_->Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
content::Source<Profile>(profile_)); content::Source<Profile>(profile_));
} }
...@@ -194,11 +192,17 @@ void SpeechInputExtensionManager::ShutdownOnUIThread() { ...@@ -194,11 +192,17 @@ void SpeechInputExtensionManager::ShutdownOnUIThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Profile shutting down."; VLOG(1) << "Profile shutting down.";
// Note: Unretained(this) is safe, also if we are freed in the meanwhile.
// It is used by the SR manager just for comparing the raw pointer and remove
// the associated sessions.
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SpeechRecognitionManager::AbortAllSessionsForListener,
base::Unretained(SpeechRecognitionManager::GetInstance()),
base::Unretained(this)));
base::AutoLock auto_lock(state_lock_); base::AutoLock auto_lock(state_lock_);
DCHECK(state_ != kShutdown); DCHECK(state_ != kShutdown);
if (state_ != kIdle) { if (state_ != kIdle) {
DCHECK(notification_.get());
notification_->Hide();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this)); base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
} }
...@@ -245,7 +249,7 @@ void SpeechInputExtensionManager::OnRecognitionResult( ...@@ -245,7 +249,7 @@ void SpeechInputExtensionManager::OnRecognitionResult(
int session_id, int session_id,
const content::SpeechRecognitionResult& result) { const content::SpeechRecognitionResult& result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(session_id, kSpeechInputSessionId); DCHECK_EQ(session_id, speech_recognition_session_id_);
// Stopping will start the disassociation with the extension. // Stopping will start the disassociation with the extension.
// Make a copy to report the results to the proper one. // Make a copy to report the results to the proper one.
...@@ -288,13 +292,13 @@ void SpeechInputExtensionManager::SetRecognitionResultOnUIThread( ...@@ -288,13 +292,13 @@ void SpeechInputExtensionManager::SetRecognitionResultOnUIThread(
} }
void SpeechInputExtensionManager::OnRecognitionStart(int session_id) { void SpeechInputExtensionManager::OnRecognitionStart(int session_id) {
DCHECK_EQ(session_id, kSpeechInputSessionId); DCHECK_EQ(session_id, speech_recognition_session_id_);
} }
void SpeechInputExtensionManager::OnAudioStart(int session_id) { void SpeechInputExtensionManager::OnAudioStart(int session_id) {
VLOG(1) << "OnAudioStart"; VLOG(1) << "OnAudioStart";
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(session_id, kSpeechInputSessionId); DCHECK_EQ(session_id, speech_recognition_session_id_);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread, base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread,
...@@ -302,11 +306,16 @@ void SpeechInputExtensionManager::OnAudioStart(int session_id) { ...@@ -302,11 +306,16 @@ void SpeechInputExtensionManager::OnAudioStart(int session_id) {
} }
void SpeechInputExtensionManager::OnAudioEnd(int session_id) { void SpeechInputExtensionManager::OnAudioEnd(int session_id) {
DCHECK_EQ(session_id, kSpeechInputSessionId);
} }
void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) { void SpeechInputExtensionManager::OnRecognitionEnd(int session_id) {
DCHECK_EQ(session_id, kSpeechInputSessionId); // In the very exceptional case in which we requested a new recognition before
// the previous one ended, don't clobber the speech_recognition_session_id_.
if (speech_recognition_session_id_ == session_id) {
is_recognition_in_progress_ = false;
speech_recognition_session_id_ =
SpeechRecognitionManager::kSessionIDInvalid;
}
} }
void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() { void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
...@@ -320,21 +329,9 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() { ...@@ -320,21 +329,9 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
VLOG(1) << "State changed to recording"; VLOG(1) << "State changed to recording";
state_ = kRecording; state_ = kRecording;
const extensions::Extension* extension = profile_->GetExtensionService()-> DCHECK(profile_);
GetExtensionById(extension_id_in_use_, true);
DCHECK(extension);
bool show_notification = !profile_->GetPrefs()->GetBoolean(
prefs::kSpeechInputTrayNotificationShown);
if (!notification_.get())
notification_ = new SpeechRecognitionTrayIconController();
notification_->Show(UTF8ToUTF16(extension->name()), show_notification);
if (show_notification) {
profile_->GetPrefs()->SetBoolean( profile_->GetPrefs()->SetBoolean(
prefs::kSpeechInputTrayNotificationShown, true); prefs::kSpeechInputTrayNotificationShown, true);
}
VLOG(1) << "Sending start notification"; VLOG(1) << "Sending start notification";
content::NotificationService::current()->Notify( content::NotificationService::current()->Notify(
...@@ -346,20 +343,13 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() { ...@@ -346,20 +343,13 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
void SpeechInputExtensionManager::OnRecognitionError( void SpeechInputExtensionManager::OnRecognitionError(
int session_id, const content::SpeechRecognitionError& error) { int session_id, const content::SpeechRecognitionError& error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(session_id, kSpeechInputSessionId); DCHECK_EQ(session_id, speech_recognition_session_id_);
// Simply return in case of an ERROR_ABORTED, since it is not contemplated
// in the speech input extensions architecture.
if (error.code == content::SPEECH_RECOGNITION_ERROR_ABORTED)
return;
VLOG(1) << "OnRecognitionError: " << error.code; VLOG(1) << "OnRecognitionError: " << error.code;
base::AutoLock auto_lock(state_lock_); base::AutoLock auto_lock(state_lock_);
if (state_ == kShutdown) if (state_ == kShutdown)
return; return;
// Release the recognizer object.
GetSpeechInputExtensionInterface()->StopRecording(true); GetSpeechInputExtensionInterface()->StopRecording(true);
std::string event_error_code; std::string event_error_code;
...@@ -369,6 +359,15 @@ void SpeechInputExtensionManager::OnRecognitionError( ...@@ -369,6 +359,15 @@ void SpeechInputExtensionManager::OnRecognitionError(
case content::SPEECH_RECOGNITION_ERROR_NONE: case content::SPEECH_RECOGNITION_ERROR_NONE:
break; break;
case content::SPEECH_RECOGNITION_ERROR_ABORTED:
// ERROR_ABORTED is received whenever AbortSession is called on the
// manager. However, we want propagate the error only if it is triggered
// by an external cause (another recognition started, aborting us), thus
// only if it occurs while we are capturing audio.
if (state_ == kRecording)
event_error_code = kErrorCaptureError;
break;
case content::SPEECH_RECOGNITION_ERROR_AUDIO: case content::SPEECH_RECOGNITION_ERROR_AUDIO:
if (state_ == kStarting) { if (state_ == kStarting) {
event_error_code = kErrorUnableToStart; event_error_code = kErrorUnableToStart;
...@@ -411,12 +410,12 @@ void SpeechInputExtensionManager::OnRecognitionError( ...@@ -411,12 +410,12 @@ void SpeechInputExtensionManager::OnRecognitionError(
void SpeechInputExtensionManager::OnEnvironmentEstimationComplete( void SpeechInputExtensionManager::OnEnvironmentEstimationComplete(
int session_id) { int session_id) {
DCHECK_EQ(session_id, kSpeechInputSessionId); DCHECK_EQ(session_id, speech_recognition_session_id_);
} }
void SpeechInputExtensionManager::OnSoundStart(int session_id) { void SpeechInputExtensionManager::OnSoundStart(int session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(session_id, kSpeechInputSessionId); DCHECK_EQ(session_id, speech_recognition_session_id_);
VLOG(1) << "OnSoundStart"; VLOG(1) << "OnSoundStart";
std::string json_args; std::string json_args;
...@@ -428,7 +427,6 @@ void SpeechInputExtensionManager::OnSoundStart(int session_id) { ...@@ -428,7 +427,6 @@ void SpeechInputExtensionManager::OnSoundStart(int session_id) {
void SpeechInputExtensionManager::OnSoundEnd(int session_id) { void SpeechInputExtensionManager::OnSoundEnd(int session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(session_id, kSpeechInputSessionId);
VLOG(1) << "OnSoundEnd"; VLOG(1) << "OnSoundEnd";
std::string json_args; std::string json_args;
...@@ -471,11 +469,6 @@ void SpeechInputExtensionManager::DispatchError( ...@@ -471,11 +469,6 @@ void SpeechInputExtensionManager::DispatchError(
if (state_ == kShutdown) if (state_ == kShutdown)
return; return;
if (state_ == kRecording) {
DCHECK(notification_.get());
notification_->Hide();
}
extension_id = extension_id_in_use_; extension_id = extension_id_in_use_;
ResetToIdleState(); ResetToIdleState();
...@@ -531,23 +524,37 @@ bool SpeechInputExtensionManager::Start( ...@@ -531,23 +524,37 @@ bool SpeechInputExtensionManager::Start(
NOTREACHED(); NOTREACHED();
} }
const extensions::Extension* extension = profile_->GetExtensionService()->
GetExtensionById(extension_id, true);
DCHECK(extension);
const string16& extension_name = UTF8ToUTF16(extension->name());
extension_id_in_use_ = extension_id; extension_id_in_use_ = extension_id;
VLOG(1) << "State changed to starting"; VLOG(1) << "State changed to starting";
state_ = kStarting; state_ = kStarting;
// Checks if the security notification balloon has been already shown (only
// once for a profile). It is reset on DidStartReceivingAudioOnUIThread.
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
profile_->GetRequestContext();
const bool show_notification = !profile_->GetPrefs()->GetBoolean(
prefs::kSpeechInputTrayNotificationShown);
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, BrowserThread::IO, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this, base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this,
profile_->GetRequestContext(), language, grammar, url_request_context_getter, extension_name, language, grammar,
filter_profanities)); filter_profanities, show_notification));
return true; return true;
} }
void SpeechInputExtensionManager::StartOnIOThread( void SpeechInputExtensionManager::StartOnIOThread(
net::URLRequestContextGetter* context_getter, scoped_refptr<net::URLRequestContextGetter> context_getter,
const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities) { bool filter_profanities,
bool show_notification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
VLOG(1) << "Requesting start (IO thread)"; VLOG(1) << "Requesting start (IO thread)";
...@@ -558,6 +565,12 @@ void SpeechInputExtensionManager::StartOnIOThread( ...@@ -558,6 +565,12 @@ void SpeechInputExtensionManager::StartOnIOThread(
if (state_ == kShutdown) if (state_ == kShutdown)
return; return;
// TODO(primiano) These two checks below could be avoided, since they are
// already handled in the speech recognition classes. However, since the
// speech input extensions tests are bypassing the manager, we need them to
// pass the tests. A complete unit test which puts all the pieces together,
// mocking just the endpoints (the audio input controller and the URL fetcher)
// should be written.
if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) { if (!GetSpeechInputExtensionInterface()->HasAudioInputDevices()) {
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, BrowserThread::UI, FROM_HERE,
...@@ -573,9 +586,13 @@ void SpeechInputExtensionManager::StartOnIOThread( ...@@ -573,9 +586,13 @@ void SpeechInputExtensionManager::StartOnIOThread(
return; return;
} }
GetSpeechInputExtensionInterface()->StartRecording( GetSpeechInputExtensionInterface()->StartRecording(this,
this, context_getter, kSpeechInputSessionId, language, grammar, context_getter,
filter_profanities); extension_name,
language,
grammar,
filter_profanities,
show_notification);
} }
bool SpeechInputExtensionManager::HasAudioInputDevices() { bool SpeechInputExtensionManager::HasAudioInputDevices() {
...@@ -602,6 +619,7 @@ void SpeechInputExtensionManager::IsRecordingOnIOThread( ...@@ -602,6 +619,7 @@ void SpeechInputExtensionManager::IsRecordingOnIOThread(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio(); bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio();
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, BrowserThread::UI, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread, base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread,
...@@ -618,20 +636,41 @@ void SpeechInputExtensionManager::IsRecordingOnUIThread( ...@@ -618,20 +636,41 @@ void SpeechInputExtensionManager::IsRecordingOnUIThread(
void SpeechInputExtensionManager::StartRecording( void SpeechInputExtensionManager::StartRecording(
content::SpeechRecognitionEventListener* listener, content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter, net::URLRequestContextGetter* context_getter,
int session_id, const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities) { bool filter_profanities,
bool show_notification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!recognizer_);
recognizer_ = content::SpeechRecognizer::Create( content::SpeechRecognitionSessionContext context;
listener, session_id, language, grammar, context_getter, context.requested_by_page_element = false;
filter_profanities, "", ""); context.is_first_request_for_context = show_notification;
recognizer_->StartRecognition(); context.context_name = extension_name;
content::SpeechRecognitionSessionConfig config;
// config.is_one_shot = true; // TODO(primiano) Uncomment when CL2.0 lands.
config.language = language;
config.grammars.push_back(content::SpeechRecognitionGrammar(grammar));
config.initial_context = context;
config.url_request_context_getter = context_getter;
config.filter_profanities = filter_profanities;
config.event_listener = listener;
DCHECK(!is_recognition_in_progress_);
SpeechRecognitionManager& manager = *SpeechRecognitionManager::GetInstance();
speech_recognition_session_id_ =
manager.CreateSession(config);
DCHECK_NE(speech_recognition_session_id_,
SpeechRecognitionManager::kSessionIDInvalid);
is_recognition_in_progress_ = true;
manager.StartSession(speech_recognition_session_id_);
} }
bool SpeechInputExtensionManager::HasValidRecognizer() { bool SpeechInputExtensionManager::HasValidRecognizer() {
return !!recognizer_; if (!is_recognition_in_progress_)
return false;
return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
} }
bool SpeechInputExtensionManager::Stop(const std::string& extension_id, bool SpeechInputExtensionManager::Stop(const std::string& extension_id,
...@@ -692,13 +731,13 @@ void SpeechInputExtensionManager::ForceStopOnIOThread() { ...@@ -692,13 +731,13 @@ void SpeechInputExtensionManager::ForceStopOnIOThread() {
} }
void SpeechInputExtensionManager::StopRecording(bool recognition_failed) { void SpeechInputExtensionManager::StopRecording(bool recognition_failed) {
if (recognizer_) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Recognition is already cancelled in case of failure. if (!is_recognition_in_progress_)
// Double-cancelling leads to assertion failures. return;
if (!recognition_failed) DCHECK_NE(speech_recognition_session_id_,
recognizer_->AbortRecognition(); SpeechRecognitionManager::kSessionIDInvalid);
recognizer_.release(); SpeechRecognitionManager::GetInstance()->AbortSession(
} speech_recognition_session_id_);
} }
void SpeechInputExtensionManager::StopSucceededOnUIThread() { void SpeechInputExtensionManager::StopSucceededOnUIThread() {
...@@ -712,9 +751,6 @@ void SpeechInputExtensionManager::StopSucceededOnUIThread() { ...@@ -712,9 +751,6 @@ void SpeechInputExtensionManager::StopSucceededOnUIThread() {
std::string extension_id = extension_id_in_use_; std::string extension_id = extension_id_in_use_;
ResetToIdleState(); ResetToIdleState();
DCHECK(notification_.get());
notification_->Hide();
content::NotificationService::current()->Notify( content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED, chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED,
// Guarded by the state_ == kShutdown check. // Guarded by the state_ == kShutdown check.
...@@ -724,17 +760,4 @@ void SpeechInputExtensionManager::StopSucceededOnUIThread() { ...@@ -724,17 +760,4 @@ void SpeechInputExtensionManager::StopSucceededOnUIThread() {
void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id, void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id,
float volume, float volume,
float noise_volume) { float noise_volume) {}
DCHECK_EQ(session_id, kSpeechInputSessionId);
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::SetInputVolumeOnUIThread,
this, volume));
}
void SpeechInputExtensionManager::SetInputVolumeOnUIThread(
float volume) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(notification_.get());
notification_->SetVUMeterVolume(volume);
}
...@@ -11,18 +11,18 @@ ...@@ -11,18 +11,18 @@
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_observer.h"
#include "content/public/browser/speech_recognition_event_listener.h" #include "content/public/browser/speech_recognition_event_listener.h"
class Profile; class Profile;
class SpeechRecognitionTrayIconController;
namespace content { namespace content {
class NotificationRegistrar; class NotificationRegistrar;
struct SpeechRecognitionError; struct SpeechRecognitionError;
class SpeechRecognitionManager;
struct SpeechRecognitionResult; struct SpeechRecognitionResult;
class SpeechRecognizer;
} }
namespace net { namespace net {
...@@ -39,10 +39,11 @@ class SpeechInputExtensionInterface { ...@@ -39,10 +39,11 @@ class SpeechInputExtensionInterface {
virtual void StartRecording( virtual void StartRecording(
content::SpeechRecognitionEventListener* listener, content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter, net::URLRequestContextGetter* context_getter,
int session_id, const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities) = 0; bool filter_profanities,
bool show_notification) = 0;
virtual void StopRecording(bool recognition_failed) = 0; virtual void StopRecording(bool recognition_failed) = 0;
virtual bool HasAudioInputDevices() = 0; virtual bool HasAudioInputDevices() = 0;
...@@ -143,19 +144,22 @@ class SpeechInputExtensionManager ...@@ -143,19 +144,22 @@ class SpeechInputExtensionManager
virtual void StartRecording( virtual void StartRecording(
content::SpeechRecognitionEventListener* listener, content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter, net::URLRequestContextGetter* context_getter,
int session_id, const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities) OVERRIDE; bool filter_profanities,
bool show_notification) OVERRIDE;
virtual void StopRecording(bool recognition_failed) OVERRIDE; virtual void StopRecording(bool recognition_failed) OVERRIDE;
// Internal methods. // Internal methods.
void StartOnIOThread( void StartOnIOThread(
net::URLRequestContextGetter* context_getter, scoped_refptr<net::URLRequestContextGetter> context_getter,
const string16& extension_name,
const std::string& language, const std::string& language,
const std::string& grammar, const std::string& grammar,
bool filter_profanities); bool filter_profanities,
bool show_notification);
void ForceStopOnIOThread(); void ForceStopOnIOThread();
void IsRecordingOnIOThread(const IsRecordingCallback& callback); void IsRecordingOnIOThread(const IsRecordingCallback& callback);
...@@ -172,7 +176,6 @@ class SpeechInputExtensionManager ...@@ -172,7 +176,6 @@ class SpeechInputExtensionManager
const std::string& json_args); const std::string& json_args);
void ExtensionUnloaded(const std::string& extension_id); void ExtensionUnloaded(const std::string& extension_id);
void SetInputVolumeOnUIThread(float volume);
void ResetToIdleState(); void ResetToIdleState();
virtual ~SpeechInputExtensionManager(); virtual ~SpeechInputExtensionManager();
...@@ -196,10 +199,10 @@ class SpeechInputExtensionManager ...@@ -196,10 +199,10 @@ class SpeechInputExtensionManager
// Used in the UI thread. // Used in the UI thread.
scoped_ptr<content::NotificationRegistrar> registrar_; scoped_ptr<content::NotificationRegistrar> registrar_;
SpeechInputExtensionInterface* speech_interface_; SpeechInputExtensionInterface* speech_interface_;
scoped_refptr<SpeechRecognitionTrayIconController> notification_;
// Used in the IO thread. // Used in the IO thread.
scoped_refptr<content::SpeechRecognizer> recognizer_; bool is_recognition_in_progress_;
int speech_recognition_session_id_;
}; };
#endif // CHROME_BROWSER_SPEECH_SPEECH_INPUT_EXTENSION_MANAGER_H_ #endif // CHROME_BROWSER_SPEECH_SPEECH_INPUT_EXTENSION_MANAGER_H_
...@@ -327,6 +327,12 @@ void SpeechRecognitionManagerImpl::AbortAllSessionsForListener( ...@@ -327,6 +327,12 @@ void SpeechRecognitionManagerImpl::AbortAllSessionsForListener(
void SpeechRecognitionManagerImpl::DispatchEvent(int session_id, void SpeechRecognitionManagerImpl::DispatchEvent(int session_id,
FSMEvent event) { FSMEvent event) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// There are some corner cases in which the session might be deleted (due to
// an EndRecognition event) between a request (e.g. Abort) and its dispatch.
if (!SessionExists(session_id))
return;
const Session& session = GetSession(session_id); const Session& session = GetSession(session_id);
FSMState session_state = GetSessionState(session_id); FSMState session_state = GetSessionState(session_id);
DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE); DCHECK_LE(session_state, SESSION_STATE_MAX_VALUE);
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "content/browser/speech/google_one_shot_remote_engine.h" #include "content/browser/speech/google_one_shot_remote_engine.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/speech_recognition_event_listener.h" #include "content/public/browser/speech_recognition_event_listener.h"
#include "content/public/browser/speech_recognizer.h"
#include "content/public/common/speech_recognition_error.h" #include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_grammar.h" #include "content/public/common/speech_recognition_grammar.h"
#include "content/public/common/speech_recognition_result.h" #include "content/public/common/speech_recognition_result.h"
...@@ -24,7 +23,6 @@ using content::SpeechRecognitionError; ...@@ -24,7 +23,6 @@ using content::SpeechRecognitionError;
using content::SpeechRecognitionEventListener; using content::SpeechRecognitionEventListener;
using content::SpeechRecognitionGrammar; using content::SpeechRecognitionGrammar;
using content::SpeechRecognitionResult; using content::SpeechRecognitionResult;
using content::SpeechRecognizer;
using media::AudioInputController; using media::AudioInputController;
using media::AudioManager; using media::AudioManager;
using media::AudioParameters; using media::AudioParameters;
...@@ -68,41 +66,6 @@ void KeepAudioControllerRefcountedForDtor(scoped_refptr<AudioInputController>) { ...@@ -68,41 +66,6 @@ void KeepAudioControllerRefcountedForDtor(scoped_refptr<AudioInputController>) {
} // namespace } // namespace
// TODO(primiano) Create(...) is transitional (until we fix speech input
// extensions) and should be removed soon. The manager should be the only one
// knowing the existence of SpeechRecognizer(Impl), thus the only one in charge
// of instantiating it.
SpeechRecognizer* SpeechRecognizer::Create(
SpeechRecognitionEventListener* listener,
int session_id,
const std::string& language,
const std::string& grammar,
net::URLRequestContextGetter* context_getter,
bool filter_profanities,
const std::string& hardware_info,
const std::string& origin_url) {
speech::SpeechRecognitionEngineConfig remote_engine_config;
remote_engine_config.language = language;
if (!grammar.empty())
remote_engine_config.grammars.push_back(SpeechRecognitionGrammar(grammar));
remote_engine_config.audio_sample_rate =
speech::SpeechRecognizerImpl::kAudioSampleRate;
remote_engine_config.audio_num_bits_per_sample =
speech::SpeechRecognizerImpl::kNumBitsPerAudioSample;
remote_engine_config.filter_profanities = filter_profanities;
remote_engine_config.hardware_info = hardware_info;
remote_engine_config.origin_url = origin_url;
// SpeechRecognizerImpl takes ownership of google_remote_engine.
speech::GoogleOneShotRemoteEngine* google_remote_engine =
new speech::GoogleOneShotRemoteEngine(context_getter);
google_remote_engine->SetConfig(remote_engine_config);
return new speech::SpeechRecognizerImpl(listener,
session_id,
google_remote_engine);
}
namespace speech { namespace speech {
const int SpeechRecognizerImpl::kAudioSampleRate = 16000; const int SpeechRecognizerImpl::kAudioSampleRate = 16000;
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "content/browser/speech/endpointer/endpointer.h" #include "content/browser/speech/endpointer/endpointer.h"
#include "content/browser/speech/speech_recognition_engine.h" #include "content/browser/speech/speech_recognition_engine.h"
#include "content/public/browser/speech_recognizer.h"
#include "content/public/common/speech_recognition_error.h" #include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h" #include "content/public/common/speech_recognition_result.h"
#include "media/audio/audio_input_controller.h" #include "media/audio/audio_input_controller.h"
...@@ -27,15 +26,13 @@ class AudioManager; ...@@ -27,15 +26,13 @@ class AudioManager;
namespace speech { namespace speech {
// TODO(primiano) Next CL: Remove the Impl suffix and the exported // TODO(primiano) Next CL: Remove the Impl suffix.
// /content/public/browser/speech_recognizer.h interface since this class should
// not be visible outside (currently we need it for speech input extension API).
// Handles speech recognition for a session (identified by |session_id|), taking // Handles speech recognition for a session (identified by |session_id|), taking
// care of audio capture, silence detection/endpointer and interaction with the // care of audio capture, silence detection/endpointer and interaction with the
// SpeechRecognitionEngine. // SpeechRecognitionEngine.
class CONTENT_EXPORT SpeechRecognizerImpl class CONTENT_EXPORT SpeechRecognizerImpl
: public NON_EXPORTED_BASE(content::SpeechRecognizer), : public base::RefCountedThreadSafe<SpeechRecognizerImpl>,
public media::AudioInputController::EventHandler, public media::AudioInputController::EventHandler,
public NON_EXPORTED_BASE(SpeechRecognitionEngineDelegate) { public NON_EXPORTED_BASE(SpeechRecognitionEngineDelegate) {
public: public:
...@@ -50,18 +47,15 @@ class CONTENT_EXPORT SpeechRecognizerImpl ...@@ -50,18 +47,15 @@ class CONTENT_EXPORT SpeechRecognizerImpl
int session_id, int session_id,
SpeechRecognitionEngine* engine); SpeechRecognitionEngine* engine);
// content::SpeechRecognizer methods. void StartRecognition();
virtual void StartRecognition() OVERRIDE; void AbortRecognition();
virtual void AbortRecognition() OVERRIDE; void StopAudioCapture();
virtual void StopAudioCapture() OVERRIDE; bool IsActive() const;
virtual bool IsActive() const OVERRIDE; bool IsCapturingAudio() const;
virtual bool IsCapturingAudio() const OVERRIDE;
const SpeechRecognitionEngine& recognition_engine() const; const SpeechRecognitionEngine& recognition_engine() const;
protected:
virtual ~SpeechRecognizerImpl();
private: private:
friend class base::RefCountedThreadSafe<SpeechRecognizerImpl>;
friend class SpeechRecognizerImplTest; friend class SpeechRecognizerImplTest;
enum FSMState { enum FSMState {
...@@ -96,6 +90,8 @@ class CONTENT_EXPORT SpeechRecognizerImpl ...@@ -96,6 +90,8 @@ class CONTENT_EXPORT SpeechRecognizerImpl
content::SpeechRecognitionError engine_error; content::SpeechRecognitionError engine_error;
}; };
virtual ~SpeechRecognizerImpl();
// Entry point for pushing any new external event into the recognizer FSM. // Entry point for pushing any new external event into the recognizer FSM.
void DispatchEvent(const FSMEventArgs& event_args); void DispatchEvent(const FSMEventArgs& event_args);
......
...@@ -139,8 +139,8 @@ ...@@ -139,8 +139,8 @@
'public/browser/speech_recognition_preferences.h', 'public/browser/speech_recognition_preferences.h',
'public/browser/speech_recognition_session_config.cc', 'public/browser/speech_recognition_session_config.cc',
'public/browser/speech_recognition_session_config.h', 'public/browser/speech_recognition_session_config.h',
'public/browser/speech_recognition_session_context.cc',
'public/browser/speech_recognition_session_context.h', 'public/browser/speech_recognition_session_context.h',
'public/browser/speech_recognizer.h',
'public/browser/trace_controller.h', 'public/browser/trace_controller.h',
'public/browser/trace_subscriber.h', 'public/browser/trace_subscriber.h',
'public/browser/user_metrics.h', 'public/browser/user_metrics.h',
......
// 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/public/browser/speech_recognition_session_context.h"
namespace content {
SpeechRecognitionSessionContext::SpeechRecognitionSessionContext()
: render_process_id(0),
render_view_id(0),
render_request_id(0),
requested_by_page_element(true),
is_first_request_for_context(false) {
}
SpeechRecognitionSessionContext::~SpeechRecognitionSessionContext() {
}
} // namespace content
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CONTENT_PUBLIC_BROWSER_SPEECH_RECOGNITION_SESSION_CONTEXT_H_ #define CONTENT_PUBLIC_BROWSER_SPEECH_RECOGNITION_SESSION_CONTEXT_H_
#pragma once #pragma once
#include "base/string16.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "ui/gfx/rect.h" #include "ui/gfx/rect.h"
...@@ -21,18 +22,32 @@ namespace content { ...@@ -21,18 +22,32 @@ namespace content {
// SpeechRecognitionManager::GetSessionContext and // SpeechRecognitionManager::GetSessionContext and
// SpeechRecognitionManager::LookupSessionByContext methods). // SpeechRecognitionManager::LookupSessionByContext methods).
struct CONTENT_EXPORT SpeechRecognitionSessionContext { struct CONTENT_EXPORT SpeechRecognitionSessionContext {
SpeechRecognitionSessionContext() SpeechRecognitionSessionContext();
: render_process_id(0), ~SpeechRecognitionSessionContext();
render_view_id(0),
render_request_id(0),
js_handle_id(0) {}
~SpeechRecognitionSessionContext() {}
int render_process_id; int render_process_id;
int render_view_id; int render_view_id;
int render_request_id; int render_request_id;
int js_handle_id; int js_handle_id;
// Determines whether recognition was requested by a page element (in which
// case its coordinates are passed in |element_rect|).
bool requested_by_page_element;
// The coordinates of the page element for placing the bubble (valid only when
// |requested_by_page_element| = true).
gfx::Rect element_rect; gfx::Rect element_rect;
// Determines whether this is the first time that this context (identified by
// |context_name|) is requesting a recognition.
// TODO(primiano) This is really temporary, remove after CL1.12 which will
// refactor SpeechRecognitionPreferences and move this check entirely whithin
// chrome, without involving content.
bool is_first_request_for_context;
// A texual description of the context (website, extension name) that is
// requesting recognition, for prompting security notifications to the user.
string16 context_name;
}; };
} // namespace content } // namespace content
......
// 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 CONTENT_PUBLIC_BROWSER_SPEECH_RECOGNIZER_H_
#define CONTENT_PUBLIC_BROWSER_SPEECH_RECOGNIZER_H_
#pragma once
#include <string>
#include "base/memory/ref_counted.h"
#include "content/common/content_export.h"
namespace net {
class URLRequestContextGetter;
}
namespace content {
class SpeechRecognitionEventListener;
// Records audio, sends recorded audio to server and translates server response
// to recognition result.
// TODO(primiano) remove this public interface as soon as we fix speech input
// extensions.
class SpeechRecognizer : public base::RefCountedThreadSafe<SpeechRecognizer> {
public:
CONTENT_EXPORT static SpeechRecognizer* Create(
SpeechRecognitionEventListener* event_listener,
int session_id,
const std::string& language,
const std::string& grammar,
net::URLRequestContextGetter* context_getter,
bool filter_profanities,
const std::string& hardware_info,
const std::string& origin_url);
// Starts audio recording and the recognition process. The same
// SpeechRecognizer instance can be used multiple times for speech recognition
// though each recognition request can be made only after the previous one
// completes (i.e. after receiving
// SpeechRecognitionEventListener::OnRecognitionEnd).
virtual void StartRecognition() = 0;
// Stops recording audio and cancels recognition. Any audio recorded so far
// gets discarded.
virtual void AbortRecognition() = 0;
// Stops recording audio and finalizes recognition, possibly getting results.
virtual void StopAudioCapture() = 0;
// Checks wether the recognizer is active, that is, either capturing audio
// or waiting for a result.
virtual bool IsActive() const = 0;
// Checks whether the recognizer is capturing audio.
virtual bool IsCapturingAudio() const = 0;
protected:
friend class base::RefCountedThreadSafe<SpeechRecognizer>;
virtual ~SpeechRecognizer() {}
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_SPEECH_RECOGNIZER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment