| Index: chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
|
| diff --git a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
|
| index f22f95541e0bd6f6ef1382511134b637126e3a2c..7bdb8ec7c6c079e82739b6112090a83aa24d9046 100644
|
| --- a/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
|
| +++ b/chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
|
| @@ -16,6 +16,7 @@
|
| #include "chrome/browser/profiles/profile_manager.h"
|
| #include "chrome/browser/speech/chrome_speech_recognition_preferences.h"
|
| #include "chrome/browser/speech/speech_recognition_tray_icon_controller.h"
|
| +#include "chrome/browser/speech/tab_watcher.h"
|
| #include "chrome/browser/tab_contents/tab_util.h"
|
| #include "chrome/browser/view_type_utils.h"
|
| #include "chrome/common/pref_names.h"
|
| @@ -39,12 +40,9 @@
|
|
|
| using content::BrowserThread;
|
| using content::SpeechRecognitionManager;
|
| -using content::SpeechRecognitionSessionContext;
|
| using content::WebContents;
|
|
|
| namespace {
|
| -const int kNoActiveBubble =
|
| - content::SpeechRecognitionManager::kSessionIDInvalid;
|
|
|
| const char kExtensionPrefix[] = "chrome-extension://";
|
|
|
| @@ -124,53 +122,54 @@ class ChromeSpeechRecognitionManagerDelegate::OptionalRequestInfo
|
| };
|
|
|
| ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate()
|
| - : 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_);
|
| - }
|
| + if (bubble_controller_.get())
|
| + bubble_controller_->CloseBubble();
|
| }
|
|
|
| void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked(
|
| int session_id, SpeechRecognitionBubble::Button button) {
|
| 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) {
|
| - GetBubbleController()->CloseBubble(session_id);
|
| + GetBubbleController()->CloseBubble();
|
| last_session_config_.reset();
|
| - active_bubble_session_id_ = kNoActiveBubble;
|
|
|
| // We can safely call AbortSession even if the session has already ended,
|
| // the manager's public methods are reliable and will handle it properly.
|
| SpeechRecognitionManager::GetInstance()->AbortSession(session_id);
|
| } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) {
|
| - GetBubbleController()->CloseBubble(session_id);
|
| - active_bubble_session_id_ = kNoActiveBubble;
|
| + GetBubbleController()->CloseBubble();
|
| RestartLastSession();
|
| + } else {
|
| + NOTREACHED();
|
| }
|
| }
|
|
|
| void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged(
|
| int session_id) {
|
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| - DCHECK_EQ(active_bubble_session_id_, session_id);
|
| +
|
| + // This check is needed since on some systems (MacOS), in rare cases, if the
|
| + // user clicks repeatedly and fast on the input element, the FocusChanged
|
| + // event (corresponding to the old session that should be aborted) can be
|
| + // received after a new session (corresponding to the 2nd click) is started.
|
| + if (GetBubbleController()->GetActiveSessionID() != session_id)
|
| + return;
|
|
|
| // Note, the session might have been destroyed, therefore avoid calls to the
|
| // manager which imply its existance (e.g., GetSessionContext()).
|
| -
|
| - GetBubbleController()->CloseBubble(session_id);
|
| + GetBubbleController()->CloseBubble();
|
| last_session_config_.reset();
|
| - active_bubble_session_id_ = kNoActiveBubble;
|
|
|
| // If the user clicks outside the bubble while capturing audio we abort the
|
| // session. Otherwise, i.e. audio capture is ended and we are just waiting for
|
| @@ -183,11 +182,27 @@ void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() {
|
| DCHECK(last_session_config_.get());
|
| SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance();
|
| const int new_session_id = manager->CreateSession(*last_session_config_);
|
| - DCHECK_NE(new_session_id, kNoActiveBubble);
|
| + DCHECK_NE(SpeechRecognitionManager::kSessionIDInvalid, new_session_id);
|
| last_session_config_.reset();
|
| manager->StartSession(new_session_id);
|
| }
|
|
|
| +void ChromeSpeechRecognitionManagerDelegate::TabClosedCallback(
|
| + int render_process_id, int render_view_id) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + if (SpeechRecognitionManager* manager =
|
| + SpeechRecognitionManager::GetInstance()) {
|
| + manager->AbortAllSessionsForRenderer(render_process_id, render_view_id);
|
| + }
|
| +
|
| + if (bubble_controller_.get() &&
|
| + bubble_controller_->IsShowingBubbleOn(render_process_id,
|
| + render_view_id)) {
|
| + bubble_controller_->CloseBubble();
|
| + }
|
| +}
|
| +
|
| void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
|
| int session_id) {
|
| const content::SpeechRecognitionSessionContext& context =
|
| @@ -199,21 +214,27 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart(
|
| 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);
|
| + // Register callback to auto abort session on tab closure.
|
| + // |tab_watcher_| is lazyly istantiated on the first call.
|
| + if (!tab_watcher_.get()) {
|
| + tab_watcher_ = new TabWatcher(
|
| + base::Bind(&ChromeSpeechRecognitionManagerDelegate::TabClosedCallback,
|
| + base::Unretained(this)),
|
| + BrowserThread::IO);
|
| }
|
| + tab_watcher_->Watch(context.render_process_id, context.render_view_id);
|
| }
|
|
|
| void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {
|
| if (RequiresBubble(session_id)) {
|
| - GetBubbleController()->SetBubbleRecordingMode(session_id);
|
| + DCHECK_EQ(session_id, GetBubbleController()->GetActiveSessionID());
|
| + GetBubbleController()->SetBubbleRecordingMode();
|
| } else if (RequiresTrayIcon(session_id)) {
|
| // We post the action to the UI thread for sessions requiring a tray icon,
|
| // since ChromeSpeechRecognitionPreferences (which requires UI thread) is
|
| @@ -242,8 +263,9 @@ 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 (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
|
| - GetBubbleController()->SetBubbleRecognizingMode(session_id);
|
| + if (GetBubbleController()->GetActiveSessionID() == session_id) {
|
| + DCHECK(RequiresBubble(session_id));
|
| + GetBubbleController()->SetBubbleRecognizingMode();
|
| } else if (RequiresTrayIcon(session_id)) {
|
| GetTrayIconController()->Hide();
|
| }
|
| @@ -251,19 +273,13 @@ void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {
|
|
|
| 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 (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) {
|
| - GetBubbleController()->CloseBubble(session_id);
|
| - last_session_config_.reset();
|
| - active_bubble_session_id_ = kNoActiveBubble;
|
| - }
|
| + // The bubble will be closed upon the OnEnd event, which will follow soon.
|
| }
|
|
|
| void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
|
| int session_id, const content::SpeechRecognitionError& error) {
|
| // An error can be dispatched when the bubble is not visible anymore.
|
| - if (active_bubble_session_id_ != session_id)
|
| + if (GetBubbleController()->GetActiveSessionID() != session_id)
|
| return;
|
| DCHECK(RequiresBubble(session_id));
|
|
|
| @@ -299,28 +315,29 @@ void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError(
|
| return;
|
| }
|
| GetBubbleController()->SetBubbleMessage(
|
| - session_id, l10n_util::GetStringUTF16(error_message_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) {
|
| + if (GetBubbleController()->GetActiveSessionID() == session_id) {
|
| DCHECK(RequiresBubble(session_id));
|
| - GetBubbleController()->SetBubbleInputVolume(session_id,
|
| - volume, noise_volume);
|
| + GetBubbleController()->SetBubbleInputVolume(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.
|
| + // The only case in which the OnRecognitionEnd should not close the bubble is
|
| + // when we are showing an error. In this case the bubble will be closed by
|
| + // the |InfoBubbleFocusChanged| method, when the users clicks either the
|
| + // "Cancel" button or outside of the bubble.
|
| + if (GetBubbleController()->GetActiveSessionID() == session_id &&
|
| + !GetBubbleController()->IsShowingMessage()) {
|
| + DCHECK(RequiresBubble(session_id));
|
| + GetBubbleController()->CloseBubble();
|
| + }
|
| }
|
|
|
| void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation(
|
|
|