Index: chrome/browser/speech/speech_input_bubble_controller.cc |
diff --git a/chrome/browser/speech/speech_input_bubble_controller.cc b/chrome/browser/speech/speech_input_bubble_controller.cc |
index 682d0283d27bc4ebbea220175f1e754ccecbb4c0..f17342c5813648a8daf19315c80d6e203a767561 100644 |
--- a/chrome/browser/speech/speech_input_bubble_controller.cc |
+++ b/chrome/browser/speech/speech_input_bubble_controller.cc |
@@ -7,13 +7,17 @@ |
#include "chrome/browser/browser_thread.h" |
#include "chrome/browser/tab_contents/tab_contents.h" |
#include "chrome/browser/tab_contents/tab_util.h" |
+#include "chrome/common/notification_registrar.h" |
+#include "chrome/common/notification_source.h" |
+#include "chrome/common/notification_type.h" |
#include "gfx/rect.h" |
namespace speech_input { |
SpeechInputBubbleController::SpeechInputBubbleController(Delegate* delegate) |
: delegate_(delegate), |
- current_bubble_caller_id_(0) { |
+ current_bubble_caller_id_(0), |
+ registrar_(new NotificationRegistrar) { |
} |
SpeechInputBubbleController::~SpeechInputBubbleController() { |
@@ -43,6 +47,16 @@ void SpeechInputBubbleController::CreateBubble(int caller_id, |
return; |
bubbles_[caller_id] = bubble; |
+ |
+ // If this is the first bubble in our list for this TabContents, register |
hans
2011/01/06 12:30:54
"First in our list", but the call is to IsOnlyBubb
|
+ // for notifications when the tab closes. This ensures that we can safely |
+ // kill the bubble and recording session even if the user clicked on the |
+ // speech button and immediately closes the tab, or if the tab crashes in |
bulach
2011/01/06 12:32:39
s/in the somewhere/
|
+ // the somewhere while the bubble & recording are active. |
hans
2011/01/06 12:30:54
nit: "crashes in the somewhere" sounds weird
|
+ if (IsOnlyBubbleForTab(caller_id)) { |
bulach
2011/01/06 12:32:39
as we chatted, the "IsOnlyBubbleForTab" is the bes
|
+ registrar_->Add(this, NotificationType::TAB_CONTENTS_DESTROYED, |
+ Source<TabContents>(tab_contents)); |
+ } |
} |
void SpeechInputBubbleController::CloseBubble(int caller_id) { |
@@ -70,6 +84,47 @@ void SpeechInputBubbleController::SetBubbleMessage(int caller_id, |
ProcessRequestInUiThread(caller_id, REQUEST_SET_MESSAGE, text, 0); |
} |
+bool SpeechInputBubbleController::IsOnlyBubbleForTab(int caller_id) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ TabContents* tab_contents = bubbles_[caller_id]->tab_contents(); |
+ for (BubbleCallerIdMap::iterator iter = bubbles_.begin(); |
+ iter != bubbles_.end(); ++iter) { |
+ if (iter->second->tab_contents() == tab_contents && |
+ iter->first != caller_id) { |
+ // At least one other bubble exists for the same TabContents. |
bulach
2011/01/06 12:32:39
yeah, as above, this would be the best place to sa
|
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+void SpeechInputBubbleController::Observe(NotificationType type, |
+ const NotificationSource& source, |
+ const NotificationDetails& details) { |
+ if (type == NotificationType::TAB_CONTENTS_DESTROYED) { |
+ // Cancel all bubbles and active recognition sessions for this tab. |
+ TabContents* tab_contents = Source<TabContents>(source).ptr(); |
+ BubbleCallerIdMap::iterator iter = bubbles_.begin(); |
+ while (iter != bubbles_.end()) { |
+ if (iter->second->tab_contents() == tab_contents) { |
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
+ NewRunnableMethod( |
+ this, |
+ &SpeechInputBubbleController::InvokeDelegateButtonClicked, |
+ iter->first, SpeechInputBubble::BUTTON_CANCEL)); |
+ CloseBubble(iter->first); |
+ // We expect to have a very small number of items in this map so |
+ // redo-ing from start is ok. |
+ iter = bubbles_.begin(); |
+ } else { |
+ ++iter; |
+ } |
+ } |
+ } else { |
+ NOTREACHED() << "Unknown notification"; |
+ } |
+} |
+ |
void SpeechInputBubbleController::ProcessRequestInUiThread( |
int caller_id, RequestType type, const string16& text, float volume) { |
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
@@ -109,6 +164,12 @@ void SpeechInputBubbleController::ProcessRequestInUiThread( |
case REQUEST_CLOSE: |
if (current_bubble_caller_id_ == caller_id) |
current_bubble_caller_id_ = 0; |
+ // If this is the last bubble for this TabContents, unregister from |
+ // close notifications. |
+ if (IsOnlyBubbleForTab(caller_id)) { |
+ registrar_->Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, |
+ Source<TabContents>(bubble->tab_contents())); |
+ } |
delete bubble; |
bubbles_.erase(caller_id); |
break; |