summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/extensions/api/system_private/system_private_api.cc6
-rw-r--r--chrome/browser/extensions/api/system_private/system_private_api.h12
-rw-r--r--chrome/browser/extensions/component_loader.cc9
-rw-r--r--chrome/browser/extensions/component_loader.h3
-rw-r--r--chrome/browser/extensions/extension_function_histogram_value.h1
-rw-r--r--chrome/browser/resources/component_extension_resources.grd2
-rw-r--r--chrome/browser/resources/network_speech_synthesis/manifest.json90
-rw-r--r--chrome/browser/resources/network_speech_synthesis/tts_extension.js251
-rw-r--r--chrome/browser/speech/extension_api/tts_engine_extension_api.cc1
-rw-r--r--chrome/browser/speech/extension_api/tts_extension_api.cc1
-rw-r--r--chrome/browser/speech/extension_api/tts_extension_api_constants.cc45
-rw-r--r--chrome/browser/speech/extension_api/tts_extension_api_constants.h43
-rw-r--r--chrome/browser/speech/extension_api/tts_extension_apitest.cc14
-rw-r--r--chrome/browser/speech/tts_controller.cc2
-rw-r--r--chrome/browser/speech/tts_controller.h4
-rw-r--r--chrome/browser/speech/tts_message_filter.cc2
-rw-r--r--chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc10
-rw-r--r--chrome/common/extensions/api/speech/tts_engine_manifest_handler.h1
-rw-r--r--chrome/common/extensions/api/system_private.json18
-rw-r--r--chrome/common/extensions/api/tts.json5
-rw-r--r--chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/manifest.json10
-rw-r--r--chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/test.js17
-rw-r--r--extensions/common/manifest_constants.cc3
-rw-r--r--extensions/common/manifest_constants.h2
25 files changed, 507 insertions, 46 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index db93304..687a4d0 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -149,6 +149,7 @@
<include name="IDR_INSTANT_JS" file="resources\instant\instant.js" flattenhtml="true" type="BINDATA" />
<include name="IDR_NET_EXPORT_HTML" file="resources\net_export\net_export.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_NET_EXPORT_JS" file="resources\net_export\net_export.js" flattenhtml="true" type="BINDATA" />
+ <include name="IDR_NETWORK_SPEECH_SYNTHESIS_MANIFEST" file="resources\network_speech_synthesis\manifest.json" type="BINDATA" />
<if expr="not is_android">
<include name="IDR_PERFORMANCE_MONITOR_CHART_CSS" file="resources\performance_monitor\chart.css" flattenhtml="true" type="BINDATA" />
<include name="IDR_PERFORMANCE_MONITOR_CHART_JS" file="resources\performance_monitor\chart.js" type="BINDATA" />
diff --git a/chrome/browser/extensions/api/system_private/system_private_api.cc b/chrome/browser/extensions/api/system_private/system_private_api.cc
index 20e3c37..0ef4103 100644
--- a/chrome/browser/extensions/api/system_private/system_private_api.cc
+++ b/chrome/browser/extensions/api/system_private/system_private_api.cc
@@ -11,6 +11,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/system_private.h"
#include "chrome/common/pref_names.h"
+#include "google_apis/google_api_keys.h"
#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
@@ -132,6 +133,11 @@ bool SystemPrivateGetUpdateStatusFunction::RunImpl() {
return true;
}
+bool SystemPrivateGetApiKeyFunction::RunImpl() {
+ SetResult(new base::StringValue(google_apis::GetAPIKey()));
+ return true;
+}
+
void DispatchVolumeChangedEvent(double volume, bool is_volume_muted) {
base::DictionaryValue* dict = new base::DictionaryValue();
dict->SetDouble(kVolumeKey, volume);
diff --git a/chrome/browser/extensions/api/system_private/system_private_api.h b/chrome/browser/extensions/api/system_private/system_private_api.h
index 33f91ba..ddb820b 100644
--- a/chrome/browser/extensions/api/system_private/system_private_api.h
+++ b/chrome/browser/extensions/api/system_private/system_private_api.h
@@ -39,6 +39,18 @@ class SystemPrivateGetUpdateStatusFunction
virtual bool RunImpl() OVERRIDE;
};
+// API function which returns the Google API key.
+class SystemPrivateGetApiKeyFunction : public SyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("systemPrivate.getApiKey", SYSTEMPRIVATE_GETAPIKEY)
+
+ protected:
+ virtual ~SystemPrivateGetApiKeyFunction() {}
+
+ // ExtensionFunction:
+ virtual bool RunImpl() OVERRIDE;
+};
+
// Dispatches systemPrivate.onBrightnessChanged event for extensions.
void DispatchBrightnessChangedEvent(int brightness, bool user_initiated);
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 07ea15e..c023be6 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -316,6 +316,11 @@ void ComponentLoader::AddBookmarksExtensions() {
#endif
}
+void ComponentLoader::AddNetworkSpeechSynthesisExtension() {
+ Add(IDR_NETWORK_SPEECH_SYNTHESIS_MANIFEST,
+ base::FilePath(FILE_PATH_LITERAL("network_speech_synthesis")));
+}
+
void ComponentLoader::AddWithName(int manifest_resource_id,
const base::FilePath& root_directory,
const std::string& name) {
@@ -536,6 +541,10 @@ void ComponentLoader::AddDefaultComponentExtensionsWithBackgroundPages(
base::FilePath(FILE_PATH_LITERAL("google_now")));
}
#endif
+
+#if defined(GOOGLE_CHROME_BUILD)
+ AddNetworkSpeechSynthesisExtension();
+#endif // defined(GOOGLE_CHROME_BUILD)
}
void ComponentLoader::UnloadComponent(ComponentExtensionInfo* component) {
diff --git a/chrome/browser/extensions/component_loader.h b/chrome/browser/extensions/component_loader.h
index 8b1a961..1dd2e8d 100644
--- a/chrome/browser/extensions/component_loader.h
+++ b/chrome/browser/extensions/component_loader.h
@@ -115,6 +115,7 @@ class ComponentLoader {
void AddHangoutServicesExtension();
void AddImageLoaderExtension();
void AddBookmarksExtensions();
+ void AddNetworkSpeechSynthesisExtension();
void AddWithName(int manifest_resource_id,
const base::FilePath& root_directory,
@@ -135,6 +136,8 @@ class ComponentLoader {
typedef std::vector<ComponentExtensionInfo> RegisteredComponentExtensions;
RegisteredComponentExtensions component_extensions_;
+ FRIEND_TEST_ALL_PREFIXES(TtsApiTest, NetworkSpeechEngine);
+
DISALLOW_COPY_AND_ASSIGN(ComponentLoader);
};
diff --git a/chrome/browser/extensions/extension_function_histogram_value.h b/chrome/browser/extensions/extension_function_histogram_value.h
index 14d357d..42e5ccd 100644
--- a/chrome/browser/extensions/extension_function_histogram_value.h
+++ b/chrome/browser/extensions/extension_function_histogram_value.h
@@ -682,6 +682,7 @@ enum HistogramValue {
APP_CURRENTWINDOWINTERNAL_SETMINHEIGHT,
APP_CURRENTWINDOWINTERNAL_SETMAXWIDTH,
APP_CURRENTWINDOWINTERNAL_SETMAXHEIGHT,
+ SYSTEMPRIVATE_GETAPIKEY,
ENUM_BOUNDARY // Last entry: Add new entries above.
};
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index f1a9202..5107a7d 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -21,6 +21,8 @@
</if>
</structures>
<includes>
+ <include name="IDR_NETWORK_SPEECH_SYNTHESIS_JS" file="network_speech_synthesis/tts_extension.js" type="BINDATA" />
+
<include name="IDR_BOOKMARK_MANAGER_BOOKMARK_MANAGER_SEARCH" file="bookmark_manager/images/bookmark_manager_search.png" type="BINDATA" />
<include name="IDR_BOOKMARK_MANAGER_BOOKMARK_MANAGER_SEARCH_RTL" file="bookmark_manager/images/bookmark_manager_search_rtl.png" type="BINDATA" />
<include name="IDR_BOOKMARK_MANAGER_BOOKMARK_MAIN_JS" file="bookmark_manager/js/main.js" type="BINDATA" />
diff --git a/chrome/browser/resources/network_speech_synthesis/manifest.json b/chrome/browser/resources/network_speech_synthesis/manifest.json
new file mode 100644
index 0000000..9019ee6
--- /dev/null
+++ b/chrome/browser/resources/network_speech_synthesis/manifest.json
@@ -0,0 +1,90 @@
+{
+ "background": {
+ "scripts": [ "tts_extension.js" ],
+ "persistent": false
+ },
+ "description": "Component extension providing speech via the Google network text-to-speech service.",
+ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8GSbNUMGygqQTNDMFGIjZNcwXsHLzkNkHjWbuY37PbNdSDZ4VqlVjzbWqODSe+MjELdv5Keb51IdytnoGYXBMyqKmWpUrg+RnKvQ5ibWr4MW9pyIceOIdp9GrzC1WZGgTmZismYR3AjaIpufZ7xDdQQv+XrghPWCkdVqLN+qZDA1HU+DURznkMICiDDSH2sU0egm9UbWfS218bZqzKeQDiC3OnTPlaxcbJtKUuupIm5knjze3Wo9Ae9poTDMzKgchg0VlFCv3uqox+wlD8sjXBoyBCCK9HpImdVAF1a7jpdgiUHpPeV/26oYzM9/grltwNR3bzECQgSpyXp0eyoegwIDAQAB",
+ "manifest_version": 2,
+ "name": "Google Network Speech",
+ "permissions": [
+ "systemPrivate",
+ "ttsEngine",
+ "https://www.google.com/"
+ ],
+ "tts_engine": {
+ "voices": [
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "en-US",
+ "voice_name": "Google US English",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "male",
+ "lang": "en-GB",
+ "voice_name": "Google UK English Male",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "en-GB",
+ "voice_name": "Google UK English Female",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "es-ES",
+ "voice_name": "Google Español",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "fr-FR",
+ "voice_name": "Google Français",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "it-IT",
+ "voice_name": "Google Italiano",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "de-DE",
+ "voice_name": "Google Deutsch",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "ja-JP",
+ "voice_name": "Google 日本人",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "ko-KR",
+ "voice_name": "Google 한국의",
+ "remote": true
+ },
+ {
+ "event_types": [ "start", "end", "error" ],
+ "gender": "female",
+ "lang": "zh-CN",
+ "voice_name": "Google 中国的",
+ "remote": true
+ }
+ ]
+ },
+ "version": "1.0"
+}
diff --git a/chrome/browser/resources/network_speech_synthesis/tts_extension.js b/chrome/browser/resources/network_speech_synthesis/tts_extension.js
new file mode 100644
index 0000000..b2a3e71
--- /dev/null
+++ b/chrome/browser/resources/network_speech_synthesis/tts_extension.js
@@ -0,0 +1,251 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * This is a component extension that implements a text-to-speech (TTS)
+ * engine powered by Google's speech synthesis API.
+ *
+ * This is an "event page", so it's not loaded when the API isn't being used,
+ * and doesn't waste resources. When a web page or web app makes a speech
+ * request and the parameters match one of the voices in this extension's
+ * manifest, it makes a request to Google's API using Chrome's private key
+ * and plays the resulting speech using HTML5 audio.
+ */
+
+/**
+ * The main class for this extension. Adds listeners to
+ * chrome.ttsEngine.onSpeak and chrome.ttsEngine.onStop and implements
+ * them using Google's speech synthesis API.
+ * @constructor
+ */
+function TtsExtension() {}
+
+TtsExtension.prototype = {
+ /**
+ * The url prefix of the speech server, including static query
+ * parameters that don't change.
+ * @type {string}
+ * @const
+ * @private
+ */
+ SPEECH_SERVER_URL_:
+ 'https://www.google.com/speech-api/v2/synthesize?' +
+ 'enc=mpeg&client=chromium',
+
+ /**
+ * A mapping from language and gender to voice name, hardcoded for now
+ * until the speech synthesis server capabilities response provides this.
+ * The key of this map is of the form '<lang>-<gender>'.
+ * @type {Object.<string, string>}
+ * @private
+ */
+ LANG_AND_GENDER_TO_VOICE_NAME_: {
+ 'en-gb-male': 'rjs',
+ 'en-gb-female': 'fis',
+ },
+
+ /**
+ * The arguments passed to the onSpeak event handler for the utterance
+ * that's currently being spoken. Should be null when no object is
+ * pending.
+ *
+ * @type {?{utterance: string, options: Object, callback: Function}}
+ * @private
+ */
+ currentUtterance_: null,
+
+ /**
+ * The HTML5 audio element we use for playing the sound served by the
+ * speech server.
+ * @type {HTMLAudioElement}
+ * @private
+ */
+ audioElement_: null,
+
+ /**
+ * A mapping from voice name to language and gender, derived from the
+ * manifest file. This is used in case the speech synthesis request
+ * specifies a voice name but doesn't specify a language code or gender.
+ * @type {Object.<string, {lang: string, gender: string}>}
+ * @private
+ */
+ voiceNameToLangAndGender_: {},
+
+ /**
+ * This is the main function called to initialize this extension.
+ * Initializes data structures and adds event listeners.
+ */
+ init: function() {
+ // Get voices from manifest.
+ var voices = chrome.app.getDetails().tts_engine.voices;
+ for (var i = 0; i < voices.length; i++) {
+ this.voiceNameToLangAndGender_[voices[i].voice_name] = {
+ lang: voices[i].lang,
+ gender: voices[i].gender
+ };
+ }
+
+ // Initialize the audio element and event listeners on it.
+ this.audioElement_ = document.createElement('audio');
+ document.body.appendChild(this.audioElement_);
+ this.audioElement_.addEventListener(
+ 'ended', this.onStop_.bind(this), false);
+ this.audioElement_.addEventListener(
+ 'canplaythrough', this.onStart_.bind(this), false);
+
+ // Install event listeners for the ttsEngine API.
+ chrome.ttsEngine.onSpeak.addListener(this.onSpeak_.bind(this));
+ chrome.ttsEngine.onStop.addListener(this.onStop_.bind(this));
+ chrome.ttsEngine.onPause.addListener(this.onPause_.bind(this));
+ chrome.ttsEngine.onResume.addListener(this.onResume_.bind(this));
+ },
+
+ /**
+ * Handler for the chrome.ttsEngine.onSpeak interface.
+ * Gets Chrome's Google API key and then uses it to generate a request
+ * url for the requested speech utterance. Sets that url as the source
+ * of the HTML5 audio element.
+ * @param {string} utterance The text to be spoken.
+ * @param {Object} options Options to control the speech, as defined
+ * in the Chrome ttsEngine extension API.
+ * @private
+ */
+ onSpeak_: function(utterance, options, callback) {
+ // Truncate the utterance if it's too long. Both Chrome's tts
+ // extension api and the web speech api specify 32k as the
+ // maximum limit for an utterance.
+ if (utterance.length > 32768)
+ utterance = utterance.substr(0, 32768);
+
+ try {
+ // First, stop any pending audio.
+ this.onStop_();
+
+ this.currentUtterance_ = {
+ utterance: utterance,
+ options: options,
+ callback: callback
+ };
+
+ var lang = options.lang;
+ var gender = options.gender;
+ if (options.voiceName) {
+ lang = this.voiceNameToLangAndGender_[options.voiceName].lang;
+ gender = this.voiceNameToLangAndGender_[options.voiceName].gender;
+ }
+
+ // Look up the specific voice name for this language and gender.
+ // If it's not in the map, it doesn't matter - the language will
+ // be used directly. This is only used for languages where more
+ // than one gender is actually available.
+ var key = lang.toLowerCase() + '-' + gender;
+ var voiceName = this.LANG_AND_GENDER_TO_VOICE_NAME_[key];
+
+ var url = this.SPEECH_SERVER_URL_;
+ chrome.systemPrivate.getApiKey((function(key) {
+ url += '&key=' + key;
+ url += '&text=' + escape(utterance);
+ url += '&lang=' + lang.toLowerCase();
+
+ if (voiceName)
+ url += '&name=' + voiceName;
+
+ if (options.rate) {
+ // Input rate is between 0.1 and 10.0 with a default of 1.0.
+ // Output speed is between 0.0 and 1.0 with a default of 0.5.
+ url += '&speed=' + (options.rate / 2.0);
+ }
+
+ if (options.pitch) {
+ // Input pitch is between 0.0 and 2.0 with a default of 1.0.
+ // Output pitch is between 0.0 and 1.0 with a default of 0.5.
+ url += '&pitch=' + (options.pitch / 2.0);
+ }
+
+ // This begins loading the audio but does not play it.
+ // When enough of the audio has loaded to begin playback,
+ // the 'canplaythrough' handler will call this.onStart_,
+ // which sends a start event to the ttsEngine callback and
+ // then begins playing audio.
+ this.audioElement_.src = url;
+ }).bind(this));
+ } catch (err) {
+ console.error(String(err));
+ callback({
+ 'type': 'error',
+ 'errorMessage': String(err)
+ });
+ this.currentUtterance_ = null;
+ }
+ },
+
+ /**
+ * Handler for the chrome.ttsEngine.onStop interface.
+ * Called either when the ttsEngine API requests us to stop, or when
+ * we reach the end of the audio stream. Pause the audio element to
+ * silence it, and send a callback to the ttsEngine API to let it know
+ * that we've completed. Note that the ttsEngine API manages callback
+ * messages and will automatically replace the 'end' event with a
+ * more specific callback like 'interrupted' when sending it to the
+ * TTS client.
+ * @private
+ */
+ onStop_: function() {
+ if (this.currentUtterance_) {
+ this.audioElement_.pause();
+ this.currentUtterance_.callback({
+ 'type': 'end',
+ 'charIndex': this.currentUtterance_.utterance.length
+ });
+ }
+ this.currentUtterance_ = null;
+ },
+
+ /**
+ * Handler for the canplaythrough event on the audio element.
+ * Called when the audio element has buffered enough audio to begin
+ * playback. Send the 'start' event to the ttsEngine callback and
+ * then begin playing the audio element.
+ * @private
+ */
+ onStart_: function() {
+ if (this.currentUtterance_) {
+ if (this.currentUtterance_.options.volume !== undefined) {
+ // Both APIs use the same range for volume, between 0.0 and 1.0.
+ this.audioElement_.volume = this.currentUtterance_.options.volume;
+ }
+ this.audioElement_.play();
+ this.currentUtterance_.callback({
+ 'type': 'start',
+ 'charIndex': 0
+ });
+ }
+ },
+
+ /**
+ * Handler for the chrome.ttsEngine.onPause interface.
+ * Pauses audio if we're in the middle of an utterance.
+ * @private
+ */
+ onPause_: function() {
+ if (this.currentUtterance_) {
+ this.audioElement_.pause();
+ }
+ },
+
+ /**
+ * Handler for the chrome.ttsEngine.onPause interface.
+ * Resumes audio if we're in the middle of an utterance.
+ * @private
+ */
+ onResume_: function() {
+ if (this.currentUtterance_) {
+ this.audioElement_.play();
+ }
+ }
+
+};
+
+(new TtsExtension()).init();
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 ba5e65b..b6908760 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -91,6 +91,7 @@ void GetExtensionVoices(Profile* profile, std::vector<VoiceData>* out_voices) {
result_voice.native = false;
result_voice.name = voice.voice_name;
result_voice.lang = voice.lang;
+ result_voice.remote = voice.remote;
result_voice.extension_id = extension->id();
if (voice.gender == constants::kGenderMale)
result_voice.gender = TTS_GENDER_MALE;
diff --git a/chrome/browser/speech/extension_api/tts_extension_api.cc b/chrome/browser/speech/extension_api/tts_extension_api.cc
index 6523b4e..14c5641 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api.cc
@@ -307,6 +307,7 @@ bool TtsGetVoicesFunction::RunImpl() {
const VoiceData& voice = voices[i];
DictionaryValue* result_voice = new DictionaryValue();
result_voice->SetString(constants::kVoiceNameKey, voice.name);
+ result_voice->SetBoolean(constants::kRemoteKey, voice.remote);
if (!voice.lang.empty())
result_voice->SetString(constants::kLangKey, voice.lang);
if (voice.gender == TTS_GENDER_MALE)
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 7b8c45b..f1853dc 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_constants.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_api_constants.cc
@@ -6,48 +6,49 @@
namespace tts_extension_api_constants {
-const char kVoiceNameKey[] = "voiceName";
-const char kLangKey[] = "lang";
-const char kGenderKey[] = "gender";
-const char kRateKey[] = "rate";
-const char kPitchKey[] = "pitch";
-const char kVolumeKey[] = "volume";
+const char kCharIndexKey[] = "charIndex";
+const char kDesiredEventTypesKey[] = "desiredEventTypes";
const char kEnqueueKey[] = "enqueue";
+const char kErrorMessageKey[] = "errorMessage";
const char kEventTypeKey[] = "type";
const char kEventTypesKey[] = "eventTypes";
-const char kCharIndexKey[] = "charIndex";
-const char kErrorMessageKey[] = "errorMessage";
-const char kRequiredEventTypesKey[] = "requiredEventTypes";
-const char kDesiredEventTypesKey[] = "desiredEventTypes";
const char kExtensionIdKey[] = "extensionId";
-const char kSrcIdKey[] = "srcId";
+const char kGenderKey[] = "gender";
const char kIsFinalEventKey[] = "isFinalEvent";
+const char kLangKey[] = "lang";
const char kOnEventKey[] = "onEvent";
+const char kPitchKey[] = "pitch";
+const char kRateKey[] = "rate";
+const char kRemoteKey[] = "remote";
+const char kRequiredEventTypesKey[] = "requiredEventTypes";
+const char kSrcIdKey[] = "srcId";
+const char kVoiceNameKey[] = "voiceName";
+const char kVolumeKey[] = "volume";
const char kGenderFemale[] = "female";
const char kGenderMale[] = "male";
-const char kEventTypeStart[] = "start";
-const char kEventTypeEnd[] = "end";
-const char kEventTypeWord[] = "word";
-const char kEventTypeSentence[] = "sentence";
-const char kEventTypeMarker[] = "marker";
-const char kEventTypeInterrupted[] = "interrupted";
const char kEventTypeCancelled[] = "cancelled";
+const char kEventTypeEnd[] = "end";
const char kEventTypeError[] = "error";
+const char kEventTypeInterrupted[] = "interrupted";
+const char kEventTypeMarker[] = "marker";
const char kEventTypePause[] = "pause";
const char kEventTypeResume[] = "resume";
+const char kEventTypeSentence[] = "sentence";
+const char kEventTypeStart[] = "start";
+const char kEventTypeWord[] = "word";
-const char kErrorUndeclaredEventType[] =
- "Cannot send an event type that is not declared in the extension manifest.";
-const char kErrorUtteranceTooLong[] = "Utterance length is too long.";
-const char kErrorInvalidLang[] = "Invalid lang.";
const char kErrorInvalidGender[] = "Invalid gender.";
-const char kErrorInvalidRate[] = "Invalid rate.";
+const char kErrorInvalidLang[] = "Invalid lang.";
const char kErrorInvalidPitch[] = "Invalid pitch.";
+const char kErrorInvalidRate[] = "Invalid rate.";
const char kErrorInvalidVolume[] = "Invalid volume.";
const char kErrorMissingPauseOrResume[] =
"A TTS engine extension should either listen for both onPause and onResume "
"events, or neither.";
+const char kErrorUndeclaredEventType[] =
+ "Cannot send an event type that is not declared in the extension manifest.";
+const char kErrorUtteranceTooLong[] = "Utterance length is too long.";
} // 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 0809473..bec1749 100644
--- a/chrome/browser/speech/extension_api/tts_extension_api_constants.h
+++ b/chrome/browser/speech/extension_api/tts_extension_api_constants.h
@@ -11,46 +11,47 @@
namespace tts_extension_api_constants {
-extern const char kVoiceNameKey[];
-extern const char kLangKey[];
-extern const char kGenderKey[];
-extern const char kRateKey[];
-extern const char kPitchKey[];
-extern const char kVolumeKey[];
+extern const char kCharIndexKey[];
+extern const char kDesiredEventTypesKey[];
extern const char kEnqueueKey[];
+extern const char kErrorMessageKey[];
extern const char kEventTypeKey[];
extern const char kEventTypesKey[];
-extern const char kCharIndexKey[];
-extern const char kErrorMessageKey[];
-extern const char kRequiredEventTypesKey[];
-extern const char kDesiredEventTypesKey[];
extern const char kExtensionIdKey[];
-extern const char kSrcIdKey[];
+extern const char kGenderKey[];
extern const char kIsFinalEventKey[];
+extern const char kLangKey[];
extern const char kOnEventKey[];
+extern const char kPitchKey[];
+extern const char kRateKey[];
+extern const char kRemoteKey[];
+extern const char kRequiredEventTypesKey[];
+extern const char kSrcIdKey[];
+extern const char kVoiceNameKey[];
+extern const char kVolumeKey[];
extern const char kGenderFemale[];
extern const char kGenderMale[];
-extern const char kEventTypeStart[];
-extern const char kEventTypeEnd[];
-extern const char kEventTypeWord[];
-extern const char kEventTypeSentence[];
-extern const char kEventTypeMarker[];
-extern const char kEventTypeInterrupted[];
extern const char kEventTypeCancelled[];
+extern const char kEventTypeEnd[];
extern const char kEventTypeError[];
+extern const char kEventTypeInterrupted[];
+extern const char kEventTypeMarker[];
extern const char kEventTypePause[];
extern const char kEventTypeResume[];
+extern const char kEventTypeSentence[];
+extern const char kEventTypeStart[];
+extern const char kEventTypeWord[];
-extern const char kErrorUndeclaredEventType[];
-extern const char kErrorUtteranceTooLong[];
-extern const char kErrorInvalidLang[];
extern const char kErrorInvalidGender[];
-extern const char kErrorInvalidRate[];
+extern const char kErrorInvalidLang[];
extern const char kErrorInvalidPitch[];
+extern const char kErrorInvalidRate[];
extern const char kErrorInvalidVolume[];
extern const char kErrorMissingPauseOrResume[];
+extern const char kErrorUndeclaredEventType[];
+extern const char kErrorUtteranceTooLong[];
} // 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 63cc83c..b9e7a9a 100644
--- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -6,7 +6,10 @@
#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/speech/extension_api/tts_extension_api.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/browser/speech/tts_platform.h"
@@ -32,6 +35,8 @@ namespace {
int g_saved_utterance_id;
}
+namespace extensions {
+
class MockTtsPlatformImpl : public TtsPlatformImpl {
public:
MockTtsPlatformImpl()
@@ -375,7 +380,16 @@ IN_PROC_BROWSER_TEST_F(TtsApiTest, LangMatching) {
ASSERT_TRUE(RunExtensionTest("tts_engine/lang_matching")) << message_;
}
+IN_PROC_BROWSER_TEST_F(TtsApiTest, NetworkSpeechEngine) {
+ ExtensionService* service = extensions::ExtensionSystem::Get(
+ profile())->extension_service();
+ service->component_loader()->AddNetworkSpeechSynthesisExtension();
+ ASSERT_TRUE(RunExtensionTest("tts_engine/network_speech_engine")) << message_;
+}
+
// http://crbug.com/122474
IN_PROC_BROWSER_TEST_F(TtsApiTest, EngineApi) {
ASSERT_TRUE(RunExtensionTest("tts_engine/engine_api")) << message_;
}
+
+} // namespace extensions
diff --git a/chrome/browser/speech/tts_controller.cc b/chrome/browser/speech/tts_controller.cc
index a0d376f..34f72a4 100644
--- a/chrome/browser/speech/tts_controller.cc
+++ b/chrome/browser/speech/tts_controller.cc
@@ -57,6 +57,7 @@ UtteranceContinuousParameters::UtteranceContinuousParameters()
VoiceData::VoiceData()
: gender(TTS_GENDER_NONE),
+ remote(false),
native(false) {}
VoiceData::~VoiceData() {}
@@ -419,4 +420,3 @@ void TtsController::RemoveVoicesChangedDelegate(
VoicesChangedDelegate* delegate) {
voices_changed_delegates_.erase(delegate);
}
-
diff --git a/chrome/browser/speech/tts_controller.h b/chrome/browser/speech/tts_controller.h
index 18cbcd9..6081518 100644
--- a/chrome/browser/speech/tts_controller.h
+++ b/chrome/browser/speech/tts_controller.h
@@ -66,6 +66,10 @@ struct VoiceData {
std::string extension_id;
std::set<TtsEventType> events;
+ // If true, the synthesis engine is a remote network resource.
+ // It may be higher latency and may incur bandwidth costs.
+ bool remote;
+
// If true, this is implemented by this platform's subclass of
// TtsPlatformImpl. If false, this is implemented by an extension.
bool native;
diff --git a/chrome/browser/speech/tts_message_filter.cc b/chrome/browser/speech/tts_message_filter.cc
index 714c51f..3c397cd 100644
--- a/chrome/browser/speech/tts_message_filter.cc
+++ b/chrome/browser/speech/tts_message_filter.cc
@@ -67,7 +67,7 @@ void TtsMessageFilter::OnInitializeVoiceList() {
out_voice.voice_uri = voices[i].name;
out_voice.name = voices[i].name;
out_voice.lang = voices[i].lang;
- out_voice.local_service = true;
+ out_voice.local_service = !voices[i].remote;
out_voice.is_default = (i == 0);
}
Send(new TtsMsg_SetVoiceList(out_voices));
diff --git a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc
index bed4937..e18c594 100644
--- a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc
+++ b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.cc
@@ -29,7 +29,8 @@ struct TtsVoices : public Extension::ManifestData {
} // namespace
-TtsVoice::TtsVoice() {}
+TtsVoice::TtsVoice() : remote(false) {}
+
TtsVoice::~TtsVoice() {}
// static
@@ -95,6 +96,13 @@ bool TtsEngineManifestHandler::Parse(Extension* extension, string16* error) {
return false;
}
}
+ if (one_tts_voice->HasKey(keys::kTtsVoicesRemote)) {
+ if (!one_tts_voice->GetBoolean(
+ keys::kTtsVoicesRemote, &voice_data.remote)) {
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesRemote);
+ return false;
+ }
+ }
if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) {
const base::ListValue* event_types_list;
if (!one_tts_voice->GetList(
diff --git a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h
index 71d8e05..13b194b 100644
--- a/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h
+++ b/chrome/common/extensions/api/speech/tts_engine_manifest_handler.h
@@ -21,6 +21,7 @@ struct TtsVoice {
std::string voice_name;
std::string lang;
std::string gender;
+ bool remote;
std::set<std::string> event_types;
static const std::vector<TtsVoice>* GetTtsVoices(const Extension* extension);
diff --git a/chrome/common/extensions/api/system_private.json b/chrome/common/extensions/api/system_private.json
index 7d95450..ea9ae38 100644
--- a/chrome/common/extensions/api/system_private.json
+++ b/chrome/common/extensions/api/system_private.json
@@ -86,6 +86,24 @@
]
}
]
+ },
+ {
+ "name": "getApiKey",
+ "type": "function",
+ "description": "Gets Chrome's API key to use for requests to Google services.",
+ "parameters": [
+ {
+ "type": "function",
+ "name": "callback",
+ "parameters": [
+ {
+ "name": "key",
+ "type": "string",
+ "description": "The API key."
+ }
+ ]
+ }
+ ]
}
],
"events": [
diff --git a/chrome/common/extensions/api/tts.json b/chrome/common/extensions/api/tts.json
index ae8771e..995968ca 100644
--- a/chrome/common/extensions/api/tts.json
+++ b/chrome/common/extensions/api/tts.json
@@ -62,6 +62,11 @@
"description": "This voice's gender.",
"enum": ["male", "female"]
},
+ "remote": {
+ "type": "boolean",
+ "optional": true,
+ "description": "If true, the synthesis engine is a remote network resource. It may be higher latency and may incur bandwidth costs."
+ },
"extensionId": {
"type": "string",
"optional": true,
diff --git a/chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/manifest.json b/chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/manifest.json
new file mode 100644
index 0000000..14fec9e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/manifest.json
@@ -0,0 +1,10 @@
+{
+ "name": "chrome.tts",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description": "browser test for chrome.tts API",
+ "background": {
+ "scripts": [ "test.js" ]
+ },
+ "permissions": ["tts"]
+}
diff --git a/chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/test.js b/chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/test.js
new file mode 100644
index 0000000..72b7e34
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/tts_engine/network_speech_engine/test.js
@@ -0,0 +1,17 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// browser_tests.exe --gtest_filter="TtsApiTest.*"
+
+chrome.test.runTests([
+ function testNetworkSpeechVoices() {
+ chrome.tts.getVoices(function(voices) {
+ chrome.test.assertTrue(voices.length >= 6);
+ for (var i = 0; i < voices.length; i++) {
+ chrome.test.assertEq(true, voices[i].remote);
+ }
+ chrome.test.succeed();
+ });
+ }
+]);
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 962ff47..b15db19 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -146,6 +146,7 @@ const char kTtsVoicesEventTypeWord[] = "word";
const char kTtsVoicesEventTypes[] = "event_types";
const char kTtsVoicesGender[] = "gender";
const char kTtsVoicesLang[] = "lang";
+const char kTtsVoicesRemote[] = "remote";
const char kTtsVoicesVoiceName[] = "voice_name";
const char kType[] = "type";
const char kUpdateURL[] = "update_url";
@@ -573,6 +574,8 @@ const char kInvalidTtsVoicesGender[] =
"Invalid value for 'tts_engine.voices[*].gender'.";
const char kInvalidTtsVoicesLang[] =
"Invalid value for 'tts_engine.voices[*].lang'.";
+const char kInvalidTtsVoicesRemote[] =
+ "Invalid value for 'tts_engine.voices[*].remote'.";
const char kInvalidTtsVoicesVoiceName[] =
"Invalid value for 'tts_engine.voices[*].voice_name'.";
const char kInvalidUpdateURL[] =
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index 9b3fe9b..f645477 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -154,6 +154,7 @@ extern const char kTtsVoicesEventTypeWord[];
extern const char kTtsVoicesEventTypes[];
extern const char kTtsVoicesGender[];
extern const char kTtsVoicesLang[];
+extern const char kTtsVoicesRemote[];
extern const char kTtsVoicesVoiceName[];
extern const char kType[];
extern const char kUpdateURL[];
@@ -398,6 +399,7 @@ extern const char kInvalidTtsVoices[];
extern const char kInvalidTtsVoicesEventTypes[];
extern const char kInvalidTtsVoicesGender[];
extern const char kInvalidTtsVoicesLang[];
+extern const char kInvalidTtsVoicesRemote[];
extern const char kInvalidTtsVoicesVoiceName[];
extern const char kInvalidUpdateURL[];
extern const char kInvalidURLPatternError[];