Home | History | Annotate | Download | only in extension_api
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
      6 
      7 #include <string>
      8 
      9 #include "base/json/json_writer.h"
     10 #include "base/values.h"
     11 #include "chrome/browser/extensions/component_loader.h"
     12 #include "chrome/browser/extensions/extension_service.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h"
     15 #include "chrome/browser/speech/extension_api/tts_extension_api.h"
     16 #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h"
     17 #include "chrome/browser/speech/tts_controller.h"
     18 #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h"
     19 #include "chrome/common/extensions/extension_constants.h"
     20 #include "content/public/browser/render_process_host.h"
     21 #include "content/public/browser/render_view_host.h"
     22 #include "content/public/common/console_message_level.h"
     23 #include "extensions/browser/event_router.h"
     24 #include "extensions/browser/extension_host.h"
     25 #include "extensions/browser/extension_registry.h"
     26 #include "extensions/browser/extension_system.h"
     27 #include "extensions/browser/process_manager.h"
     28 #include "extensions/common/extension.h"
     29 #include "extensions/common/extension_messages.h"
     30 #include "extensions/common/extension_set.h"
     31 #include "net/base/network_change_notifier.h"
     32 
     33 using extensions::EventRouter;
     34 using extensions::Extension;
     35 using extensions::ExtensionSystem;
     36 
     37 namespace constants = tts_extension_api_constants;
     38 
     39 namespace tts_engine_events {
     40 const char kOnSpeak[] = "ttsEngine.onSpeak";
     41 const char kOnStop[] = "ttsEngine.onStop";
     42 const char kOnPause[] = "ttsEngine.onPause";
     43 const char kOnResume[] = "ttsEngine.onResume";
     44 };  // namespace tts_engine_events
     45 
     46 namespace {
     47 
     48 void WarnIfMissingPauseOrResumeListener(
     49     Profile* profile, EventRouter* event_router, std::string extension_id) {
     50   bool has_onpause = event_router->ExtensionHasEventListener(
     51       extension_id, tts_engine_events::kOnPause);
     52   bool has_onresume = event_router->ExtensionHasEventListener(
     53       extension_id, tts_engine_events::kOnResume);
     54   if (has_onpause == has_onresume)
     55     return;
     56 
     57   extensions::ProcessManager* process_manager =
     58       ExtensionSystem::Get(profile)->process_manager();
     59   extensions::ExtensionHost* host =
     60       process_manager->GetBackgroundHostForExtension(extension_id);
     61   host->render_process_host()->Send(new ExtensionMsg_AddMessageToConsole(
     62       host->render_view_host()->GetRoutingID(),
     63       content::CONSOLE_MESSAGE_LEVEL_WARNING,
     64       constants::kErrorMissingPauseOrResume));
     65 }
     66 
     67 }  // namespace
     68 
     69 TtsExtensionEngine* TtsExtensionEngine::GetInstance() {
     70   return Singleton<TtsExtensionEngine>::get();
     71 }
     72 
     73 void TtsExtensionEngine::GetVoices(content::BrowserContext* browser_context,
     74     std::vector<VoiceData>* out_voices) {
     75   Profile* profile = Profile::FromBrowserContext(browser_context);
     76   EventRouter* event_router = EventRouter::Get(profile);
     77   DCHECK(event_router);
     78 
     79   bool is_offline = (net::NetworkChangeNotifier::GetConnectionType() ==
     80                      net::NetworkChangeNotifier::CONNECTION_NONE);
     81 
     82   const extensions::ExtensionSet& extensions =
     83       extensions::ExtensionRegistry::Get(profile)->enabled_extensions();
     84   extensions::ExtensionSet::const_iterator iter;
     85   for (iter = extensions.begin(); iter != extensions.end(); ++iter) {
     86     const Extension* extension = iter->get();
     87 
     88     if (!event_router->ExtensionHasEventListener(
     89             extension->id(), tts_engine_events::kOnSpeak) ||
     90         !event_router->ExtensionHasEventListener(
     91             extension->id(), tts_engine_events::kOnStop)) {
     92       continue;
     93     }
     94 
     95     const std::vector<extensions::TtsVoice>* tts_voices =
     96         extensions::TtsVoice::GetTtsVoices(extension);
     97     if (!tts_voices)
     98       continue;
     99 
    100     for (size_t i = 0; i < tts_voices->size(); ++i) {
    101       const extensions::TtsVoice& voice = tts_voices->at(i);
    102 
    103       // Don't return remote voices when the system is offline.
    104       if (voice.remote && is_offline)
    105         continue;
    106 
    107       out_voices->push_back(VoiceData());
    108       VoiceData& result_voice = out_voices->back();
    109 
    110       result_voice.native = false;
    111       result_voice.name = voice.voice_name;
    112       result_voice.lang = voice.lang;
    113       result_voice.remote = voice.remote;
    114       result_voice.extension_id = extension->id();
    115       if (voice.gender == constants::kGenderMale)
    116         result_voice.gender = TTS_GENDER_MALE;
    117       else if (voice.gender == constants::kGenderFemale)
    118         result_voice.gender = TTS_GENDER_FEMALE;
    119       else
    120         result_voice.gender = TTS_GENDER_NONE;
    121 
    122       for (std::set<std::string>::const_iterator iter =
    123                voice.event_types.begin();
    124            iter != voice.event_types.end();
    125            ++iter) {
    126         result_voice.events.insert(TtsEventTypeFromString(*iter));
    127       }
    128 
    129       // If the extension sends end events, the controller will handle
    130       // queueing and send interrupted and cancelled events.
    131       if (voice.event_types.find(constants::kEventTypeEnd) !=
    132           voice.event_types.end()) {
    133         result_voice.events.insert(TTS_EVENT_CANCELLED);
    134         result_voice.events.insert(TTS_EVENT_INTERRUPTED);
    135       }
    136     }
    137   }
    138 }
    139 
    140 void TtsExtensionEngine::Speak(Utterance* utterance,
    141                                const VoiceData& voice) {
    142   // See if the engine supports the "end" event; if so, we can keep the
    143   // utterance around and track it. If not, we're finished with this
    144   // utterance now.
    145   bool sends_end_event = voice.events.find(TTS_EVENT_END) != voice.events.end();
    146 
    147   scoped_ptr<base::ListValue> args(new base::ListValue());
    148   args->AppendString(utterance->text());
    149 
    150   // Pass through most options to the speech engine, but remove some
    151   // that are handled internally.
    152   scoped_ptr<base::DictionaryValue> options(static_cast<base::DictionaryValue*>(
    153       utterance->options()->DeepCopy()));
    154   if (options->HasKey(constants::kRequiredEventTypesKey))
    155     options->Remove(constants::kRequiredEventTypesKey, NULL);
    156   if (options->HasKey(constants::kDesiredEventTypesKey))
    157     options->Remove(constants::kDesiredEventTypesKey, NULL);
    158   if (sends_end_event && options->HasKey(constants::kEnqueueKey))
    159     options->Remove(constants::kEnqueueKey, NULL);
    160   if (options->HasKey(constants::kSrcIdKey))
    161     options->Remove(constants::kSrcIdKey, NULL);
    162   if (options->HasKey(constants::kIsFinalEventKey))
    163     options->Remove(constants::kIsFinalEventKey, NULL);
    164   if (options->HasKey(constants::kOnEventKey))
    165     options->Remove(constants::kOnEventKey, NULL);
    166 
    167   // Add the voice name and language to the options if they're not
    168   // already there, since they might have been picked by the TTS controller
    169   // rather than directly by the client that requested the speech.
    170   if (!options->HasKey(constants::kVoiceNameKey))
    171     options->SetString(constants::kVoiceNameKey, voice.name);
    172   if (!options->HasKey(constants::kLangKey))
    173     options->SetString(constants::kLangKey, voice.lang);
    174 
    175   args->Append(options.release());
    176   args->AppendInteger(utterance->id());
    177 
    178   scoped_ptr<extensions::Event> event(new extensions::Event(
    179       tts_engine_events::kOnSpeak, args.Pass()));
    180   Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
    181   event->restrict_to_browser_context = profile;
    182   EventRouter::Get(profile)
    183       ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
    184 }
    185 
    186 void TtsExtensionEngine::Stop(Utterance* utterance) {
    187   scoped_ptr<base::ListValue> args(new base::ListValue());
    188   scoped_ptr<extensions::Event> event(new extensions::Event(
    189       tts_engine_events::kOnStop, args.Pass()));
    190   Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
    191   event->restrict_to_browser_context = profile;
    192   EventRouter::Get(profile)
    193       ->DispatchEventToExtension(utterance->extension_id(), event.Pass());
    194 }
    195 
    196 void TtsExtensionEngine::Pause(Utterance* utterance) {
    197   scoped_ptr<base::ListValue> args(new base::ListValue());
    198   scoped_ptr<extensions::Event> event(new extensions::Event(
    199       tts_engine_events::kOnPause, args.Pass()));
    200   Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
    201   event->restrict_to_browser_context = profile;
    202   EventRouter* event_router = EventRouter::Get(profile);
    203   std::string id = utterance->extension_id();
    204   event_router->DispatchEventToExtension(id, event.Pass());
    205   WarnIfMissingPauseOrResumeListener(profile, event_router, id);
    206 }
    207 
    208 void TtsExtensionEngine::Resume(Utterance* utterance) {
    209   scoped_ptr<base::ListValue> args(new base::ListValue());
    210   scoped_ptr<extensions::Event> event(new extensions::Event(
    211       tts_engine_events::kOnResume, args.Pass()));
    212   Profile* profile = Profile::FromBrowserContext(utterance->browser_context());
    213   event->restrict_to_browser_context = profile;
    214   EventRouter* event_router = EventRouter::Get(profile);
    215   std::string id = utterance->extension_id();
    216   event_router->DispatchEventToExtension(id, event.Pass());
    217   WarnIfMissingPauseOrResumeListener(profile, event_router, id);
    218 }
    219 
    220 bool TtsExtensionEngine::LoadBuiltInTtsExtension(
    221     content::BrowserContext* browser_context) {
    222 #if defined(OS_CHROMEOS)
    223   Profile* profile = Profile::FromBrowserContext(browser_context);
    224   // Check to see if the engine was previously loaded.
    225   if (TtsEngineExtensionObserver::GetInstance(profile)->SawExtensionLoad(
    226           extension_misc::kSpeechSynthesisExtensionId, true)) {
    227     return false;
    228   }
    229 
    230   // Load the component extension into this profile.
    231   ExtensionService* extension_service =
    232       extensions::ExtensionSystem::Get(profile)->extension_service();
    233   DCHECK(extension_service);
    234   extension_service->component_loader()
    235       ->AddChromeOsSpeechSynthesisExtension();
    236   return true;
    237 #else
    238   return false;
    239 #endif
    240 }
    241 
    242 bool ExtensionTtsEngineSendTtsEventFunction::RunSync() {
    243   int utterance_id;
    244   EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &utterance_id));
    245 
    246   base::DictionaryValue* event;
    247   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &event));
    248 
    249   std::string event_type;
    250   EXTENSION_FUNCTION_VALIDATE(
    251       event->GetString(constants::kEventTypeKey, &event_type));
    252 
    253   int char_index = 0;
    254   if (event->HasKey(constants::kCharIndexKey)) {
    255     EXTENSION_FUNCTION_VALIDATE(
    256         event->GetInteger(constants::kCharIndexKey, &char_index));
    257   }
    258 
    259   // Make sure the extension has included this event type in its manifest.
    260   bool event_type_allowed = false;
    261   const std::vector<extensions::TtsVoice>* tts_voices =
    262       extensions::TtsVoice::GetTtsVoices(extension());
    263   if (!tts_voices) {
    264     error_ = constants::kErrorUndeclaredEventType;
    265     return false;
    266   }
    267 
    268   for (size_t i = 0; i < tts_voices->size(); i++) {
    269     const extensions::TtsVoice& voice = tts_voices->at(i);
    270     if (voice.event_types.find(event_type) != voice.event_types.end()) {
    271       event_type_allowed = true;
    272       break;
    273     }
    274   }
    275   if (!event_type_allowed) {
    276     error_ = constants::kErrorUndeclaredEventType;
    277     return false;
    278   }
    279 
    280   TtsController* controller = TtsController::GetInstance();
    281   if (event_type == constants::kEventTypeStart) {
    282     controller->OnTtsEvent(
    283         utterance_id, TTS_EVENT_START, char_index, std::string());
    284   } else if (event_type == constants::kEventTypeEnd) {
    285     controller->OnTtsEvent(
    286         utterance_id, TTS_EVENT_END, char_index, std::string());
    287   } else if (event_type == constants::kEventTypeWord) {
    288     controller->OnTtsEvent(
    289         utterance_id, TTS_EVENT_WORD, char_index, std::string());
    290   } else if (event_type == constants::kEventTypeSentence) {
    291     controller->OnTtsEvent(
    292         utterance_id, TTS_EVENT_SENTENCE, char_index, std::string());
    293   } else if (event_type == constants::kEventTypeMarker) {
    294     controller->OnTtsEvent(
    295         utterance_id, TTS_EVENT_MARKER, char_index, std::string());
    296   } else if (event_type == constants::kEventTypeError) {
    297     std::string error_message;
    298     event->GetString(constants::kErrorMessageKey, &error_message);
    299     controller->OnTtsEvent(
    300         utterance_id, TTS_EVENT_ERROR, char_index, error_message);
    301   } else if (event_type == constants::kEventTypePause) {
    302     controller->OnTtsEvent(
    303         utterance_id, TTS_EVENT_PAUSE, char_index, std::string());
    304   } else if (event_type == constants::kEventTypeResume) {
    305     controller->OnTtsEvent(
    306         utterance_id, TTS_EVENT_RESUME, char_index, std::string());
    307   } else {
    308     EXTENSION_FUNCTION_VALIDATE(false);
    309   }
    310 
    311   return true;
    312 }
    313