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