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