summaryrefslogtreecommitdiffstats
path: root/chrome/browser/speech
diff options
context:
space:
mode:
authorprimiano@chromium.org <primiano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-24 10:58:24 +0000
committerprimiano@chromium.org <primiano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-24 10:58:24 +0000
commitd305111d5d25b32277e9b844832d19e08321db80 (patch)
tree62794788d05700882f2c4b6133df6bf8460a18fa /chrome/browser/speech
parent909fd560e81d4df154acfff63dd276297842b3b8 (diff)
downloadchromium_src-d305111d5d25b32277e9b844832d19e08321db80.zip
chromium_src-d305111d5d25b32277e9b844832d19e08321db80.tar.gz
chromium_src-d305111d5d25b32277e9b844832d19e08321db80.tar.bz2
SpeechInputExtensionManager now interface (exclusively) with SpeechRecognitionManagerDelegate (Speech CL1.11)
This is a re-submit of r138498 which was reverted in r138568. Fixes introduced by this CL: - Added a check on speech_input_extensions_manager shutdown to assert that the speech_recognition_manager has not been unexpectedly destroyed (happens in some tests). - Added WeakPtr to speech_recognition_manager_impl so that it can be destroyed ungracefully without crashing. Original CL description: - 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,129408 TEST=browser_tests on Linux ChromiumOS (dbg) Review URL: https://chromiumcodereview.appspot.com/10440011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138765 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/speech')
-rw-r--r--chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc130
-rw-r--r--chrome/browser/speech/chrome_speech_recognition_manager_delegate.h7
-rw-r--r--chrome/browser/speech/speech_input_extension_apitest.cc35
-rw-r--r--chrome/browser/speech/speech_input_extension_manager.cc208
-rw-r--r--chrome/browser/speech/speech_input_extension_manager.h27
5 files changed, 265 insertions, 142 deletions
diff --git a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
index 6b0162c..b771ac7 100644
--- a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
+++ b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.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/view_type_utils.h"
#include "chrome/common/pref_names.h"
@@ -36,10 +37,20 @@
using content::BrowserThread;
using content::SpeechRecognitionManager;
using content::WebContents;
+using content::SpeechRecognitionSessionContext;
namespace {
const int kNoActiveBubble =
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 speech {
@@ -108,13 +119,17 @@ class ChromeSpeechRecognitionManagerDelegate::OptionalRequestInfo
};
ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate()
- : bubble_controller_(new SpeechRecognitionBubbleController(
- ALLOW_THIS_IN_INITIALIZER_LIST(this))),
- active_bubble_session_id_(kNoActiveBubble) {
+ : active_bubble_session_id_(kNoActiveBubble) {
}
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(
@@ -122,8 +137,11 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
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) {
- bubble_controller_->CloseBubble(session_id);
+ GetBubbleController()->CloseBubble(session_id);
last_session_config_.reset();
active_bubble_session_id_ = kNoActiveBubble;
@@ -131,7 +149,7 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
// the manager's public methods are reliable and will handle it properly.
SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
} else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) {
- bubble_controller_->CloseBubble(session_id);
+ GetBubbleController()->CloseBubble(session_id);
active_bubble_session_id_ = kNoActiveBubble;
RestartLastSession();
}
@@ -142,7 +160,10 @@ void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
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();
active_bubble_session_id_ = kNoActiveBubble;
@@ -164,25 +185,36 @@ void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() {
void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
int session_id) {
- // Copy the configuration of the session (for the "try again" button).
- last_session_config_.reset(new content::SpeechRecognitionSessionConfig(
- SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id)));
-
- // Create and show the bubble.
- DCHECK_EQ(active_bubble_session_id_, kNoActiveBubble);
- active_bubble_session_id_ = session_id;
const content::SpeechRecognitionSessionContext& context =
SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
- bubble_controller_->CreateBubble(session_id,
- context.render_process_id,
- context.render_view_id,
- context.element_rect);
- // TODO(primiano) Why not create directly the bubble in warmup mode?
- bubble_controller_->SetBubbleWarmUpMode(session_id);
+
+ if (RequiresBubble(session_id)) {
+ // Copy the configuration of the session (for the "try again" button).
+ last_session_config_.reset(new content::SpeechRecognitionSessionConfig(
+ SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id)));
+
+ // Create and show the bubble.
+ DCHECK_EQ(active_bubble_session_id_, kNoActiveBubble);
+ active_bubble_session_id_ = session_id;
+ GetBubbleController()->CreateBubble(session_id,
+ context.render_process_id,
+ context.render_view_id,
+ context.element_rect);
+
+ // TODO(primiano) Why not creating directly the bubble in warmup mode?
+ GetBubbleController()->SetBubbleWarmUpMode(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(
@@ -198,16 +230,19 @@ void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {
void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {
// OnAudioEnd can be also raised after an abort, when the bubble has already
// been closed.
- if (active_bubble_session_id_ == session_id)
- bubble_controller_->SetBubbleRecognizingMode(session_id);
+ if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
+ GetBubbleController()->SetBubbleRecognizingMode(session_id);
+ } else if (RequiresTrayIcon(session_id)) {
+ GetTrayIconController()->Hide();
+ }
}
void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult(
int session_id, const content::SpeechRecognitionResult& result) {
// 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).
- if (active_bubble_session_id_ == session_id) {
- bubble_controller_->CloseBubble(session_id);
+ if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
+ GetBubbleController()->CloseBubble(session_id);
last_session_config_.reset();
active_bubble_session_id_ = kNoActiveBubble;
}
@@ -218,6 +253,7 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
// An error can be dispatched when the bubble is not visible anymore.
if (active_bubble_session_id_ != session_id)
return;
+ DCHECK(RequiresBubble(session_id));
int error_message_id = 0;
switch (error.code) {
@@ -250,17 +286,29 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
NOTREACHED() << "unknown error " << error.code;
return;
}
- bubble_controller_->SetBubbleMessage(
+ GetBubbleController()->SetBubbleMessage(
session_id, l10n_util::GetStringUTF16(error_message_id));
}
void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
int session_id, float volume, float noise_volume) {
- if (active_bubble_session_id_ == session_id)
- bubble_controller_->SetBubbleInputVolume(session_id, volume, noise_volume);
+ if (active_bubble_session_id_ == session_id) {
+ 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) {
+ // 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(
@@ -286,11 +334,20 @@ void ChromeSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
int session_id,
base::Callback<void(int session_id, bool is_allowed)> callback) {
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
// CheckRenderViewType, which will issue the callback on our behalf.
+ const content::SpeechRecognitionSessionContext& context =
+ SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&CheckRenderViewType,
session_id,
@@ -330,4 +387,19 @@ void ChromeSpeechRecognitionManagerDelegate::CheckRenderViewType(
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
diff --git a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.h b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.h
index e881d06..f59e451 100644
--- a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.h
+++ b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.h
@@ -13,6 +13,8 @@
#include "content/public/browser/speech_recognition_manager_delegate.h"
#include "content/public/browser/speech_recognition_session_config.h"
+class SpeechRecognitionTrayIconController;
+
namespace speech {
// This is Chrome's implementation of the SpeechRecognitionManagerDelegate
@@ -69,7 +71,12 @@ class ChromeSpeechRecognitionManagerDelegate
// (which is copied into |last_session_config_|). Used for "try again".
void RestartLastSession();
+ // Lazy initializers for bubble and tray icon controller.
+ SpeechRecognitionBubbleController* GetBubbleController();
+ SpeechRecognitionTrayIconController* GetTrayIconController();
+
scoped_refptr<SpeechRecognitionBubbleController> bubble_controller_;
+ scoped_refptr<SpeechRecognitionTrayIconController> tray_icon_controller_;
scoped_refptr<OptionalRequestInfo> optional_request_info_;
scoped_ptr<content::SpeechRecognitionSessionConfig> last_session_config_;
diff --git a/chrome/browser/speech/speech_input_extension_apitest.cc b/chrome/browser/speech/speech_input_extension_apitest.cc
index cf5e7a9..3718a03 100644
--- a/chrome/browser/speech/speech_input_extension_apitest.cc
+++ b/chrome/browser/speech/speech_input_extension_apitest.cc
@@ -23,6 +23,10 @@ namespace net {
class URLRequestContextGetter;
}
+namespace {
+const int kSessionIDForTests = 0;
+}
+
// Mock class used to test the extension speech input API.
class SpeechInputExtensionApiTest : public ExtensionApiTest,
public SpeechInputExtensionInterface {
@@ -77,10 +81,11 @@ class SpeechInputExtensionApiTest : public ExtensionApiTest,
virtual void StartRecording(
content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter,
- int session_id,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities) OVERRIDE;
+ bool filter_profanities,
+ bool show_notification) OVERRIDE;
virtual void StopRecording(bool recognition_failed) OVERRIDE;
@@ -107,7 +112,7 @@ class SpeechInputExtensionApiTest : public ExtensionApiTest,
};
private:
- void ProvideResults(int session_id);
+ void ProvideResults();
bool recording_devices_available_;
bool recognizer_is_valid_;
@@ -131,10 +136,11 @@ SpeechInputExtensionApiTest::~SpeechInputExtensionApiTest() {
void SpeechInputExtensionApiTest::StartRecording(
content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter,
- int session_id,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities) {
+ bool filter_profanities,
+ bool show_notification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
recognizer_is_valid_ = true;
@@ -143,7 +149,7 @@ void SpeechInputExtensionApiTest::StartRecording(
FROM_HERE,
base::Bind(&SpeechInputExtensionManager::OnAudioStart,
GetManager(),
- session_id),
+ kSessionIDForTests),
base::TimeDelta());
// Notify sound start in the input device.
@@ -151,16 +157,14 @@ void SpeechInputExtensionApiTest::StartRecording(
FROM_HERE,
base::Bind(&SpeechInputExtensionManager::OnSoundStart,
GetManager(),
- session_id),
+ kSessionIDForTests),
base::TimeDelta());
if (result_delay_ms_ != kDontDispatchCall) {
// Dispatch the recognition results.
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
- base::Bind(&SpeechInputExtensionApiTest::ProvideResults,
- this,
- session_id),
+ base::Bind(&SpeechInputExtensionApiTest::ProvideResults, this),
base::TimeDelta::FromMilliseconds(result_delay_ms_));
}
}
@@ -170,18 +174,19 @@ void SpeechInputExtensionApiTest::StopRecording(bool recognition_failed) {
recognizer_is_valid_ = false;
}
-void SpeechInputExtensionApiTest::ProvideResults(int session_id) {
+void SpeechInputExtensionApiTest::ProvideResults() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (next_error_ != content::SPEECH_RECOGNITION_ERROR_NONE) {
GetManager()->OnRecognitionError(
- session_id, content::SpeechRecognitionError(next_error_));
+ kSessionIDForTests, content::SpeechRecognitionError(next_error_));
return;
}
- GetManager()->OnSoundEnd(session_id);
- GetManager()->OnAudioEnd(session_id);
- GetManager()->OnRecognitionResult(session_id, next_result_);
+ GetManager()->OnSoundEnd(kSessionIDForTests);
+ GetManager()->OnAudioEnd(kSessionIDForTests);
+ GetManager()->OnRecognitionResult(kSessionIDForTests, next_result_);
+ GetManager()->OnRecognitionEnd(kSessionIDForTests);
}
// Every test should leave the manager in the idle state when finished.
diff --git a/chrome/browser/speech/speech_input_extension_manager.cc b/chrome/browser/speech/speech_input_extension_manager.cc
index 047f1de..ed69815 100644
--- a/chrome/browser/speech/speech_input_extension_manager.cc
+++ b/chrome/browser/speech/speech_input_extension_manager.cc
@@ -15,7 +15,6 @@
#include "chrome/browser/profiles/profile_dependency_manager.h"
#include "chrome/browser/profiles/profile_keyed_service.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/extensions/extension.h"
#include "chrome/common/pref_names.h"
@@ -23,9 +22,11 @@
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.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_result.h"
+#include "net/url_request/url_request_context_getter.h"
using content::BrowserThread;
using content::SpeechRecognitionHypothesis;
@@ -55,12 +56,6 @@ const char kOnResultEvent[] = "experimental.speechInput.onResult";
const char kOnSoundStartEvent[] = "experimental.speechInput.onSoundStart";
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
// assertion failures on destruction because of not using release().
class SpeechInputExtensionManagerWrapper : public ProfileKeyedService {
@@ -157,7 +152,10 @@ SpeechInputExtensionManager::SpeechInputExtensionManager(Profile* profile)
: profile_(profile),
state_(kIdle),
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,
content::Source<Profile>(profile_));
}
@@ -194,11 +192,16 @@ void SpeechInputExtensionManager::ShutdownOnUIThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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(&SpeechInputExtensionManager::AbortAllSessionsOnIOThread,
+ base::Unretained(this)));
+
base::AutoLock auto_lock(state_lock_);
DCHECK(state_ != kShutdown);
if (state_ != kIdle) {
- DCHECK(notification_.get());
- notification_->Hide();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::ForceStopOnIOThread, this));
}
@@ -208,6 +211,18 @@ void SpeechInputExtensionManager::ShutdownOnUIThread() {
profile_ = NULL;
}
+void SpeechInputExtensionManager::AbortAllSessionsOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // TODO(primiano) The following check should not be really needed if the
+ // SpeechRecognitionManager and this class are destroyed in the correct order
+ // (this class first), as it is in current chrome implementation.
+ // However, it seems the some ChromiumOS tests violate the destruction order
+ // envisaged by browser_main_loop, so SpeechRecognitionmanager could have been
+ // freed by now.
+ if (SpeechRecognitionManager* mgr = SpeechRecognitionManager::GetInstance())
+ mgr->AbortAllSessionsForListener(this);
+}
+
void SpeechInputExtensionManager::ExtensionUnloaded(
const std::string& extension_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -245,7 +260,7 @@ void SpeechInputExtensionManager::OnRecognitionResult(
int session_id,
const content::SpeechRecognitionResult& result) {
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.
// Make a copy to report the results to the proper one.
@@ -288,13 +303,13 @@ void SpeechInputExtensionManager::SetRecognitionResultOnUIThread(
}
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) {
VLOG(1) << "OnAudioStart";
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK_EQ(session_id, kSpeechInputSessionId);
+ DCHECK_EQ(session_id, speech_recognition_session_id_);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread,
@@ -302,11 +317,16 @@ void SpeechInputExtensionManager::OnAudioStart(int session_id) {
}
void SpeechInputExtensionManager::OnAudioEnd(int session_id) {
- DCHECK_EQ(session_id, kSpeechInputSessionId);
}
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() {
@@ -320,21 +340,9 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
VLOG(1) << "State changed to recording";
state_ = kRecording;
- const extensions::Extension* extension = profile_->GetExtensionService()->
- 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(
- prefs::kSpeechInputTrayNotificationShown, true);
- }
+ DCHECK(profile_);
+ profile_->GetPrefs()->SetBoolean(
+ prefs::kSpeechInputTrayNotificationShown, true);
VLOG(1) << "Sending start notification";
content::NotificationService::current()->Notify(
@@ -346,20 +354,13 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
void SpeechInputExtensionManager::OnRecognitionError(
int session_id, const content::SpeechRecognitionError& error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK_EQ(session_id, kSpeechInputSessionId);
-
- // 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;
-
+ DCHECK_EQ(session_id, speech_recognition_session_id_);
VLOG(1) << "OnRecognitionError: " << error.code;
base::AutoLock auto_lock(state_lock_);
if (state_ == kShutdown)
return;
- // Release the recognizer object.
GetSpeechInputExtensionInterface()->StopRecording(true);
std::string event_error_code;
@@ -369,6 +370,15 @@ void SpeechInputExtensionManager::OnRecognitionError(
case content::SPEECH_RECOGNITION_ERROR_NONE:
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:
if (state_ == kStarting) {
event_error_code = kErrorUnableToStart;
@@ -411,12 +421,12 @@ void SpeechInputExtensionManager::OnRecognitionError(
void SpeechInputExtensionManager::OnEnvironmentEstimationComplete(
int session_id) {
- DCHECK_EQ(session_id, kSpeechInputSessionId);
+ DCHECK_EQ(session_id, speech_recognition_session_id_);
}
void SpeechInputExtensionManager::OnSoundStart(int session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK_EQ(session_id, kSpeechInputSessionId);
+ DCHECK_EQ(session_id, speech_recognition_session_id_);
VLOG(1) << "OnSoundStart";
std::string json_args;
@@ -428,7 +438,6 @@ void SpeechInputExtensionManager::OnSoundStart(int session_id) {
void SpeechInputExtensionManager::OnSoundEnd(int session_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK_EQ(session_id, kSpeechInputSessionId);
VLOG(1) << "OnSoundEnd";
std::string json_args;
@@ -471,11 +480,6 @@ void SpeechInputExtensionManager::DispatchError(
if (state_ == kShutdown)
return;
- if (state_ == kRecording) {
- DCHECK(notification_.get());
- notification_->Hide();
- }
-
extension_id = extension_id_in_use_;
ResetToIdleState();
@@ -531,23 +535,37 @@ bool SpeechInputExtensionManager::Start(
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;
VLOG(1) << "State changed to starting";
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::IO, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::StartOnIOThread, this,
- profile_->GetRequestContext(), language, grammar,
- filter_profanities));
+ url_request_context_getter, extension_name, language, grammar,
+ filter_profanities, show_notification));
return true;
}
void SpeechInputExtensionManager::StartOnIOThread(
- net::URLRequestContextGetter* context_getter,
+ scoped_refptr<net::URLRequestContextGetter> context_getter,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities) {
+ bool filter_profanities,
+ bool show_notification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
VLOG(1) << "Requesting start (IO thread)";
@@ -558,6 +576,12 @@ void SpeechInputExtensionManager::StartOnIOThread(
if (state_ == kShutdown)
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()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
@@ -573,9 +597,13 @@ void SpeechInputExtensionManager::StartOnIOThread(
return;
}
- GetSpeechInputExtensionInterface()->StartRecording(
- this, context_getter, kSpeechInputSessionId, language, grammar,
- filter_profanities);
+ GetSpeechInputExtensionInterface()->StartRecording(this,
+ context_getter,
+ extension_name,
+ language,
+ grammar,
+ filter_profanities,
+ show_notification);
}
bool SpeechInputExtensionManager::HasAudioInputDevices() {
@@ -602,6 +630,7 @@ void SpeechInputExtensionManager::IsRecordingOnIOThread(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
bool result = GetSpeechInputExtensionInterface()->IsCapturingAudio();
+
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SpeechInputExtensionManager::IsRecordingOnUIThread,
@@ -618,20 +647,41 @@ void SpeechInputExtensionManager::IsRecordingOnUIThread(
void SpeechInputExtensionManager::StartRecording(
content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter,
- int session_id,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities) {
+ bool filter_profanities,
+ bool show_notification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(!recognizer_);
- recognizer_ = content::SpeechRecognizer::Create(
- listener, session_id, language, grammar, context_getter,
- filter_profanities, "", "");
- recognizer_->StartRecognition();
+
+ content::SpeechRecognitionSessionContext context;
+ context.requested_by_page_element = false;
+ context.is_first_request_for_context = show_notification;
+ 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() {
- return !!recognizer_;
+ if (!is_recognition_in_progress_)
+ return false;
+ return SpeechRecognitionManager::GetInstance()->IsCapturingAudio();
}
bool SpeechInputExtensionManager::Stop(const std::string& extension_id,
@@ -692,13 +742,13 @@ void SpeechInputExtensionManager::ForceStopOnIOThread() {
}
void SpeechInputExtensionManager::StopRecording(bool recognition_failed) {
- if (recognizer_) {
- // Recognition is already cancelled in case of failure.
- // Double-cancelling leads to assertion failures.
- if (!recognition_failed)
- recognizer_->AbortRecognition();
- recognizer_.release();
- }
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!is_recognition_in_progress_)
+ return;
+ DCHECK_NE(speech_recognition_session_id_,
+ SpeechRecognitionManager::kSessionIDInvalid);
+ SpeechRecognitionManager::GetInstance()->AbortSession(
+ speech_recognition_session_id_);
}
void SpeechInputExtensionManager::StopSucceededOnUIThread() {
@@ -712,9 +762,6 @@ void SpeechInputExtensionManager::StopSucceededOnUIThread() {
std::string extension_id = extension_id_in_use_;
ResetToIdleState();
- DCHECK(notification_.get());
- notification_->Hide();
-
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED,
// Guarded by the state_ == kShutdown check.
@@ -723,18 +770,5 @@ void SpeechInputExtensionManager::StopSucceededOnUIThread() {
}
void SpeechInputExtensionManager::OnAudioLevelsChange(int session_id,
- float 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);
-}
+ float volume,
+ float noise_volume) {}
diff --git a/chrome/browser/speech/speech_input_extension_manager.h b/chrome/browser/speech/speech_input_extension_manager.h
index 3923506..5045a35 100644
--- a/chrome/browser/speech/speech_input_extension_manager.h
+++ b/chrome/browser/speech/speech_input_extension_manager.h
@@ -11,18 +11,18 @@
#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
#include "base/synchronization/lock.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/speech_recognition_event_listener.h"
class Profile;
-class SpeechRecognitionTrayIconController;
namespace content {
class NotificationRegistrar;
struct SpeechRecognitionError;
+class SpeechRecognitionManager;
struct SpeechRecognitionResult;
-class SpeechRecognizer;
}
namespace net {
@@ -39,10 +39,11 @@ class SpeechInputExtensionInterface {
virtual void StartRecording(
content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter,
- int session_id,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities) = 0;
+ bool filter_profanities,
+ bool show_notification) = 0;
virtual void StopRecording(bool recognition_failed) = 0;
virtual bool HasAudioInputDevices() = 0;
@@ -143,19 +144,22 @@ class SpeechInputExtensionManager
virtual void StartRecording(
content::SpeechRecognitionEventListener* listener,
net::URLRequestContextGetter* context_getter,
- int session_id,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities) OVERRIDE;
+ bool filter_profanities,
+ bool show_notification) OVERRIDE;
virtual void StopRecording(bool recognition_failed) OVERRIDE;
// Internal methods.
void StartOnIOThread(
- net::URLRequestContextGetter* context_getter,
+ scoped_refptr<net::URLRequestContextGetter> context_getter,
+ const string16& extension_name,
const std::string& language,
const std::string& grammar,
- bool filter_profanities);
+ bool filter_profanities,
+ bool show_notification);
void ForceStopOnIOThread();
void IsRecordingOnIOThread(const IsRecordingCallback& callback);
@@ -172,9 +176,10 @@ class SpeechInputExtensionManager
const std::string& json_args);
void ExtensionUnloaded(const std::string& extension_id);
- void SetInputVolumeOnUIThread(float volume);
void ResetToIdleState();
+ void AbortAllSessionsOnIOThread();
+
virtual ~SpeechInputExtensionManager();
friend class base::RefCountedThreadSafe<SpeechInputExtensionManager>;
@@ -196,10 +201,10 @@ class SpeechInputExtensionManager
// Used in the UI thread.
scoped_ptr<content::NotificationRegistrar> registrar_;
SpeechInputExtensionInterface* speech_interface_;
- scoped_refptr<SpeechRecognitionTrayIconController> notification_;
// 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_