diff options
Diffstat (limited to 'chrome/browser/speech/extension_api')
7 files changed, 164 insertions, 33 deletions
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc index 4c9b8d5..fa7f944 100644 --- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc +++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc @@ -9,6 +9,8 @@ #include "base/json/json_writer.h" #include "base/values.h" #include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/profiles/profile.h" @@ -17,21 +19,50 @@ #include "chrome/browser/speech/tts_controller.h" #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_messages.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/common/console_message_level.h" +using extensions::EventRouter; using extensions::Extension; +using extensions::ExtensionSystem; namespace constants = tts_extension_api_constants; namespace tts_engine_events { const char kOnSpeak[] = "ttsEngine.onSpeak"; const char kOnStop[] = "ttsEngine.onStop"; +const char kOnPause[] = "ttsEngine.onPause"; +const char kOnResume[] = "ttsEngine.onResume"; }; // namespace tts_engine_events +namespace { +void WarnIfMissingPauseOrResumeListener( + Profile* profile, EventRouter* event_router, std::string extension_id) { + bool has_onpause = event_router->ExtensionHasEventListener( + extension_id, tts_engine_events::kOnPause); + bool has_onresume = event_router->ExtensionHasEventListener( + extension_id, tts_engine_events::kOnResume); + if (has_onpause == has_onresume) + return; + + ExtensionProcessManager* process_manager = + ExtensionSystem::Get(profile)->process_manager(); + extensions::ExtensionHost* host = + process_manager->GetBackgroundHostForExtension(extension_id); + host->render_process_host()->Send(new ExtensionMsg_AddMessageToConsole( + host->render_view_host()->GetRoutingID(), + content::CONSOLE_MESSAGE_LEVEL_WARNING, + constants::kErrorMissingPauseOrResume)); +}; +} // anonymous namespace + void GetExtensionVoices(Profile* profile, std::vector<VoiceData>* out_voices) { ExtensionService* service = profile->GetExtensionService(); DCHECK(service); - extensions::EventRouter* event_router = - extensions::ExtensionSystem::Get(profile)->event_router(); + EventRouter* event_router = + ExtensionSystem::Get(profile)->event_router(); DCHECK(event_router); const ExtensionSet* extensions = service->extensions(); @@ -118,7 +149,7 @@ void ExtensionTtsEngineSpeak(Utterance* utterance, const VoiceData& voice) { scoped_ptr<extensions::Event> event(new extensions::Event( tts_engine_events::kOnSpeak, args.Pass())); event->restrict_to_profile = utterance->profile(); - extensions::ExtensionSystem::Get(utterance->profile())->event_router()-> + ExtensionSystem::Get(utterance->profile())->event_router()-> DispatchEventToExtension(utterance->extension_id(), event.Pass()); } @@ -127,10 +158,34 @@ void ExtensionTtsEngineStop(Utterance* utterance) { scoped_ptr<extensions::Event> event(new extensions::Event( tts_engine_events::kOnStop, args.Pass())); event->restrict_to_profile = utterance->profile(); - extensions::ExtensionSystem::Get(utterance->profile())->event_router()-> + ExtensionSystem::Get(utterance->profile())->event_router()-> DispatchEventToExtension(utterance->extension_id(), event.Pass()); } +void ExtensionTtsEnginePause(Utterance* utterance) { + scoped_ptr<ListValue> args(new ListValue()); + scoped_ptr<extensions::Event> event(new extensions::Event( + tts_engine_events::kOnPause, args.Pass())); + Profile* profile = utterance->profile(); + event->restrict_to_profile = profile; + EventRouter* event_router = ExtensionSystem::Get(profile)->event_router(); + std::string id = utterance->extension_id(); + event_router->DispatchEventToExtension(id, event.Pass()); + WarnIfMissingPauseOrResumeListener(profile, event_router, id); +} + +void ExtensionTtsEngineResume(Utterance* utterance) { + scoped_ptr<ListValue> args(new ListValue()); + scoped_ptr<extensions::Event> event(new extensions::Event( + tts_engine_events::kOnResume, args.Pass())); + Profile* profile = utterance->profile(); + event->restrict_to_profile = profile; + EventRouter* event_router = ExtensionSystem::Get(profile)->event_router(); + std::string id = utterance->extension_id(); + event_router->DispatchEventToExtension(id, event.Pass()); + WarnIfMissingPauseOrResumeListener(profile, event_router, id); +} + bool ExtensionTtsEngineSendTtsEventFunction::RunImpl() { int utterance_id; std::string error_message; @@ -192,6 +247,12 @@ bool ExtensionTtsEngineSendTtsEventFunction::RunImpl() { event->GetString(constants::kErrorMessageKey, &error_message); controller->OnTtsEvent( utterance_id, TTS_EVENT_ERROR, char_index, error_message); + } else if (event_type == constants::kEventTypePause) { + controller->OnTtsEvent( + utterance_id, TTS_EVENT_PAUSE, char_index, std::string()); + } else if (event_type == constants::kEventTypeResume) { + controller->OnTtsEvent( + utterance_id, TTS_EVENT_RESUME, char_index, std::string()); } else { EXTENSION_FUNCTION_VALIDATE(false); } diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.h b/chrome/browser/speech/extension_api/tts_engine_extension_api.h index b87de3a..b77f4d9 100644 --- a/chrome/browser/speech/extension_api/tts_engine_extension_api.h +++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.h @@ -24,6 +24,8 @@ class Extension; namespace tts_engine_events { extern const char kOnSpeak[]; extern const char kOnStop[]; +extern const char kOnPause[]; +extern const char kOnResume[]; } // Return a list of all available voices registered by extensions. @@ -47,6 +49,12 @@ void ExtensionTtsEngineSpeak(Utterance* utterance, // associated with this utterance. void ExtensionTtsEngineStop(Utterance* utterance); +// Pause in the middle of speaking this utterance. +void ExtensionTtsEnginePause(Utterance* utterance); + +// Resume speaking this utterance. +void ExtensionTtsEngineResume(Utterance* utterance); + // Hidden/internal extension function used to allow TTS engine extensions // to send events back to the client that's calling tts.speak(). class ExtensionTtsEngineSendTtsEventFunction : public SyncExtensionFunction { diff --git a/chrome/browser/speech/extension_api/tts_extension_api.cc b/chrome/browser/speech/extension_api/tts_extension_api.cc index 117bf732..4137421 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api.cc +++ b/chrome/browser/speech/extension_api/tts_extension_api.cc @@ -40,6 +40,10 @@ const char *TtsEventTypeToString(TtsEventType event_type) { return constants::kEventTypeCancelled; case TTS_EVENT_ERROR: return constants::kEventTypeError; + case TTS_EVENT_PAUSE: + return constants::kEventTypePause; + case TTS_EVENT_RESUME: + return constants::kEventTypeResume; default: NOTREACHED(); return constants::kEventTypeError; @@ -63,6 +67,10 @@ TtsEventType TtsEventTypeFromString(const std::string& str) { return TTS_EVENT_CANCELLED; if (str == constants::kEventTypeError) return TTS_EVENT_ERROR; + if (str == constants::kEventTypePause) + return TTS_EVENT_PAUSE; + if (str == constants::kEventTypeResume) + return TTS_EVENT_RESUME; NOTREACHED(); return TTS_EVENT_ERROR; @@ -274,6 +282,16 @@ bool TtsStopSpeakingFunction::RunImpl() { return true; } +bool TtsPauseFunction::RunImpl() { + TtsController::GetInstance()->Pause(); + return true; +} + +bool TtsResumeFunction::RunImpl() { + TtsController::GetInstance()->Resume(); + return true; +} + bool TtsIsSpeakingFunction::RunImpl() { SetResult(Value::CreateBooleanValue( TtsController::GetInstance()->IsSpeaking())); @@ -301,35 +319,8 @@ bool TtsGetVoicesFunction::RunImpl() { ListValue* event_types = new ListValue(); for (std::set<TtsEventType>::iterator iter = voice.events.begin(); iter != voice.events.end(); ++iter) { - const char* event_name_constant = NULL; - switch (*iter) { - case TTS_EVENT_START: - event_name_constant = constants::kEventTypeStart; - break; - case TTS_EVENT_END: - event_name_constant = constants::kEventTypeEnd; - break; - case TTS_EVENT_WORD: - event_name_constant = constants::kEventTypeWord; - break; - case TTS_EVENT_SENTENCE: - event_name_constant = constants::kEventTypeSentence; - break; - case TTS_EVENT_MARKER: - event_name_constant = constants::kEventTypeMarker; - break; - case TTS_EVENT_INTERRUPTED: - event_name_constant = constants::kEventTypeInterrupted; - break; - case TTS_EVENT_CANCELLED: - event_name_constant = constants::kEventTypeCancelled; - break; - case TTS_EVENT_ERROR: - event_name_constant = constants::kEventTypeError; - break; - } - if (event_name_constant) - event_types->Append(Value::CreateStringValue(event_name_constant)); + const char* event_name_constant = TtsEventTypeToString(*iter); + event_types->Append(Value::CreateStringValue(event_name_constant)); } result_voice->Set(constants::kEventTypesKey, event_types); @@ -353,6 +344,8 @@ TtsAPI::TtsAPI(Profile* profile) { registry->RegisterFunction<TtsIsSpeakingFunction>(); registry->RegisterFunction<TtsSpeakFunction>(); registry->RegisterFunction<TtsStopSpeakingFunction>(); + registry->RegisterFunction<TtsPauseFunction>(); + registry->RegisterFunction<TtsResumeFunction>(); } TtsAPI::~TtsAPI() { diff --git a/chrome/browser/speech/extension_api/tts_extension_api.h b/chrome/browser/speech/extension_api/tts_extension_api.h index a70d95e..e2ed95c 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api.h +++ b/chrome/browser/speech/extension_api/tts_extension_api.h @@ -33,6 +33,20 @@ class TtsStopSpeakingFunction : public SyncExtensionFunction { DECLARE_EXTENSION_FUNCTION("tts.stop", TTS_STOP) }; +class TtsPauseFunction : public SyncExtensionFunction { + private: + virtual ~TtsPauseFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION("tts.pause", TTS_PAUSE) +}; + +class TtsResumeFunction : public SyncExtensionFunction { + private: + virtual ~TtsResumeFunction() {} + virtual bool RunImpl() OVERRIDE; + DECLARE_EXTENSION_FUNCTION("tts.resume", TTS_RESUME) +}; + class TtsIsSpeakingFunction : public SyncExtensionFunction { private: virtual ~TtsIsSpeakingFunction() {} diff --git a/chrome/browser/speech/extension_api/tts_extension_api_constants.cc b/chrome/browser/speech/extension_api/tts_extension_api_constants.cc index 3568a8d..7b8c45b 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api_constants.cc +++ b/chrome/browser/speech/extension_api/tts_extension_api_constants.cc @@ -35,6 +35,8 @@ const char kEventTypeMarker[] = "marker"; const char kEventTypeInterrupted[] = "interrupted"; const char kEventTypeCancelled[] = "cancelled"; const char kEventTypeError[] = "error"; +const char kEventTypePause[] = "pause"; +const char kEventTypeResume[] = "resume"; const char kErrorUndeclaredEventType[] = "Cannot send an event type that is not declared in the extension manifest."; @@ -44,5 +46,8 @@ const char kErrorInvalidGender[] = "Invalid gender."; const char kErrorInvalidRate[] = "Invalid rate."; const char kErrorInvalidPitch[] = "Invalid pitch."; const char kErrorInvalidVolume[] = "Invalid volume."; +const char kErrorMissingPauseOrResume[] = + "A TTS engine extension should either listen for both onPause and onResume " + "events, or neither."; } // namespace tts_extension_api_constants. diff --git a/chrome/browser/speech/extension_api/tts_extension_api_constants.h b/chrome/browser/speech/extension_api/tts_extension_api_constants.h index 3270e97..0809473 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api_constants.h +++ b/chrome/browser/speech/extension_api/tts_extension_api_constants.h @@ -40,6 +40,8 @@ extern const char kEventTypeMarker[]; extern const char kEventTypeInterrupted[]; extern const char kEventTypeCancelled[]; extern const char kEventTypeError[]; +extern const char kEventTypePause[]; +extern const char kEventTypeResume[]; extern const char kErrorUndeclaredEventType[]; extern const char kErrorUtteranceTooLong[]; @@ -48,6 +50,7 @@ extern const char kErrorInvalidGender[]; extern const char kErrorInvalidRate[]; extern const char kErrorInvalidPitch[]; extern const char kErrorInvalidVolume[]; +extern const char kErrorMissingPauseOrResume[]; } // namespace tts_extension_api_constants. #endif // CHROME_BROWSER_SPEECH_EXTENSION_API_TTS_EXTENSION_API_CONSTANTS_H_ diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc index dc4dc90..fcf86b1 100644 --- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc +++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc @@ -24,9 +24,14 @@ using ::testing::DoAll; using ::testing::InSequence; using ::testing::InvokeWithoutArgs; using ::testing::Return; +using ::testing::SaveArg; using ::testing::StrictMock; using ::testing::_; +namespace { +int g_saved_utterance_id; +} + class MockTtsPlatformImpl : public TtsPlatformImpl { public: MockTtsPlatformImpl() @@ -45,6 +50,10 @@ class MockTtsPlatformImpl : public TtsPlatformImpl { MOCK_METHOD0(StopSpeaking, bool(void)); + MOCK_METHOD0(Pause, void(void)); + + MOCK_METHOD0(Resume, void(void)); + MOCK_METHOD0(IsSpeaking, bool(void)); MOCK_METHOD1(GetVoices, void(std::vector<VoiceData>*)); @@ -53,6 +62,15 @@ class MockTtsPlatformImpl : public TtsPlatformImpl { set_error("epic fail"); } + void SendEndEventOnSavedUtteranceId() { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind( + &MockTtsPlatformImpl::SendEvent, + ptr_factory_.GetWeakPtr(), + false, g_saved_utterance_id, TTS_EVENT_END, 0, std::string()), + base::TimeDelta()); + } + void SendEndEvent(int utterance_id, const std::string& utterance, const std::string& lang, @@ -276,6 +294,35 @@ IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformWordCallbacks) { ASSERT_TRUE(RunExtensionTest("tts/word_callbacks")) << message_; } +IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformPauseResume) { + EXPECT_CALL(mock_platform_impl_, IsSpeaking()) + .Times(AnyNumber()); + + InSequence s; + EXPECT_CALL(mock_platform_impl_, Speak(_, "test 1", _, _, _)) + .WillOnce(DoAll( + Invoke(&mock_platform_impl_, + &MockTtsPlatformImpl::SendEndEvent), + Return(true))); + EXPECT_CALL(mock_platform_impl_, StopSpeaking()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_platform_impl_, Speak(_, "test 2", _, _, _)) + .WillOnce(DoAll( + SaveArg<0>(&g_saved_utterance_id), + Return(true))); + EXPECT_CALL(mock_platform_impl_, Pause()); + EXPECT_CALL(mock_platform_impl_, Resume()) + .WillOnce( + InvokeWithoutArgs( + &mock_platform_impl_, + &MockTtsPlatformImpl::SendEndEventOnSavedUtteranceId)); + ASSERT_TRUE(RunExtensionTest("tts/pause_resume")) << message_; +} + +// +// TTS Engine tests. +// + IN_PROC_BROWSER_TEST_F(TtsApiTest, RegisterEngine) { EXPECT_CALL(mock_platform_impl_, IsSpeaking()) .Times(AnyNumber()); |