| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/speech/speech_recognition_bubble_controller.h" | 5 #include "chrome/browser/speech/speech_recognition_bubble_controller.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "chrome/browser/tab_contents/tab_util.h" | 8 #include "chrome/browser/tab_contents/tab_util.h" |
| 9 #include "content/public/browser/browser_thread.h" | 9 #include "content/public/browser/browser_thread.h" |
| 10 #include "content/public/browser/notification_registrar.h" | 10 #include "content/public/browser/notification_registrar.h" |
| 11 #include "content/public/browser/notification_source.h" | 11 #include "content/public/browser/notification_source.h" |
| 12 #include "content/public/browser/notification_types.h" | 12 #include "content/public/browser/notification_types.h" |
| 13 #include "content/public/browser/web_contents.h" | 13 #include "content/public/browser/web_contents.h" |
| 14 #include "ui/gfx/rect.h" | 14 #include "ui/gfx/rect.h" |
| 15 | 15 |
| 16 using content::BrowserThread; | 16 using content::BrowserThread; |
| 17 using content::WebContents; | 17 using content::WebContents; |
| 18 | 18 |
| 19 namespace speech { | 19 namespace speech { |
| 20 | 20 |
| 21 SpeechRecognitionBubbleController::SpeechRecognitionBubbleController( | 21 SpeechRecognitionBubbleController::SpeechRecognitionBubbleController( |
| 22 Delegate* delegate) | 22 Delegate* delegate) |
| 23 : delegate_(delegate), | 23 : delegate_(delegate), |
| 24 current_bubble_caller_id_(0), | 24 current_bubble_session_id_(0), |
| 25 registrar_(new content::NotificationRegistrar) { | 25 registrar_(new content::NotificationRegistrar) { |
| 26 } | 26 } |
| 27 | 27 |
| 28 SpeechRecognitionBubbleController::~SpeechRecognitionBubbleController() { | 28 SpeechRecognitionBubbleController::~SpeechRecognitionBubbleController() { |
| 29 DCHECK(bubbles_.empty()); | 29 DCHECK(bubbles_.empty()); |
| 30 } | 30 } |
| 31 | 31 |
| 32 void SpeechRecognitionBubbleController::CreateBubble( | 32 void SpeechRecognitionBubbleController::CreateBubble( |
| 33 int caller_id, | 33 int session_id, |
| 34 int render_process_id, | 34 int render_process_id, |
| 35 int render_view_id, | 35 int render_view_id, |
| 36 const gfx::Rect& element_rect) { | 36 const gfx::Rect& element_rect) { |
| 37 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | 37 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 38 BrowserThread::PostTask( | 38 BrowserThread::PostTask( |
| 39 BrowserThread::UI, FROM_HERE, | 39 BrowserThread::UI, FROM_HERE, |
| 40 base::Bind(&SpeechRecognitionBubbleController::CreateBubble, this, | 40 base::Bind(&SpeechRecognitionBubbleController::CreateBubble, this, |
| 41 caller_id, render_process_id, render_view_id, element_rect)); | 41 session_id, render_process_id, render_view_id, |
| 42 element_rect)); |
| 42 return; | 43 return; |
| 43 } | 44 } |
| 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 45 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, | 46 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, |
| 46 render_view_id); | 47 render_view_id); |
| 47 | 48 |
| 48 DCHECK_EQ(0u, bubbles_.count(caller_id)); | 49 DCHECK_EQ(0u, bubbles_.count(session_id)); |
| 49 SpeechRecognitionBubble* bubble = SpeechRecognitionBubble::Create( | 50 SpeechRecognitionBubble* bubble = SpeechRecognitionBubble::Create( |
| 50 web_contents, this, element_rect); | 51 web_contents, this, element_rect); |
| 51 if (!bubble) { | 52 if (!bubble) { |
| 52 // Could be null if tab or display rect were invalid. | 53 // Could be null if tab or display rect were invalid. |
| 53 // Simulate the cancel button being clicked to inform the delegate. | 54 // Simulate the cancel button being clicked to inform the delegate. |
| 54 BrowserThread::PostTask( | 55 BrowserThread::PostTask( |
| 55 BrowserThread::IO, FROM_HERE, | 56 BrowserThread::IO, FROM_HERE, |
| 56 base::Bind( | 57 base::Bind( |
| 57 &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, | 58 &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, |
| 58 this, caller_id, SpeechRecognitionBubble::BUTTON_CANCEL)); | 59 this, session_id, SpeechRecognitionBubble::BUTTON_CANCEL)); |
| 59 return; | 60 return; |
| 60 } | 61 } |
| 61 | 62 |
| 62 bubbles_[caller_id] = bubble; | 63 bubbles_[session_id] = bubble; |
| 63 | 64 |
| 64 UpdateTabContentsSubscription(caller_id, BUBBLE_ADDED); | 65 UpdateTabContentsSubscription(session_id, BUBBLE_ADDED); |
| 65 } | 66 } |
| 66 | 67 |
| 67 void SpeechRecognitionBubbleController::CloseBubble(int caller_id) { | 68 void SpeechRecognitionBubbleController::CloseBubble(int session_id) { |
| 68 ProcessRequestInUiThread(caller_id, REQUEST_CLOSE, string16(), 0, 0); | 69 ProcessRequestInUiThread(session_id, REQUEST_CLOSE, string16(), 0, 0); |
| 69 } | 70 } |
| 70 | 71 |
| 71 void SpeechRecognitionBubbleController::SetBubbleWarmUpMode(int caller_id) { | 72 void SpeechRecognitionBubbleController::SetBubbleWarmUpMode(int session_id) { |
| 72 ProcessRequestInUiThread(caller_id, REQUEST_SET_WARM_UP_MODE, | 73 ProcessRequestInUiThread(session_id, REQUEST_SET_WARM_UP_MODE, |
| 73 string16(), 0, 0); | 74 string16(), 0, 0); |
| 74 } | 75 } |
| 75 | 76 |
| 76 void SpeechRecognitionBubbleController::SetBubbleRecordingMode(int caller_id) { | 77 void SpeechRecognitionBubbleController::SetBubbleRecordingMode(int session_id) { |
| 77 ProcessRequestInUiThread(caller_id, REQUEST_SET_RECORDING_MODE, | 78 ProcessRequestInUiThread(session_id, REQUEST_SET_RECORDING_MODE, |
| 78 string16(), 0, 0); | 79 string16(), 0, 0); |
| 79 } | 80 } |
| 80 | 81 |
| 81 void SpeechRecognitionBubbleController::SetBubbleRecognizingMode( | 82 void SpeechRecognitionBubbleController::SetBubbleRecognizingMode( |
| 82 int caller_id) { | 83 int session_id) { |
| 83 ProcessRequestInUiThread(caller_id, REQUEST_SET_RECOGNIZING_MODE, | 84 ProcessRequestInUiThread(session_id, REQUEST_SET_RECOGNIZING_MODE, |
| 84 string16(), 0, 0); | 85 string16(), 0, 0); |
| 85 } | 86 } |
| 86 | 87 |
| 87 void SpeechRecognitionBubbleController::SetBubbleInputVolume( | 88 void SpeechRecognitionBubbleController::SetBubbleInputVolume( |
| 88 int caller_id, float volume, float noise_volume) { | 89 int session_id, float volume, float noise_volume) { |
| 89 ProcessRequestInUiThread(caller_id, REQUEST_SET_INPUT_VOLUME, string16(), | 90 ProcessRequestInUiThread(session_id, REQUEST_SET_INPUT_VOLUME, string16(), |
| 90 volume, noise_volume); | 91 volume, noise_volume); |
| 91 } | 92 } |
| 92 | 93 |
| 93 void SpeechRecognitionBubbleController::SetBubbleMessage(int caller_id, | 94 void SpeechRecognitionBubbleController::SetBubbleMessage(int session_id, |
| 94 const string16& text) { | 95 const string16& text) { |
| 95 ProcessRequestInUiThread(caller_id, REQUEST_SET_MESSAGE, text, 0, 0); | 96 ProcessRequestInUiThread(session_id, REQUEST_SET_MESSAGE, text, 0, 0); |
| 96 } | 97 } |
| 97 | 98 |
| 98 void SpeechRecognitionBubbleController::UpdateTabContentsSubscription( | 99 void SpeechRecognitionBubbleController::UpdateTabContentsSubscription( |
| 99 int caller_id, ManageSubscriptionAction action) { | 100 int session_id, ManageSubscriptionAction action) { |
| 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 101 | 102 |
| 102 // If there are any other bubbles existing for the same TabContents, we would | 103 // If there are any other bubbles existing for the same TabContents, we would |
| 103 // have subscribed to tab close notifications on their behalf and we need to | 104 // have subscribed to tab close notifications on their behalf and we need to |
| 104 // stay registered. So we don't change the subscription in such cases. | 105 // stay registered. So we don't change the subscription in such cases. |
| 105 WebContents* web_contents = bubbles_[caller_id]->GetWebContents(); | 106 WebContents* web_contents = bubbles_[session_id]->GetWebContents(); |
| 106 for (BubbleCallerIdMap::iterator iter = bubbles_.begin(); | 107 for (BubbleSessionIdMap::iterator iter = bubbles_.begin(); |
| 107 iter != bubbles_.end(); ++iter) { | 108 iter != bubbles_.end(); ++iter) { |
| 108 if (iter->second->GetWebContents() == web_contents && | 109 if (iter->second->GetWebContents() == web_contents && |
| 109 iter->first != caller_id) { | 110 iter->first != session_id) { |
| 110 // At least one other bubble exists for the same TabContents. So don't | 111 // At least one other bubble exists for the same TabContents. So don't |
| 111 // make any change to the subscription. | 112 // make any change to the subscription. |
| 112 return; | 113 return; |
| 113 } | 114 } |
| 114 } | 115 } |
| 115 | 116 |
| 116 if (action == BUBBLE_ADDED) { | 117 if (action == BUBBLE_ADDED) { |
| 117 registrar_->Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | 118 registrar_->Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
| 118 content::Source<WebContents>(web_contents)); | 119 content::Source<WebContents>(web_contents)); |
| 119 } else { | 120 } else { |
| 120 registrar_->Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | 121 registrar_->Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
| 121 content::Source<WebContents>(web_contents)); | 122 content::Source<WebContents>(web_contents)); |
| 122 } | 123 } |
| 123 } | 124 } |
| 124 | 125 |
| 125 void SpeechRecognitionBubbleController::Observe( | 126 void SpeechRecognitionBubbleController::Observe( |
| 126 int type, | 127 int type, |
| 127 const content::NotificationSource& source, | 128 const content::NotificationSource& source, |
| 128 const content::NotificationDetails& details) { | 129 const content::NotificationDetails& details) { |
| 129 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { | 130 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { |
| 130 // Cancel all bubbles and active recognition sessions for this tab. | 131 // Cancel all bubbles and active recognition sessions for this tab. |
| 131 WebContents* web_contents = content::Source<WebContents>(source).ptr(); | 132 WebContents* web_contents = content::Source<WebContents>(source).ptr(); |
| 132 BubbleCallerIdMap::iterator iter = bubbles_.begin(); | 133 BubbleSessionIdMap::iterator iter = bubbles_.begin(); |
| 133 while (iter != bubbles_.end()) { | 134 while (iter != bubbles_.end()) { |
| 134 if (iter->second->GetWebContents() == web_contents) { | 135 if (iter->second->GetWebContents() == web_contents) { |
| 135 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 136 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| 136 base::Bind( | 137 base::Bind( |
| 137 &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, | 138 &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, |
| 138 this, iter->first, SpeechRecognitionBubble::BUTTON_CANCEL)); | 139 this, iter->first, SpeechRecognitionBubble::BUTTON_CANCEL)); |
| 139 CloseBubble(iter->first); | 140 CloseBubble(iter->first); |
| 140 // We expect to have a very small number of items in this map so | 141 // We expect to have a very small number of items in this map so |
| 141 // redo-ing from start is ok. | 142 // redo-ing from start is ok. |
| 142 iter = bubbles_.begin(); | 143 iter = bubbles_.begin(); |
| 143 } else { | 144 } else { |
| 144 ++iter; | 145 ++iter; |
| 145 } | 146 } |
| 146 } | 147 } |
| 147 } else { | 148 } else { |
| 148 NOTREACHED() << "Unknown notification"; | 149 NOTREACHED() << "Unknown notification"; |
| 149 } | 150 } |
| 150 } | 151 } |
| 151 | 152 |
| 152 void SpeechRecognitionBubbleController::ProcessRequestInUiThread( | 153 void SpeechRecognitionBubbleController::ProcessRequestInUiThread( |
| 153 int caller_id, RequestType type, const string16& text, float volume, | 154 int session_id, RequestType type, const string16& text, float volume, |
| 154 float noise_volume) { | 155 float noise_volume) { |
| 155 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | 156 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 156 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 157 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 157 &SpeechRecognitionBubbleController::ProcessRequestInUiThread, this, | 158 &SpeechRecognitionBubbleController::ProcessRequestInUiThread, this, |
| 158 caller_id, type, text, volume, noise_volume)); | 159 session_id, type, text, volume, noise_volume)); |
| 159 return; | 160 return; |
| 160 } | 161 } |
| 161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 162 // The bubble may have been closed before we got a chance to process this | 163 // The bubble may have been closed before we got a chance to process this |
| 163 // request. So check before proceeding. | 164 // request. So check before proceeding. |
| 164 if (!bubbles_.count(caller_id)) | 165 if (!bubbles_.count(session_id)) |
| 165 return; | 166 return; |
| 166 | 167 |
| 167 bool change_active_bubble = (type == REQUEST_SET_WARM_UP_MODE || | 168 bool change_active_bubble = (type == REQUEST_SET_WARM_UP_MODE || |
| 168 type == REQUEST_SET_MESSAGE); | 169 type == REQUEST_SET_MESSAGE); |
| 169 if (change_active_bubble) { | 170 if (change_active_bubble) { |
| 170 if (current_bubble_caller_id_ && current_bubble_caller_id_ != caller_id) | 171 if (current_bubble_session_id_ && current_bubble_session_id_ != session_id) |
| 171 bubbles_[current_bubble_caller_id_]->Hide(); | 172 bubbles_[current_bubble_session_id_]->Hide(); |
| 172 current_bubble_caller_id_ = caller_id; | 173 current_bubble_session_id_ = session_id; |
| 173 } | 174 } |
| 174 | 175 |
| 175 SpeechRecognitionBubble* bubble = bubbles_[caller_id]; | 176 SpeechRecognitionBubble* bubble = bubbles_[session_id]; |
| 176 switch (type) { | 177 switch (type) { |
| 177 case REQUEST_SET_WARM_UP_MODE: | 178 case REQUEST_SET_WARM_UP_MODE: |
| 178 bubble->SetWarmUpMode(); | 179 bubble->SetWarmUpMode(); |
| 179 break; | 180 break; |
| 180 case REQUEST_SET_RECORDING_MODE: | 181 case REQUEST_SET_RECORDING_MODE: |
| 181 bubble->SetRecordingMode(); | 182 bubble->SetRecordingMode(); |
| 182 break; | 183 break; |
| 183 case REQUEST_SET_RECOGNIZING_MODE: | 184 case REQUEST_SET_RECOGNIZING_MODE: |
| 184 bubble->SetRecognizingMode(); | 185 bubble->SetRecognizingMode(); |
| 185 break; | 186 break; |
| 186 case REQUEST_SET_MESSAGE: | 187 case REQUEST_SET_MESSAGE: |
| 187 bubble->SetMessage(text); | 188 bubble->SetMessage(text); |
| 188 break; | 189 break; |
| 189 case REQUEST_SET_INPUT_VOLUME: | 190 case REQUEST_SET_INPUT_VOLUME: |
| 190 bubble->SetInputVolume(volume, noise_volume); | 191 bubble->SetInputVolume(volume, noise_volume); |
| 191 break; | 192 break; |
| 192 case REQUEST_CLOSE: | 193 case REQUEST_CLOSE: |
| 193 if (current_bubble_caller_id_ == caller_id) | 194 if (current_bubble_session_id_ == session_id) |
| 194 current_bubble_caller_id_ = 0; | 195 current_bubble_session_id_ = 0; |
| 195 UpdateTabContentsSubscription(caller_id, BUBBLE_REMOVED); | 196 UpdateTabContentsSubscription(session_id, BUBBLE_REMOVED); |
| 196 delete bubble; | 197 delete bubble; |
| 197 bubbles_.erase(caller_id); | 198 bubbles_.erase(session_id); |
| 198 break; | 199 break; |
| 199 default: | 200 default: |
| 200 NOTREACHED(); | 201 NOTREACHED(); |
| 201 break; | 202 break; |
| 202 } | 203 } |
| 203 | 204 |
| 204 if (change_active_bubble) | 205 if (change_active_bubble) |
| 205 bubble->Show(); | 206 bubble->Show(); |
| 206 } | 207 } |
| 207 | 208 |
| 208 void SpeechRecognitionBubbleController::InfoBubbleButtonClicked( | 209 void SpeechRecognitionBubbleController::InfoBubbleButtonClicked( |
| 209 SpeechRecognitionBubble::Button button) { | 210 SpeechRecognitionBubble::Button button) { |
| 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 211 DCHECK(current_bubble_caller_id_); | 212 DCHECK(current_bubble_session_id_); |
| 212 | 213 |
| 213 BrowserThread::PostTask( | 214 BrowserThread::PostTask( |
| 214 BrowserThread::IO, FROM_HERE, | 215 BrowserThread::IO, FROM_HERE, |
| 215 base::Bind( | 216 base::Bind( |
| 216 &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, | 217 &SpeechRecognitionBubbleController::InvokeDelegateButtonClicked, |
| 217 this, current_bubble_caller_id_, button)); | 218 this, current_bubble_session_id_, button)); |
| 218 } | 219 } |
| 219 | 220 |
| 220 void SpeechRecognitionBubbleController::InfoBubbleFocusChanged() { | 221 void SpeechRecognitionBubbleController::InfoBubbleFocusChanged() { |
| 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 222 DCHECK(current_bubble_caller_id_); | 223 DCHECK(current_bubble_session_id_); |
| 223 | 224 |
| 224 int old_bubble_caller_id = current_bubble_caller_id_; | 225 int old_bubble_session_id = current_bubble_session_id_; |
| 225 current_bubble_caller_id_ = 0; | 226 current_bubble_session_id_ = 0; |
| 226 | 227 |
| 227 BrowserThread::PostTask( | 228 BrowserThread::PostTask( |
| 228 BrowserThread::IO, FROM_HERE, | 229 BrowserThread::IO, FROM_HERE, |
| 229 base::Bind( | 230 base::Bind( |
| 230 &SpeechRecognitionBubbleController::InvokeDelegateFocusChanged, | 231 &SpeechRecognitionBubbleController::InvokeDelegateFocusChanged, |
| 231 this, old_bubble_caller_id)); | 232 this, old_bubble_session_id)); |
| 232 } | 233 } |
| 233 | 234 |
| 234 void SpeechRecognitionBubbleController::InvokeDelegateButtonClicked( | 235 void SpeechRecognitionBubbleController::InvokeDelegateButtonClicked( |
| 235 int caller_id, SpeechRecognitionBubble::Button button) { | 236 int session_id, SpeechRecognitionBubble::Button button) { |
| 236 delegate_->InfoBubbleButtonClicked(caller_id, button); | 237 delegate_->InfoBubbleButtonClicked(session_id, button); |
| 237 } | 238 } |
| 238 | 239 |
| 239 void SpeechRecognitionBubbleController::InvokeDelegateFocusChanged( | 240 void SpeechRecognitionBubbleController::InvokeDelegateFocusChanged( |
| 240 int caller_id) { | 241 int session_id) { |
| 241 delegate_->InfoBubbleFocusChanged(caller_id); | 242 delegate_->InfoBubbleFocusChanged(session_id); |
| 242 } | 243 } |
| 243 | 244 |
| 244 } // namespace speech | 245 } // namespace speech |
| OLD | NEW |