| 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/chrome_speech_recognition_manager_delegate.h" | 5 #include "chrome/browser/speech/chrome_speech_recognition_manager_delegate.h" |
| 6 | 6 |
| 7 #include <set> |
| 7 #include <string> | 8 #include <string> |
| 8 | 9 |
| 9 #include "base/bind.h" | 10 #include "base/bind.h" |
| 10 #include "base/synchronization/lock.h" | 11 #include "base/synchronization/lock.h" |
| 11 #include "base/threading/thread_restrictions.h" | 12 #include "base/threading/thread_restrictions.h" |
| 12 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 13 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/extensions/extension_service.h" | 15 #include "chrome/browser/extensions/extension_service.h" |
| 15 #include "chrome/browser/prefs/pref_service.h" | 16 #include "chrome/browser/prefs/pref_service.h" |
| 16 #include "chrome/browser/profiles/profile_manager.h" | 17 #include "chrome/browser/profiles/profile_manager.h" |
| 17 #include "chrome/browser/speech/chrome_speech_recognition_preferences.h" | 18 #include "chrome/browser/speech/chrome_speech_recognition_preferences.h" |
| 18 #include "chrome/browser/speech/speech_recognition_tray_icon_controller.h" | 19 #include "chrome/browser/speech/speech_recognition_tray_icon_controller.h" |
| 19 #include "chrome/browser/tab_contents/tab_util.h" | 20 #include "chrome/browser/tab_contents/tab_util.h" |
| 20 #include "chrome/browser/view_type_utils.h" | 21 #include "chrome/browser/view_type_utils.h" |
| 21 #include "chrome/common/pref_names.h" | 22 #include "chrome/common/pref_names.h" |
| 22 #include "content/public/browser/browser_thread.h" | 23 #include "content/public/browser/browser_thread.h" |
| 24 #include "content/public/browser/notification_registrar.h" |
| 25 #include "content/public/browser/notification_source.h" |
| 26 #include "content/public/browser/notification_types.h" |
| 23 #include "content/public/browser/render_process_host.h" | 27 #include "content/public/browser/render_process_host.h" |
| 24 #include "content/public/browser/render_view_host.h" | 28 #include "content/public/browser/render_view_host.h" |
| 25 #include "content/public/browser/resource_context.h" | 29 #include "content/public/browser/resource_context.h" |
| 26 #include "content/public/browser/speech_recognition_manager.h" | 30 #include "content/public/browser/speech_recognition_manager.h" |
| 27 #include "content/public/browser/speech_recognition_session_config.h" | 31 #include "content/public/browser/speech_recognition_session_config.h" |
| 28 #include "content/public/browser/speech_recognition_session_context.h" | 32 #include "content/public/browser/speech_recognition_session_context.h" |
| 29 #include "content/public/browser/web_contents.h" | 33 #include "content/public/browser/web_contents.h" |
| 30 #include "content/public/common/speech_recognition_error.h" | 34 #include "content/public/common/speech_recognition_error.h" |
| 31 #include "content/public/common/speech_recognition_result.h" | 35 #include "content/public/common/speech_recognition_result.h" |
| 32 #include "grit/generated_resources.h" | 36 #include "grit/generated_resources.h" |
| 33 #include "net/url_request/url_request_context_getter.h" | 37 #include "net/url_request/url_request_context_getter.h" |
| 34 #include "ui/base/l10n/l10n_util.h" | 38 #include "ui/base/l10n/l10n_util.h" |
| 35 | 39 |
| 36 #if defined(OS_WIN) | 40 #if defined(OS_WIN) |
| 37 #include "chrome/installer/util/wmi.h" | 41 #include "chrome/installer/util/wmi.h" |
| 38 #endif | 42 #endif |
| 39 | 43 |
| 40 using content::BrowserThread; | 44 using content::BrowserThread; |
| 41 using content::SpeechRecognitionManager; | 45 using content::SpeechRecognitionManager; |
| 42 using content::SpeechRecognitionSessionContext; | |
| 43 using content::WebContents; | 46 using content::WebContents; |
| 44 | 47 |
| 45 namespace { | 48 namespace { |
| 46 const int kNoActiveBubble = | |
| 47 content::SpeechRecognitionManager::kSessionIDInvalid; | |
| 48 | 49 |
| 49 const char kExtensionPrefix[] = "chrome-extension://"; | 50 const char kExtensionPrefix[] = "chrome-extension://"; |
| 50 | 51 |
| 51 bool RequiresBubble(int session_id) { | 52 bool RequiresBubble(int session_id) { |
| 52 return SpeechRecognitionManager::GetInstance()-> | 53 return SpeechRecognitionManager::GetInstance()-> |
| 53 GetSessionContext(session_id).requested_by_page_element; | 54 GetSessionContext(session_id).requested_by_page_element; |
| 54 } | 55 } |
| 55 | 56 |
| 56 bool RequiresTrayIcon(int session_id) { | 57 bool RequiresTrayIcon(int session_id) { |
| 57 return !RequiresBubble(session_id); | 58 return !RequiresBubble(session_id); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 | 117 |
| 117 ~OptionalRequestInfo() {} | 118 ~OptionalRequestInfo() {} |
| 118 | 119 |
| 119 base::Lock lock_; | 120 base::Lock lock_; |
| 120 std::string value_; | 121 std::string value_; |
| 121 bool can_report_metrics_; | 122 bool can_report_metrics_; |
| 122 | 123 |
| 123 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo); | 124 DISALLOW_COPY_AND_ASSIGN(OptionalRequestInfo); |
| 124 }; | 125 }; |
| 125 | 126 |
| 126 ChromeSpeechRecognitionManagerDelegate::ChromeSpeechRecognitionManagerDelegate() | 127 // Simple utility to get notified when a WebContent (a tab or an extension's |
| 127 : active_bubble_session_id_(kNoActiveBubble) { | 128 // background page) is closed or crashes. Both the callback site and the |
| 129 // callback thread are passed by the caller in the constructor. |
| 130 // There is no restriction on the constructor, however this class must be |
| 131 // destroyed on the UI thread, due to the NotificationRegistrar dependency. |
| 132 class ChromeSpeechRecognitionManagerDelegate::TabWatcher |
| 133 : public base::RefCountedThreadSafe<TabWatcher>, |
| 134 public content::NotificationObserver { |
| 135 public: |
| 136 typedef base::Callback<void(int render_process_id, int render_view_id)> |
| 137 TabClosedCallback; |
| 138 |
| 139 TabWatcher(TabClosedCallback tab_closed_callback, |
| 140 BrowserThread::ID callback_thread) |
| 141 : tab_closed_callback_(tab_closed_callback), |
| 142 callback_thread_(callback_thread) { |
| 143 } |
| 144 |
| 145 // Starts monitoring the WebContents corresponding to the given |
| 146 // |render_process_id|, |render_view_id| pair, invoking |tab_closed_callback_| |
| 147 // if closed/unloaded. |
| 148 void Watch(int render_process_id, int render_view_id) { |
| 149 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 150 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 151 &TabWatcher::Watch, this, render_process_id, render_view_id)); |
| 152 return; |
| 153 } |
| 154 |
| 155 WebContents* web_contents = tab_util::GetWebContentsByID(render_process_id, |
| 156 render_view_id); |
| 157 // Sessions initiated by speech input extension APIs will end up in a NULL |
| 158 // WebContent here, but they are properly managed by the |
| 159 // chrome::SpeechInputExtensionManager. However, sessions initiated within a |
| 160 // extension using the (new) speech JS APIs, will be properly handled here. |
| 161 // TODO(primiano) turn this line into a DCHECK once speech input extension |
| 162 // API is deprecated. |
| 163 if (!web_contents) |
| 164 return; |
| 165 |
| 166 // Avoid multiple registrations on |registrar_| for the same |web_contents|. |
| 167 if (registered_web_contents_.find(web_contents) != |
| 168 registered_web_contents_.end()) { |
| 169 return; |
| 170 } |
| 171 registered_web_contents_.insert(web_contents); |
| 172 |
| 173 // Lazy initialize the registrar. |
| 174 if (!registrar_.get()) |
| 175 registrar_.reset(new content::NotificationRegistrar()); |
| 176 |
| 177 registrar_->Add(this, |
| 178 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, |
| 179 content::Source<WebContents>(web_contents)); |
| 180 } |
| 181 |
| 182 // content::NotificationObserver implementation. |
| 183 virtual void Observe(int type, |
| 184 const content::NotificationSource& source, |
| 185 const content::NotificationDetails& details) OVERRIDE { |
| 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 187 DCHECK_EQ(content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, type); |
| 188 |
| 189 WebContents* web_contents = content::Source<WebContents>(source).ptr(); |
| 190 int render_process_id = web_contents->GetRenderProcessHost()->GetID(); |
| 191 int render_view_id = web_contents->GetRenderViewHost()->GetRoutingID(); |
| 192 |
| 193 registrar_->Remove(this, |
| 194 content::NOTIFICATION_WEB_CONTENTS_DISCONNECTED, |
| 195 content::Source<WebContents>(web_contents)); |
| 196 registered_web_contents_.erase(web_contents); |
| 197 |
| 198 BrowserThread::PostTask(callback_thread_, FROM_HERE, base::Bind( |
| 199 tab_closed_callback_, render_process_id, render_view_id)); |
| 200 } |
| 201 |
| 202 private: |
| 203 friend class base::RefCountedThreadSafe<TabWatcher>; |
| 204 |
| 205 virtual ~TabWatcher() { |
| 206 // Must be destroyed on the UI thread due to |registrar_| non thread-safety. |
| 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 208 } |
| 209 |
| 210 // Lazy-initialized and used on the UI thread to handle web contents |
| 211 // notifications (tab closing). |
| 212 scoped_ptr<content::NotificationRegistrar> registrar_; |
| 213 |
| 214 // Keeps track of which WebContent(s) have been registered, in order to avoid |
| 215 // double registrations on |registrar_| |
| 216 std::set<content::WebContents*> registered_web_contents_; |
| 217 |
| 218 // Callback used to notify, on the thread specified by |callback_thread_| the |
| 219 // closure of a registered tab. |
| 220 TabClosedCallback tab_closed_callback_; |
| 221 content::BrowserThread::ID callback_thread_; |
| 222 |
| 223 DISALLOW_COPY_AND_ASSIGN(TabWatcher); |
| 224 }; |
| 225 |
| 226 ChromeSpeechRecognitionManagerDelegate |
| 227 ::ChromeSpeechRecognitionManagerDelegate() { |
| 128 } | 228 } |
| 129 | 229 |
| 130 ChromeSpeechRecognitionManagerDelegate:: | 230 ChromeSpeechRecognitionManagerDelegate |
| 131 ~ChromeSpeechRecognitionManagerDelegate() { | 231 ::~ChromeSpeechRecognitionManagerDelegate() { |
| 132 if (tray_icon_controller_.get()) | 232 if (tray_icon_controller_.get()) |
| 133 tray_icon_controller_->Hide(); | 233 tray_icon_controller_->Hide(); |
| 134 if (active_bubble_session_id_ != kNoActiveBubble) { | 234 if (bubble_controller_.get()) |
| 135 DCHECK(bubble_controller_.get()); | 235 bubble_controller_->CloseBubble(); |
| 136 bubble_controller_->CloseBubble(active_bubble_session_id_); | |
| 137 } | |
| 138 } | 236 } |
| 139 | 237 |
| 140 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked( | 238 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleButtonClicked( |
| 141 int session_id, SpeechRecognitionBubble::Button button) { | 239 int session_id, SpeechRecognitionBubble::Button button) { |
| 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 143 DCHECK_EQ(active_bubble_session_id_, session_id); | |
| 144 | 241 |
| 145 // Note, the session might have been destroyed, therefore avoid calls to the | 242 // Note, the session might have been destroyed, therefore avoid calls to the |
| 146 // manager which imply its existance (e.g., GetSessionContext()). | 243 // manager which imply its existance (e.g., GetSessionContext()). |
| 147 | 244 |
| 148 if (button == SpeechRecognitionBubble::BUTTON_CANCEL) { | 245 if (button == SpeechRecognitionBubble::BUTTON_CANCEL) { |
| 149 GetBubbleController()->CloseBubble(session_id); | 246 GetBubbleController()->CloseBubble(); |
| 150 last_session_config_.reset(); | 247 last_session_config_.reset(); |
| 151 active_bubble_session_id_ = kNoActiveBubble; | |
| 152 | 248 |
| 153 // We can safely call AbortSession even if the session has already ended, | 249 // We can safely call AbortSession even if the session has already ended, |
| 154 // the manager's public methods are reliable and will handle it properly. | 250 // the manager's public methods are reliable and will handle it properly. |
| 155 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); | 251 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); |
| 156 } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) { | 252 } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) { |
| 157 GetBubbleController()->CloseBubble(session_id); | 253 GetBubbleController()->CloseBubble(); |
| 158 active_bubble_session_id_ = kNoActiveBubble; | |
| 159 RestartLastSession(); | 254 RestartLastSession(); |
| 255 } else { |
| 256 NOTREACHED(); |
| 160 } | 257 } |
| 161 } | 258 } |
| 162 | 259 |
| 163 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged( | 260 void ChromeSpeechRecognitionManagerDelegate::InfoBubbleFocusChanged( |
| 164 int session_id) { | 261 int session_id) { |
| 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 166 DCHECK_EQ(active_bubble_session_id_, session_id); | 263 |
| 264 // This check is needed since on some systems (MacOS), in rare cases, if the |
| 265 // user clicks repeatedly and fast on the input element, the FocusChanged |
| 266 // event (corresponding to the old session that should be aborted) can be |
| 267 // received after a new session (corresponding to the 2nd click) is started. |
| 268 if (GetBubbleController()->GetActiveSessionID() != session_id) |
| 269 return; |
| 167 | 270 |
| 168 // Note, the session might have been destroyed, therefore avoid calls to the | 271 // Note, the session might have been destroyed, therefore avoid calls to the |
| 169 // manager which imply its existance (e.g., GetSessionContext()). | 272 // manager which imply its existance (e.g., GetSessionContext()). |
| 170 | 273 GetBubbleController()->CloseBubble(); |
| 171 GetBubbleController()->CloseBubble(session_id); | |
| 172 last_session_config_.reset(); | 274 last_session_config_.reset(); |
| 173 active_bubble_session_id_ = kNoActiveBubble; | |
| 174 | 275 |
| 175 // If the user clicks outside the bubble while capturing audio we abort the | 276 // If the user clicks outside the bubble while capturing audio we abort the |
| 176 // session. Otherwise, i.e. audio capture is ended and we are just waiting for | 277 // session. Otherwise, i.e. audio capture is ended and we are just waiting for |
| 177 // results, this activity is carried silently in background. | 278 // results, this activity is carried silently in background. |
| 178 if (SpeechRecognitionManager::GetInstance()->IsCapturingAudio()) | 279 if (SpeechRecognitionManager::GetInstance()->IsCapturingAudio()) |
| 179 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); | 280 SpeechRecognitionManager::GetInstance()->AbortSession(session_id); |
| 180 } | 281 } |
| 181 | 282 |
| 182 void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() { | 283 void ChromeSpeechRecognitionManagerDelegate::RestartLastSession() { |
| 183 DCHECK(last_session_config_.get()); | 284 DCHECK(last_session_config_.get()); |
| 184 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance(); | 285 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance(); |
| 185 const int new_session_id = manager->CreateSession(*last_session_config_); | 286 const int new_session_id = manager->CreateSession(*last_session_config_); |
| 186 DCHECK_NE(new_session_id, kNoActiveBubble); | 287 DCHECK_NE(SpeechRecognitionManager::kSessionIDInvalid, new_session_id); |
| 187 last_session_config_.reset(); | 288 last_session_config_.reset(); |
| 188 manager->StartSession(new_session_id); | 289 manager->StartSession(new_session_id); |
| 189 } | 290 } |
| 190 | 291 |
| 292 void ChromeSpeechRecognitionManagerDelegate::TabClosedCallback( |
| 293 int render_process_id, int render_view_id) { |
| 294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 295 |
| 296 SpeechRecognitionManager* manager = SpeechRecognitionManager::GetInstance(); |
| 297 // |manager| becomes NULL if a browser shutdown happens between the post of |
| 298 // this task (from the UI thread) and this call (on the IO thread). In this |
| 299 // case we just return. |
| 300 if (!manager) |
| 301 return; |
| 302 |
| 303 manager->AbortAllSessionsForRenderView(render_process_id, render_view_id); |
| 304 |
| 305 if (bubble_controller_.get() && |
| 306 bubble_controller_->IsShowingBubbleForRenderView(render_process_id, |
| 307 render_view_id)) { |
| 308 bubble_controller_->CloseBubble(); |
| 309 } |
| 310 } |
| 311 |
| 191 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart( | 312 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionStart( |
| 192 int session_id) { | 313 int session_id) { |
| 193 const content::SpeechRecognitionSessionContext& context = | 314 const content::SpeechRecognitionSessionContext& context = |
| 194 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); | 315 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); |
| 195 | 316 |
| 196 if (RequiresBubble(session_id)) { | 317 if (RequiresBubble(session_id)) { |
| 197 // Copy the configuration of the session (for the "try again" button). | 318 // Copy the configuration of the session (for the "try again" button). |
| 198 last_session_config_.reset(new content::SpeechRecognitionSessionConfig( | 319 last_session_config_.reset(new content::SpeechRecognitionSessionConfig( |
| 199 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id))); | 320 SpeechRecognitionManager::GetInstance()->GetSessionConfig(session_id))); |
| 200 | 321 |
| 201 // Create and show the bubble. | 322 // Create and show the bubble. |
| 202 DCHECK_EQ(active_bubble_session_id_, kNoActiveBubble); | |
| 203 active_bubble_session_id_ = session_id; | |
| 204 GetBubbleController()->CreateBubble(session_id, | 323 GetBubbleController()->CreateBubble(session_id, |
| 205 context.render_process_id, | 324 context.render_process_id, |
| 206 context.render_view_id, | 325 context.render_view_id, |
| 207 context.element_rect); | 326 context.element_rect); |
| 327 } |
| 208 | 328 |
| 209 // TODO(primiano) Why not creating directly the bubble in warmup mode? | 329 // Register callback to auto abort session on tab closure. |
| 210 GetBubbleController()->SetBubbleWarmUpMode(session_id); | 330 // |tab_watcher_| is lazyly istantiated on the first call. |
| 331 if (!tab_watcher_.get()) { |
| 332 tab_watcher_ = new TabWatcher( |
| 333 base::Bind(&ChromeSpeechRecognitionManagerDelegate::TabClosedCallback, |
| 334 base::Unretained(this)), |
| 335 BrowserThread::IO); |
| 211 } | 336 } |
| 337 tab_watcher_->Watch(context.render_process_id, context.render_view_id); |
| 212 } | 338 } |
| 213 | 339 |
| 214 void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) { | 340 void ChromeSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) { |
| 215 if (RequiresBubble(session_id)) { | 341 if (RequiresBubble(session_id)) { |
| 216 GetBubbleController()->SetBubbleRecordingMode(session_id); | 342 DCHECK_EQ(session_id, GetBubbleController()->GetActiveSessionID()); |
| 343 GetBubbleController()->SetBubbleRecordingMode(); |
| 217 } else if (RequiresTrayIcon(session_id)) { | 344 } else if (RequiresTrayIcon(session_id)) { |
| 218 // We post the action to the UI thread for sessions requiring a tray icon, | 345 // We post the action to the UI thread for sessions requiring a tray icon, |
| 219 // since ChromeSpeechRecognitionPreferences (which requires UI thread) is | 346 // since ChromeSpeechRecognitionPreferences (which requires UI thread) is |
| 220 // involved for determining whether a security alert balloon is required. | 347 // involved for determining whether a security alert balloon is required. |
| 221 const content::SpeechRecognitionSessionContext& context = | 348 const content::SpeechRecognitionSessionContext& context = |
| 222 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); | 349 SpeechRecognitionManager::GetInstance()->GetSessionContext(session_id); |
| 223 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( | 350 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( |
| 224 &ChromeSpeechRecognitionManagerDelegate::ShowTrayIconOnUIThread, | 351 &ChromeSpeechRecognitionManagerDelegate::ShowTrayIconOnUIThread, |
| 225 context.context_name, | 352 context.context_name, |
| 226 context.render_process_id, | 353 context.render_process_id, |
| 227 scoped_refptr<SpeechRecognitionTrayIconController>( | 354 scoped_refptr<SpeechRecognitionTrayIconController>( |
| 228 GetTrayIconController()))); | 355 GetTrayIconController()))); |
| 229 } | 356 } |
| 230 } | 357 } |
| 231 | 358 |
| 232 void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( | 359 void ChromeSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( |
| 233 int session_id) { | 360 int session_id) { |
| 234 } | 361 } |
| 235 | 362 |
| 236 void ChromeSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) { | 363 void ChromeSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) { |
| 237 } | 364 } |
| 238 | 365 |
| 239 void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) { | 366 void ChromeSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) { |
| 240 } | 367 } |
| 241 | 368 |
| 242 void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) { | 369 void ChromeSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) { |
| 243 // OnAudioEnd can be also raised after an abort, when the bubble has already | 370 // OnAudioEnd can be also raised after an abort, when the bubble has already |
| 244 // been closed. | 371 // been closed. |
| 245 if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) { | 372 if (GetBubbleController()->GetActiveSessionID() == session_id) { |
| 246 GetBubbleController()->SetBubbleRecognizingMode(session_id); | 373 DCHECK(RequiresBubble(session_id)); |
| 374 GetBubbleController()->SetBubbleRecognizingMode(); |
| 247 } else if (RequiresTrayIcon(session_id)) { | 375 } else if (RequiresTrayIcon(session_id)) { |
| 248 GetTrayIconController()->Hide(); | 376 GetTrayIconController()->Hide(); |
| 249 } | 377 } |
| 250 } | 378 } |
| 251 | 379 |
| 252 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult( | 380 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionResult( |
| 253 int session_id, const content::SpeechRecognitionResult& result) { | 381 int session_id, const content::SpeechRecognitionResult& result) { |
| 254 // A result can be dispatched when the bubble is not visible anymore (e.g., | 382 // The bubble will be closed upon the OnEnd event, which will follow soon. |
| 255 // lost focus while waiting for a result, thus continuing in background). | |
| 256 if (RequiresBubble(session_id) && active_bubble_session_id_ == session_id) { | |
| 257 GetBubbleController()->CloseBubble(session_id); | |
| 258 last_session_config_.reset(); | |
| 259 active_bubble_session_id_ = kNoActiveBubble; | |
| 260 } | |
| 261 } | 383 } |
| 262 | 384 |
| 263 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError( | 385 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionError( |
| 264 int session_id, const content::SpeechRecognitionError& error) { | 386 int session_id, const content::SpeechRecognitionError& error) { |
| 265 // An error can be dispatched when the bubble is not visible anymore. | 387 // An error can be dispatched when the bubble is not visible anymore. |
| 266 if (active_bubble_session_id_ != session_id) | 388 if (GetBubbleController()->GetActiveSessionID() != session_id) |
| 267 return; | 389 return; |
| 268 DCHECK(RequiresBubble(session_id)); | 390 DCHECK(RequiresBubble(session_id)); |
| 269 | 391 |
| 270 int error_message_id = 0; | 392 int error_message_id = 0; |
| 271 switch (error.code) { | 393 switch (error.code) { |
| 272 case content::SPEECH_RECOGNITION_ERROR_AUDIO: | 394 case content::SPEECH_RECOGNITION_ERROR_AUDIO: |
| 273 switch (error.details) { | 395 switch (error.details) { |
| 274 case content::SPEECH_AUDIO_ERROR_DETAILS_NO_MIC: | 396 case content::SPEECH_AUDIO_ERROR_DETAILS_NO_MIC: |
| 275 error_message_id = IDS_SPEECH_INPUT_NO_MIC; | 397 error_message_id = IDS_SPEECH_INPUT_NO_MIC; |
| 276 break; | 398 break; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 292 error_message_id = IDS_SPEECH_INPUT_NO_RESULTS; | 414 error_message_id = IDS_SPEECH_INPUT_NO_RESULTS; |
| 293 break; | 415 break; |
| 294 case content::SPEECH_RECOGNITION_ERROR_NETWORK: | 416 case content::SPEECH_RECOGNITION_ERROR_NETWORK: |
| 295 error_message_id = IDS_SPEECH_INPUT_NET_ERROR; | 417 error_message_id = IDS_SPEECH_INPUT_NET_ERROR; |
| 296 break; | 418 break; |
| 297 default: | 419 default: |
| 298 NOTREACHED() << "unknown error " << error.code; | 420 NOTREACHED() << "unknown error " << error.code; |
| 299 return; | 421 return; |
| 300 } | 422 } |
| 301 GetBubbleController()->SetBubbleMessage( | 423 GetBubbleController()->SetBubbleMessage( |
| 302 session_id, l10n_util::GetStringUTF16(error_message_id)); | 424 l10n_util::GetStringUTF16(error_message_id)); |
| 303 } | 425 } |
| 304 | 426 |
| 305 void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange( | 427 void ChromeSpeechRecognitionManagerDelegate::OnAudioLevelsChange( |
| 306 int session_id, float volume, float noise_volume) { | 428 int session_id, float volume, float noise_volume) { |
| 307 if (active_bubble_session_id_ == session_id) { | 429 if (GetBubbleController()->GetActiveSessionID() == session_id) { |
| 308 DCHECK(RequiresBubble(session_id)); | 430 DCHECK(RequiresBubble(session_id)); |
| 309 GetBubbleController()->SetBubbleInputVolume(session_id, | 431 GetBubbleController()->SetBubbleInputVolume(volume, noise_volume); |
| 310 volume, noise_volume); | |
| 311 } else if (RequiresTrayIcon(session_id)) { | 432 } else if (RequiresTrayIcon(session_id)) { |
| 312 GetTrayIconController()->SetVUMeterVolume(volume); | 433 GetTrayIconController()->SetVUMeterVolume(volume); |
| 313 } | 434 } |
| 314 } | 435 } |
| 315 | 436 |
| 316 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) { | 437 void ChromeSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) { |
| 317 // No need to remove the bubble here, since either one of the following events | 438 // The only case in which the OnRecognitionEnd should not close the bubble is |
| 318 // must have happened prior to this callback: | 439 // when we are showing an error. In this case the bubble will be closed by |
| 319 // - A previous OnRecognitionResult event already closed the bubble. | 440 // the |InfoBubbleFocusChanged| method, when the users clicks either the |
| 320 // - An error occurred, so the bubble is showing the error and will be closed | 441 // "Cancel" button or outside of the bubble. |
| 321 // when it will lose focus (by InfoBubbleFocusChanged()). | 442 if (GetBubbleController()->GetActiveSessionID() == session_id && |
| 322 // - The bubble lost focus or the user pressed the Cancel button, thus it has | 443 !GetBubbleController()->IsShowingMessage()) { |
| 323 // been closed by InfoBubbleFocusChanged(), which triggered an AbortSession. | 444 DCHECK(RequiresBubble(session_id)); |
| 445 GetBubbleController()->CloseBubble(); |
| 446 } |
| 324 } | 447 } |
| 325 | 448 |
| 326 void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation( | 449 void ChromeSpeechRecognitionManagerDelegate::GetDiagnosticInformation( |
| 327 bool* can_report_metrics, | 450 bool* can_report_metrics, |
| 328 std::string* hardware_info) { | 451 std::string* hardware_info) { |
| 329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 330 if (!optional_request_info_.get()) { | 453 if (!optional_request_info_.get()) { |
| 331 optional_request_info_ = new OptionalRequestInfo(); | 454 optional_request_info_ = new OptionalRequestInfo(); |
| 332 // Since hardware info is optional with speech input requests, we start an | 455 // Since hardware info is optional with speech input requests, we start an |
| 333 // asynchronous fetch here and move on with recording audio. This first | 456 // asynchronous fetch here and move on with recording audio. This first |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 447 | 570 |
| 448 SpeechRecognitionTrayIconController* | 571 SpeechRecognitionTrayIconController* |
| 449 ChromeSpeechRecognitionManagerDelegate::GetTrayIconController() { | 572 ChromeSpeechRecognitionManagerDelegate::GetTrayIconController() { |
| 450 if (!tray_icon_controller_.get()) | 573 if (!tray_icon_controller_.get()) |
| 451 tray_icon_controller_ = new SpeechRecognitionTrayIconController(); | 574 tray_icon_controller_ = new SpeechRecognitionTrayIconController(); |
| 452 return tray_icon_controller_.get(); | 575 return tray_icon_controller_.get(); |
| 453 } | 576 } |
| 454 | 577 |
| 455 | 578 |
| 456 } // namespace speech | 579 } // namespace speech |
| OLD | NEW |