Chromium Code Reviews| 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/extensions/speech_input/extension_speech_input_manager. h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/json/json_writer.h" | |
| 9 #include "base/utf_string_conversions.h" | |
| 10 #include "base/values.h" | |
| 11 #include "chrome/browser/extensions/extension_event_router.h" | |
| 12 #include "chrome/browser/extensions/speech_input/extension_speech_input_api_cons tants.h" | |
| 13 #include "chrome/browser/profiles/profile.h" | |
| 14 #include "chrome/browser/profiles/profile_dependency_manager.h" | |
| 15 #include "chrome/browser/profiles/profile_keyed_service.h" | |
| 16 #include "chrome/browser/profiles/profile_keyed_service_factory.h" | |
| 17 #include "chrome/common/chrome_notification_types.h" | |
| 18 #include "chrome/common/extensions/extension.h" | |
| 19 #include "content/browser/browser_thread.h" | |
| 20 #include "content/common/notification_service.h" | |
| 21 | |
| 22 using namespace speech_input; | |
| 23 | |
| 24 namespace constants = extension_speech_input_api_constants; | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 // Caller id provided to the speech recognizer. Since only one extension can | |
| 29 // be recording on the same time a constant value is enough as id. | |
| 30 static const int kSpeechCallerId = 1; | |
| 31 | |
| 32 // Wrap an ExtensionSpeechInputManager using scoped_refptr to avoid | |
| 33 // assertion failures on destruction because of not using release(). | |
| 34 class ExtensionSpeechInputManagerWrapper : public ProfileKeyedService { | |
| 35 public: | |
| 36 explicit ExtensionSpeechInputManagerWrapper( | |
| 37 ExtensionSpeechInputManager* manager) | |
| 38 : manager_(manager) {} | |
| 39 | |
| 40 virtual ~ExtensionSpeechInputManagerWrapper() {} | |
| 41 | |
| 42 ExtensionSpeechInputManager* manager() const { return manager_.get(); } | |
| 43 | |
| 44 private: | |
| 45 // Methods from ProfileKeyedService. | |
| 46 virtual void Shutdown() OVERRIDE { | |
| 47 manager()->ShutdownOnUIThread(); | |
| 48 } | |
| 49 | |
| 50 scoped_refptr<ExtensionSpeechInputManager> manager_; | |
| 51 }; | |
| 52 | |
| 53 } | |
| 54 | |
| 55 // Factory for ExtensionSpeechInputManagers as profile keyed services. | |
| 56 class ExtensionSpeechInputManager::Factory : public ProfileKeyedServiceFactory { | |
| 57 public: | |
| 58 static void Initialize(); | |
| 59 static Factory* GetInstance(); | |
| 60 | |
| 61 ExtensionSpeechInputManagerWrapper* GetForProfile(Profile* profile); | |
| 62 | |
| 63 private: | |
| 64 friend struct DefaultSingletonTraits<Factory>; | |
| 65 | |
| 66 Factory(); | |
| 67 virtual ~Factory(); | |
| 68 | |
| 69 // ProfileKeyedServiceFactory methods: | |
| 70 virtual ProfileKeyedService* BuildServiceInstanceFor( | |
| 71 Profile* profile) const OVERRIDE; | |
| 72 virtual bool ServiceRedirectedInIncognito() OVERRIDE { return false; } | |
| 73 virtual bool ServiceIsNULLWhileTesting() OVERRIDE { return true; } | |
| 74 virtual bool ServiceIsCreatedWithProfile() OVERRIDE { return true; } | |
| 75 | |
| 76 DISALLOW_COPY_AND_ASSIGN(Factory); | |
| 77 }; | |
| 78 | |
| 79 void ExtensionSpeechInputManager::Factory::Initialize() { | |
| 80 GetInstance(); | |
| 81 } | |
| 82 | |
| 83 ExtensionSpeechInputManager::Factory* | |
| 84 ExtensionSpeechInputManager::Factory::GetInstance() { | |
| 85 return Singleton<ExtensionSpeechInputManager::Factory>::get(); | |
| 86 } | |
| 87 | |
| 88 ExtensionSpeechInputManagerWrapper* | |
| 89 ExtensionSpeechInputManager::Factory::GetForProfile( | |
| 90 Profile* profile) { | |
| 91 return static_cast<ExtensionSpeechInputManagerWrapper*>( | |
| 92 GetServiceForProfile(profile, true)); | |
| 93 } | |
| 94 | |
| 95 ExtensionSpeechInputManager::Factory::Factory() | |
| 96 : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) { | |
| 97 } | |
| 98 | |
| 99 ExtensionSpeechInputManager::Factory::~Factory() { | |
| 100 } | |
| 101 | |
| 102 ProfileKeyedService* | |
| 103 ExtensionSpeechInputManager::Factory::BuildServiceInstanceFor( | |
| 104 Profile* profile) const { | |
| 105 scoped_refptr<ExtensionSpeechInputManager> manager( | |
| 106 new ExtensionSpeechInputManager(profile)); | |
| 107 return new ExtensionSpeechInputManagerWrapper(manager); | |
| 108 } | |
| 109 | |
| 110 ExtensionSpeechInterface::ExtensionSpeechInterface() { | |
| 111 } | |
| 112 | |
| 113 ExtensionSpeechInterface::~ExtensionSpeechInterface() { | |
| 114 } | |
| 115 | |
| 116 ExtensionSpeechInputManager::ExtensionSpeechInputManager(Profile* profile) | |
| 117 : profile_(profile), | |
| 118 state_(kIdle), | |
| 119 speech_interface_(NULL) { | |
| 120 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
| 121 Source<Profile>(profile_)); | |
| 122 } | |
| 123 | |
| 124 ExtensionSpeechInputManager::~ExtensionSpeechInputManager() { | |
| 125 } | |
| 126 | |
| 127 ExtensionSpeechInputManager* ExtensionSpeechInputManager::GetForProfile( | |
| 128 Profile* profile) { | |
| 129 ExtensionSpeechInputManagerWrapper *wrapper = | |
| 130 Factory::GetInstance()->GetForProfile(profile); | |
| 131 if (!wrapper) | |
| 132 return NULL; | |
| 133 return wrapper->manager(); | |
| 134 } | |
| 135 | |
| 136 void ExtensionSpeechInputManager::InitializeFactory() { | |
| 137 Factory::Initialize(); | |
| 138 } | |
| 139 | |
| 140 void ExtensionSpeechInputManager::Observe(int type, | |
| 141 const NotificationSource& source, | |
| 142 const NotificationDetails& details) { | |
| 143 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) | |
| 144 ExtensionUnloaded(Details<UnloadedExtensionInfo>(details)->extension->id()); | |
| 145 else | |
| 146 NOTREACHED(); | |
| 147 } | |
| 148 | |
| 149 void ExtensionSpeechInputManager::ShutdownOnUIThread() { | |
| 150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 151 VLOG(1) << "Profile shutting down."; | |
| 152 | |
| 153 base::AutoLock auto_lock(state_lock_); | |
| 154 DCHECK(state_ != kShutdown); | |
| 155 if (state_ != kIdle) { | |
| 156 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 157 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
| 158 } | |
| 159 state_ = kShutdown; | |
| 160 VLOG(1) << "Entering the shutdown sink state."; | |
| 161 registrar_.RemoveAll(); | |
| 162 profile_ = NULL; | |
| 163 } | |
| 164 | |
| 165 void ExtensionSpeechInputManager::ExtensionUnloaded( | |
| 166 const std::string& extension_id) { | |
| 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 168 | |
| 169 base::AutoLock auto_lock(state_lock_); | |
| 170 if (state_ == kShutdown) | |
| 171 return; | |
| 172 | |
| 173 VLOG(1) << "Extension unloaded. Requesting to enforce stop..."; | |
| 174 if (extension_id_in_use_ == extension_id) { | |
| 175 if (state_ != kIdle) { | |
| 176 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 177 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
| 178 } | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 void ExtensionSpeechInputManager::SetExtensionSpeechInterface( | |
| 183 ExtensionSpeechInterface* interface) { | |
| 184 speech_interface_ = interface; | |
| 185 } | |
| 186 | |
| 187 ExtensionSpeechInterface* | |
| 188 ExtensionSpeechInputManager::GetExtensionSpeechInterface() { | |
| 189 return speech_interface_ ? speech_interface_ : this; | |
| 190 } | |
| 191 | |
| 192 void ExtensionSpeechInputManager::ResetToIdleState() { | |
| 193 VLOG(1) << "State changed to idle. Deassociating any extensions."; | |
| 194 state_ = kIdle; | |
| 195 extension_id_in_use_.clear(); | |
| 196 } | |
| 197 | |
| 198 void ExtensionSpeechInputManager::SetRecognitionResult( | |
| 199 int caller_id, | |
| 200 const SpeechInputResult& result) { | |
| 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 202 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 203 | |
| 204 // Stopping will start the disassociation with the extension. | |
| 205 // Make a copy to report the results to the proper one. | |
| 206 std::string extension_id = extension_id_in_use_; | |
| 207 ForceStopOnIOThread(); | |
| 208 | |
| 209 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 210 base::Bind(&ExtensionSpeechInputManager::SetRecognitionResultOnUIThread, | |
| 211 this, result, extension_id)); | |
| 212 } | |
| 213 | |
| 214 void ExtensionSpeechInputManager::SetRecognitionResultOnUIThread( | |
| 215 const SpeechInputResult& result, const std::string& extension_id) { | |
| 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 217 | |
| 218 /*{ | |
|
Leandro GraciĆ” Gil
2011/10/12 18:24:46
Oops, this should be removed as it's not required.
| |
| 219 base::AutoLock auto_lock(state_lock_); | |
| 220 if (state_ == kShutdown) | |
| 221 return; | |
| 222 state_ = kIdle; | |
| 223 }*/ | |
| 224 | |
| 225 ListValue args; | |
| 226 DictionaryValue* js_event = new DictionaryValue(); | |
| 227 args.Append(js_event); | |
| 228 | |
| 229 ListValue* js_hypothesis_array = new ListValue(); | |
| 230 js_event->Set(constants::kHypothesesKey, js_hypothesis_array); | |
| 231 | |
| 232 for (size_t i = 0; i < result.hypotheses.size(); ++i) { | |
| 233 const SpeechInputHypothesis& hypothesis = result.hypotheses[i]; | |
| 234 | |
| 235 DictionaryValue* js_hypothesis_object = new DictionaryValue(); | |
| 236 js_hypothesis_array->Append(js_hypothesis_object); | |
| 237 | |
| 238 js_hypothesis_object->SetString(constants::kUtteranceKey, | |
| 239 UTF16ToUTF8(hypothesis.utterance)); | |
| 240 js_hypothesis_object->SetDouble(constants::kConfidenceKey, | |
| 241 hypothesis.confidence); | |
| 242 } | |
| 243 | |
| 244 std::string json_args; | |
| 245 base::JSONWriter::Write(&args, false, &json_args); | |
| 246 VLOG(1) << "Results: " << json_args; | |
| 247 DispatchEventToExtension(extension_id, constants::kOnResultEvent, json_args); | |
| 248 } | |
| 249 | |
| 250 void ExtensionSpeechInputManager::DidStartReceivingAudio(int caller_id) { | |
| 251 VLOG(1) << "DidStartReceivingAudio"; | |
| 252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 253 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 254 | |
| 255 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 256 base::Bind(&ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread, | |
| 257 this)); | |
| 258 } | |
| 259 | |
| 260 void ExtensionSpeechInputManager::DidCompleteRecording(int caller_id) { | |
| 261 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 262 } | |
| 263 | |
| 264 void ExtensionSpeechInputManager::DidCompleteRecognition(int caller_id) { | |
| 265 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 266 } | |
| 267 | |
| 268 void ExtensionSpeechInputManager::DidStartReceivingAudioOnUIThread() { | |
| 269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 270 | |
| 271 base::AutoLock auto_lock(state_lock_); | |
| 272 if (state_ == kShutdown) | |
| 273 return; | |
| 274 | |
| 275 DCHECK_EQ(state_, kStarting); | |
| 276 VLOG(1) << "State changed to recording"; | |
| 277 state_ = kRecording; | |
| 278 | |
| 279 VLOG(1) << "Sending start notification"; | |
| 280 NotificationService::current()->Notify( | |
| 281 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STARTED, | |
| 282 Source<Profile>(profile_), | |
| 283 Details<std::string>(&extension_id_in_use_)); | |
| 284 } | |
| 285 | |
| 286 void ExtensionSpeechInputManager::OnRecognizerError( | |
| 287 int caller_id, SpeechInputError error) { | |
| 288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 289 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 290 VLOG(1) << "OnRecognizerError: " << error; | |
| 291 | |
| 292 base::AutoLock auto_lock(state_lock_); | |
| 293 if (state_ == kShutdown) | |
| 294 return; | |
| 295 | |
| 296 // Release the recognizer object. | |
| 297 GetExtensionSpeechInterface()->StopRecording(true); | |
| 298 | |
| 299 std::string event_error_code; | |
| 300 bool report_to_event = true; | |
| 301 | |
| 302 switch (error) { | |
| 303 case kErrorNone: | |
| 304 break; | |
| 305 | |
| 306 case kErrorAudio: | |
| 307 if (state_ == kStarting) { | |
| 308 event_error_code = constants::kErrorUnableToStart; | |
| 309 report_to_event = false; | |
| 310 } else { | |
| 311 event_error_code = constants::kErrorCaptureError; | |
| 312 } | |
| 313 break; | |
| 314 | |
| 315 case kErrorNetwork: | |
| 316 event_error_code = constants::kErrorNetworkError; | |
| 317 break; | |
| 318 | |
| 319 case kErrorBadGrammar: | |
| 320 // No error is returned on invalid language, for example. | |
| 321 // To avoid confusion about when this is would be fired, the invalid | |
| 322 // params error is not being exposed to the onError event. | |
| 323 event_error_code = constants::kErrorUnableToStart; | |
| 324 break; | |
| 325 | |
| 326 case kErrorNoSpeech: | |
| 327 event_error_code = constants::kErrorNoSpeechHeard; | |
| 328 break; | |
| 329 | |
| 330 case kErrorNoMatch: | |
| 331 event_error_code = constants::kErrorNoResults; | |
| 332 break; | |
| 333 | |
| 334 // The remaining kErrorAborted case should never be returned by the server. | |
| 335 default: | |
| 336 NOTREACHED(); | |
| 337 } | |
| 338 | |
| 339 if (!event_error_code.empty()) { | |
| 340 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 341 base::Bind(&ExtensionSpeechInputManager::DispatchError, | |
| 342 this, event_error_code, report_to_event)); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 void ExtensionSpeechInputManager::DidCompleteEnvironmentEstimation( | |
| 347 int caller_id) { | |
| 348 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 349 } | |
| 350 | |
| 351 void ExtensionSpeechInputManager::DidStartReceivingSpeech(int caller_id) { | |
| 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 353 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 354 VLOG(1) << "DidStartReceivingSpeech"; | |
| 355 | |
| 356 std::string json_args; | |
| 357 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 358 base::Bind(&ExtensionSpeechInputManager::DispatchEventToExtension, | |
| 359 this, extension_id_in_use_, std::string(constants::kOnSoundStartEvent), | |
| 360 json_args)); | |
| 361 } | |
| 362 | |
| 363 void ExtensionSpeechInputManager::DidStopReceivingSpeech(int caller_id) { | |
| 364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 365 DCHECK_EQ(caller_id, kSpeechCallerId); | |
| 366 VLOG(1) << "DidStopReceivingSpeech"; | |
| 367 | |
| 368 std::string json_args; | |
| 369 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 370 base::Bind(&ExtensionSpeechInputManager::DispatchEventToExtension, | |
| 371 this, extension_id_in_use_, std::string(constants::kOnSoundEndEvent), | |
| 372 json_args)); | |
| 373 } | |
| 374 | |
| 375 void ExtensionSpeechInputManager::DispatchEventToExtension( | |
| 376 const std::string& extension_id, const std::string& event, | |
| 377 const std::string& json_args) { | |
| 378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 379 | |
| 380 base::AutoLock auto_lock(state_lock_); | |
| 381 if (state_ == kShutdown) | |
| 382 return; | |
| 383 | |
| 384 if (profile_ && profile_->GetExtensionEventRouter()) { | |
| 385 std::string final_args; | |
| 386 if (json_args.empty()) { | |
| 387 ListValue args; | |
| 388 base::JSONWriter::Write(&args, false, &final_args); | |
| 389 } else { | |
| 390 final_args = json_args; | |
| 391 } | |
| 392 | |
| 393 profile_->GetExtensionEventRouter()->DispatchEventToExtension( | |
| 394 extension_id, event, final_args, profile_, GURL()); | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 void ExtensionSpeechInputManager::DispatchError( | |
| 399 const std::string& error, bool dispatch_event) { | |
| 400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 401 | |
| 402 std::string extension_id; | |
| 403 { | |
| 404 base::AutoLock auto_lock(state_lock_); | |
| 405 if (state_ == kShutdown) | |
| 406 return; | |
| 407 | |
| 408 extension_id = extension_id_in_use_; | |
| 409 ResetToIdleState(); | |
| 410 | |
| 411 // Will set the error property in the ongoing extension function calls. | |
| 412 ExtensionError details(extension_id, error); | |
| 413 NotificationService::current()->Notify( | |
| 414 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_FAILED, | |
| 415 Source<Profile>(profile_), | |
| 416 Details<ExtensionError>(&details)); | |
| 417 } | |
| 418 | |
| 419 // Used for errors that are also reported via the onError event. | |
| 420 if (dispatch_event) { | |
| 421 ListValue args; | |
| 422 DictionaryValue *js_error = new DictionaryValue(); | |
| 423 args.Append(js_error); | |
| 424 js_error->SetString(constants::kErrorCodeKey, error); | |
| 425 std::string json_args; | |
| 426 base::JSONWriter::Write(&args, false, &json_args); | |
| 427 DispatchEventToExtension(extension_id, | |
| 428 constants::kOnErrorEvent, json_args); | |
| 429 } | |
| 430 } | |
| 431 | |
| 432 bool ExtensionSpeechInputManager::Start(const std::string& extension_id, | |
| 433 const std::string& language, const std::string& grammar, | |
| 434 bool filter_profanities, std::string* error) { | |
| 435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 436 DCHECK(error); | |
| 437 VLOG(1) << "Requesting start (UI thread)"; | |
| 438 | |
| 439 base::AutoLock auto_lock(state_lock_); | |
| 440 if (state_ == kShutdown || | |
| 441 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) { | |
| 442 *error = constants::kErrorRequestDenied; | |
| 443 return false; | |
| 444 } | |
| 445 | |
| 446 switch (state_) { | |
| 447 case kIdle: | |
| 448 break; | |
| 449 | |
| 450 case kStarting: | |
| 451 *error = constants::kErrorRequestInProgress; | |
| 452 return false; | |
| 453 | |
| 454 case kRecording: | |
| 455 case kStopping: | |
| 456 *error = constants::kErrorInvalidOperation; | |
| 457 return false; | |
| 458 | |
| 459 default: | |
| 460 NOTREACHED(); | |
| 461 } | |
| 462 | |
| 463 extension_id_in_use_ = extension_id; | |
| 464 VLOG(1) << "State changed to starting"; | |
| 465 state_ = kStarting; | |
| 466 | |
| 467 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 468 base::Bind(&ExtensionSpeechInputManager::StartOnIOThread, this, | |
| 469 profile_->GetRequestContext(), language, grammar, filter_profanities)); | |
| 470 return true; | |
| 471 } | |
| 472 | |
| 473 void ExtensionSpeechInputManager::StartOnIOThread( | |
| 474 net::URLRequestContextGetter* context_getter, | |
| 475 const std::string& language, const std::string& grammar, | |
| 476 bool filter_profanities) { | |
| 477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 478 VLOG(1) << "Requesting start (IO thread)"; | |
| 479 | |
| 480 // Everything put inside the lock to ensure the validity of context_getter, | |
| 481 // guaranteed while not in the shutdown state. Any ongoing or recognition | |
| 482 // request will be requested to be aborted when entering the shutdown state. | |
| 483 base::AutoLock auto_lock(state_lock_); | |
| 484 if (state_ == kShutdown) | |
| 485 return; | |
| 486 | |
| 487 if (!GetExtensionSpeechInterface()->CheckForAvailableDevices()) { | |
| 488 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 489 base::Bind(&ExtensionSpeechInputManager::DispatchError, this, | |
| 490 std::string(constants::kErrorNoDevicesAvailable), false)); | |
| 491 return; | |
| 492 } | |
| 493 | |
| 494 GetExtensionSpeechInterface()->StartRecording(this, context_getter, | |
| 495 kSpeechCallerId, language, grammar, filter_profanities); | |
| 496 } | |
| 497 | |
| 498 bool ExtensionSpeechInputManager::CheckForAvailableDevices() { | |
| 499 // TODO(leandrogracia): ensure recording exclusivity with other features | |
| 500 // in Chrome like web speech input. | |
| 501 return AudioManager::GetAudioManager()->HasAudioInputDevices(); | |
| 502 } | |
| 503 | |
| 504 void ExtensionSpeechInputManager::StartRecording( | |
| 505 speech_input::SpeechRecognizerDelegate* delegate, | |
| 506 net::URLRequestContextGetter* context_getter, int caller_id, | |
| 507 const std::string& language, const std::string& grammar, | |
| 508 bool filter_profanities) { | |
| 509 DCHECK(!recognizer_); | |
| 510 recognizer_ = new SpeechRecognizer(delegate, caller_id, language, grammar, | |
| 511 context_getter, filter_profanities, "", ""); | |
| 512 recognizer_->StartRecording(); | |
| 513 } | |
| 514 | |
| 515 bool ExtensionSpeechInputManager::HasValidRecognizer() const { | |
| 516 // Conditional expression used to avoid a performance warning on windows. | |
| 517 return recognizer_ ? true : false; | |
| 518 } | |
| 519 | |
| 520 bool ExtensionSpeechInputManager::Stop(const std::string& extension_id, | |
| 521 std::string* error) { | |
| 522 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 523 DCHECK(error); | |
| 524 VLOG(1) << "Requesting stop (UI thread)"; | |
| 525 | |
| 526 base::AutoLock auto_lock(state_lock_); | |
| 527 if (state_ == kShutdown || | |
| 528 (!extension_id_in_use_.empty() && extension_id_in_use_ != extension_id)) { | |
| 529 *error = constants::kErrorRequestDenied; | |
| 530 return false; | |
| 531 } | |
| 532 | |
| 533 switch (state_) { | |
| 534 case kRecording: | |
| 535 break; | |
| 536 | |
| 537 case kStopping: | |
| 538 *error = constants::kErrorRequestInProgress; | |
| 539 return false; | |
| 540 | |
| 541 case kIdle: | |
| 542 case kStarting: | |
| 543 *error = constants::kErrorInvalidOperation; | |
| 544 return false; | |
| 545 | |
| 546 default: | |
| 547 NOTREACHED(); | |
| 548 } | |
| 549 | |
| 550 // Guarded by the state lock. | |
| 551 DCHECK(GetExtensionSpeechInterface()->HasValidRecognizer()); | |
| 552 | |
| 553 VLOG(1) << "State changed to stopping"; | |
| 554 state_ = kStopping; | |
| 555 | |
| 556 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 557 base::Bind(&ExtensionSpeechInputManager::ForceStopOnIOThread, this)); | |
| 558 return true; | |
| 559 } | |
| 560 | |
| 561 void ExtensionSpeechInputManager::ForceStopOnIOThread() { | |
| 562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 563 VLOG(1) << "Requesting forced stop (IO thread)"; | |
| 564 | |
| 565 base::AutoLock auto_lock(state_lock_); | |
| 566 DCHECK(state_ != kIdle); | |
| 567 | |
| 568 GetExtensionSpeechInterface()->StopRecording(false); | |
| 569 | |
| 570 if (state_ == kShutdown) | |
| 571 return; | |
| 572 | |
| 573 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 574 base::Bind(&ExtensionSpeechInputManager::StopSucceededOnUIThread, this)); | |
| 575 } | |
| 576 | |
| 577 void ExtensionSpeechInputManager::StopRecording(bool recognition_failed) { | |
| 578 if (recognizer_) { | |
| 579 // Recognition is already cancelled in case of failure. | |
| 580 // Double-cancelling leads to assertion failures. | |
| 581 if (!recognition_failed) | |
| 582 recognizer_->CancelRecognition(); | |
| 583 recognizer_.release(); | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 void ExtensionSpeechInputManager::StopSucceededOnUIThread() { | |
| 588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 589 VLOG(1) << "Stop succeeded (UI thread)"; | |
| 590 | |
| 591 base::AutoLock auto_lock(state_lock_); | |
| 592 if (state_ == kShutdown) | |
| 593 return; | |
| 594 | |
| 595 std::string extension_id = extension_id_in_use_; | |
| 596 ResetToIdleState(); | |
| 597 | |
| 598 NotificationService::current()->Notify( | |
| 599 chrome::NOTIFICATION_EXTENSION_SPEECH_INPUT_RECORDING_STOPPED, | |
| 600 Source<Profile>(profile_), // Guarded by the state_ == kShutdown check. | |
| 601 Details<std::string>(&extension_id)); | |
| 602 } | |
| OLD | NEW |