refactor: move text-to-speech out of chromium_src (#15024)

* chore: add tts patch and buildflag, makes tts work again

* chore: add tts patch and buildflag, makes tts work again

* fix: make things compile

* build: add relevant tts files for linux

* fix: update patch and patch description, should now compile on mac

* build: move chrome specific sources under chromium_src:chrome target

* build: enable_extensions again

We are depending on them, check `//electron/chromium_src:chrome` target
for more info.

* fix: update tts.patch to receive notifications about browser context destruction

* fix: extend browser process from chrome layer

The global state g_browser_process is shared between //chrome
and //electron.

* spec: add basic speech synthesis test

* spec: skip speech tests on ci

* build: fix compilation on windows
This commit is contained in:
Heilig Benedek 2018-10-11 15:52:12 +02:00 committed by Charles Kerr
parent 5788600c46
commit 95696c9456
39 changed files with 625 additions and 3139 deletions

View file

@ -8,7 +8,6 @@ import("//build/config/win/manifest.gni")
import("//pdf/features.gni") import("//pdf/features.gni")
import("//services/service_manager/public/service_manifest.gni") import("//services/service_manager/public/service_manifest.gni")
import("//third_party/ffmpeg/ffmpeg_options.gni") import("//third_party/ffmpeg/ffmpeg_options.gni")
import("//third_party/widevine/cdm/widevine.gni")
import("//tools/grit/grit_rule.gni") import("//tools/grit/grit_rule.gni")
import("//tools/grit/repack.gni") import("//tools/grit/repack.gni")
import("//tools/v8_context_snapshot/v8_context_snapshot.gni") import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
@ -295,15 +294,7 @@ static_library("electron_lib") {
sources = filenames.lib_sources sources = filenames.lib_sources
set_sources_assignment_filter(sources_assignment_filter) set_sources_assignment_filter(sources_assignment_filter)
sources += [ sources += [ "$target_gen_dir/atom_natives.h" ]
"$target_gen_dir/atom_natives.h",
"//extensions/browser/app_window/size_constraints.cc",
"//extensions/browser/app_window/size_constraints.h",
"//extensions/common/constants.cc",
"//extensions/common/constants.h",
"//extensions/common/url_pattern.cc",
"//extensions/common/url_pattern.h",
]
if (is_component_build) { if (is_component_build) {
defines += [ "NODE_SHARED_MODE" ] defines += [ "NODE_SHARED_MODE" ]
@ -440,16 +431,6 @@ static_library("electron_lib") {
if (enable_pepper_flash) { if (enable_pepper_flash) {
deps += [ "components/pepper_flash" ] deps += [ "components/pepper_flash" ]
} }
if (enable_widevine) {
sources += [
"//chrome/renderer/media/chrome_key_systems.cc",
"//chrome/renderer/media/chrome_key_systems.h",
"//chrome/renderer/media/chrome_key_systems_provider.cc",
"//chrome/renderer/media/chrome_key_systems_provider.h",
]
deps += [ "//components/cdm/renderer" ]
}
} }
electron_paks("packed_resources") { electron_paks("packed_resources") {

View file

@ -23,7 +23,6 @@
#include "atom/common/native_mate_converters/net_converter.h" #include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/native_mate_converters/network_converter.h" #include "atom/common/native_mate_converters/network_converter.h"
#include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/options_switches.h" #include "atom/common/options_switches.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/environment.h" #include "base/environment.h"
@ -54,6 +53,12 @@
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
// clang-format off
// This header should be declared at the end to avoid
// redefinition errors.
#include "atom/common/node_includes.h" // NOLINT(build/include_alpha)
// clang-format on
#if defined(OS_WIN) #if defined(OS_WIN)
#include "atom/browser/ui/win/jump_list.h" #include "atom/browser/ui/win/jump_list.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"

View file

@ -40,7 +40,6 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/printing/printing_message_filter.h" #include "chrome/browser/printing/printing_message_filter.h"
#include "chrome/browser/speech/tts_message_filter.h"
#include "components/net_log/chrome_net_log.h" #include "components/net_log/chrome_net_log.h"
#include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/browser_ppapi_host.h"
#include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/client_certificate_delegate.h"
@ -85,6 +84,10 @@
#include "atom/browser/fake_location_provider.h" #include "atom/browser/fake_location_provider.h"
#endif // BUILDFLAG(OVERRIDE_LOCATION_PROVIDER) #endif // BUILDFLAG(OVERRIDE_LOCATION_PROVIDER)
#if BUILDFLAG(ENABLE_TTS)
#include "chrome/browser/speech/tts_message_filter.h"
#endif // BUILDFLAG(ENABLE_TTS)
using content::BrowserThread; using content::BrowserThread;
namespace atom { namespace atom {
@ -207,7 +210,10 @@ void AtomBrowserClient::RenderProcessWillLaunch(
return; return;
host->AddFilter(new printing::PrintingMessageFilter(process_id)); host->AddFilter(new printing::PrintingMessageFilter(process_id));
host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext()));
#if BUILDFLAG(ENABLE_TTS)
host->AddFilter(new TtsMessageFilter(host->GetBrowserContext()));
#endif
ProcessPreferences prefs; ProcessPreferences prefs;
auto* web_preferences = auto* web_preferences =

View file

@ -32,6 +32,7 @@
#include "brightray/common/application_info.h" #include "brightray/common/application_info.h"
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/prefs/json_pref_store.h" #include "components/prefs/json_pref_store.h"
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
@ -119,6 +120,8 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
proxy_config_monitor_ = std::make_unique<ProxyConfigMonitor>(prefs_.get()); proxy_config_monitor_ = std::make_unique<ProxyConfigMonitor>(prefs_.get());
io_handle_ = new URLRequestContextGetter::Handle(weak_factory_.GetWeakPtr()); io_handle_ = new URLRequestContextGetter::Handle(weak_factory_.GetWeakPtr());
cookie_change_notifier_ = std::make_unique<CookieChangeNotifier>(this); cookie_change_notifier_ = std::make_unique<CookieChangeNotifier>(this);
BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this);
} }
AtomBrowserContext::~AtomBrowserContext() { AtomBrowserContext::~AtomBrowserContext() {
@ -126,6 +129,9 @@ AtomBrowserContext::~AtomBrowserContext() {
NotifyWillBeDestroyed(this); NotifyWillBeDestroyed(this);
ShutdownStoragePartitions(); ShutdownStoragePartitions();
io_handle_->ShutdownOnUIThread(); io_handle_->ShutdownOnUIThread();
// Notify any keyed services of browser context destruction.
BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
this);
} }
void AtomBrowserContext::InitPrefs() { void AtomBrowserContext::InitPrefs() {

View file

@ -19,7 +19,7 @@
#include "atom/common/node_bindings.h" #include "atom/common/node_bindings.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/icon_manager.h" #include "chrome/browser/icon_manager.h"
#include "chrome/browser/net/chrome_net_log_helper.h" #include "chrome/browser/net/chrome_net_log_helper.h"
#include "components/net_log/chrome_net_log.h" #include "components/net_log/chrome_net_log.h"
@ -68,7 +68,7 @@ AtomBrowserMainParts* AtomBrowserMainParts::self_ = nullptr;
AtomBrowserMainParts::AtomBrowserMainParts( AtomBrowserMainParts::AtomBrowserMainParts(
const content::MainFunctionParams& params) const content::MainFunctionParams& params)
: fake_browser_process_(new BrowserProcess), : fake_browser_process_(new BrowserProcessImpl),
browser_(new Browser), browser_(new Browser),
node_bindings_(NodeBindings::Create(NodeBindings::BROWSER)), node_bindings_(NodeBindings::Create(NodeBindings::BROWSER)),
atom_bindings_(new AtomBindings(uv_default_loop())), atom_bindings_(new AtomBindings(uv_default_loop())),

View file

@ -107,7 +107,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
#endif #endif
// A fake BrowserProcess object that used to feed the source code from chrome. // A fake BrowserProcess object that used to feed the source code from chrome.
std::unique_ptr<BrowserProcess> fake_browser_process_; std::unique_ptr<BrowserProcessImpl> fake_browser_process_;
// Pointer to exit code. // Pointer to exit code.
int* exit_code_ = nullptr; int* exit_code_ = nullptr;

View file

@ -30,6 +30,10 @@ bool IsViewApiEnabled() {
return BUILDFLAG(ENABLE_VIEW_API); return BUILDFLAG(ENABLE_VIEW_API);
} }
bool IsTtsEnabled() {
return BUILDFLAG(ENABLE_TTS);
}
void Initialize(v8::Local<v8::Object> exports, void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, v8::Local<v8::Context> context,
@ -41,6 +45,7 @@ void Initialize(v8::Local<v8::Object> exports,
dict.SetMethod("isFakeLocationProviderEnabled", dict.SetMethod("isFakeLocationProviderEnabled",
&IsFakeLocationProviderEnabled); &IsFakeLocationProviderEnabled);
dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled); dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled);
dict.SetMethod("isTtsEnabled", &IsTtsEnabled);
} }
} // namespace } // namespace

View file

@ -7,4 +7,3 @@
#include "atom/common/api/api_messages.h" #include "atom/common/api/api_messages.h"
#include "chrome/common/chrome_utility_printing_messages.h" #include "chrome/common/chrome_utility_printing_messages.h"
#include "chrome/common/print_messages.h" #include "chrome/common/print_messages.h"
#include "chrome/common/tts_messages.h"

View file

@ -20,7 +20,6 @@
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "chrome/renderer/printing/print_web_view_helper.h" #include "chrome/renderer/printing/print_web_view_helper.h"
#include "chrome/renderer/tts_dispatcher.h"
#include "content/public/common/content_constants.h" #include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
@ -51,6 +50,10 @@
#include "chrome/renderer/pepper/pepper_helper.h" #include "chrome/renderer/pepper/pepper_helper.h"
#endif // BUILDFLAG(ENABLE_PEPPER_FLASH) #endif // BUILDFLAG(ENABLE_PEPPER_FLASH)
#if BUILDFLAG(ENABLE_TTS)
#include "chrome/renderer/tts_dispatcher.h"
#endif // BUILDFLAG(ENABLE_TTS)
namespace atom { namespace atom {
namespace { namespace {
@ -204,7 +207,11 @@ void RendererClientBase::DidClearWindowObject(
std::unique_ptr<blink::WebSpeechSynthesizer> std::unique_ptr<blink::WebSpeechSynthesizer>
RendererClientBase::OverrideSpeechSynthesizer( RendererClientBase::OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client) { blink::WebSpeechSynthesizerClient* client) {
#if BUILDFLAG(ENABLE_TTS)
return std::make_unique<TtsDispatcher>(client); return std::make_unique<TtsDispatcher>(client);
#else
return nullptr;
#endif
} }
bool RendererClientBase::OverrideCreatePlugin( bool RendererClientBase::OverrideCreatePlugin(

View file

@ -6,7 +6,6 @@ v8_promise_internal_field_count = 1
v8_typed_array_max_size_in_heap = 0 v8_typed_array_max_size_in_heap = 0
enable_cdm_host_verification = false enable_cdm_host_verification = false
enable_extensions = false
proprietary_codecs = true proprietary_codecs = true
ffmpeg_branding = "Chrome" ffmpeg_branding = "Chrome"

View file

@ -15,6 +15,7 @@ buildflag_header("buildflags") {
"ENABLE_VIEW_API=$enable_view_api", "ENABLE_VIEW_API=$enable_view_api",
"ENABLE_PEPPER_FLASH=$enable_pepper_flash", "ENABLE_PEPPER_FLASH=$enable_pepper_flash",
"ENABLE_PDF_VIEWER=$enable_pdf_viewer", "ENABLE_PDF_VIEWER=$enable_pdf_viewer",
"ENABLE_TTS=$enable_tts",
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
] ]
} }

View file

@ -14,6 +14,8 @@ declare_args() {
enable_pdf_viewer = false enable_pdf_viewer = false
enable_tts = true
# Provide a fake location provider for mocking # Provide a fake location provider for mocking
# the geolocation responses. Disable it if you # the geolocation responses. Disable it if you
# need to test with chromium's location provider. # need to test with chromium's location provider.

View file

@ -3,12 +3,14 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//electron/buildflags/buildflags.gni") import("//electron/buildflags/buildflags.gni")
import("//third_party/widevine/cdm/widevine.gni")
# Builds some of the chrome sources that Electron depends # Builds some of the chrome sources that Electron depends on.
# on unconditionally. static_library("chrome") {
source_set("chrome") {
visibility = [ "//electron:electron_lib" ] visibility = [ "//electron:electron_lib" ]
sources = [ sources = [
"//chrome/browser/browser_process.cc",
"//chrome/browser/browser_process.h",
"//chrome/browser/extensions/global_shortcut_listener.cc", "//chrome/browser/extensions/global_shortcut_listener.cc",
"//chrome/browser/extensions/global_shortcut_listener.h", "//chrome/browser/extensions/global_shortcut_listener.h",
"//chrome/browser/extensions/global_shortcut_listener_mac.h", "//chrome/browser/extensions/global_shortcut_listener_mac.h",
@ -31,18 +33,15 @@ source_set("chrome") {
"//chrome/browser/net/proxy_service_factory.h", "//chrome/browser/net/proxy_service_factory.h",
"//chrome/browser/ssl/security_state_tab_helper.cc", "//chrome/browser/ssl/security_state_tab_helper.cc",
"//chrome/browser/ssl/security_state_tab_helper.h", "//chrome/browser/ssl/security_state_tab_helper.h",
"//chrome/common/chrome_constants.cc", "//extensions/browser/app_window/size_constraints.cc",
"//chrome/common/chrome_constants.h", "//extensions/browser/app_window/size_constraints.h",
"//chrome/common/chrome_switches.cc",
"//chrome/common/chrome_switches.h",
"//chrome/common/secure_origin_whitelist.cc",
"//chrome/common/secure_origin_whitelist.h",
] ]
public_deps = [ public_deps = [
"//content/public/browser", "//content/public/browser",
] ]
deps = [ deps = [
"//chrome/common:constants", "//chrome/common",
"//components/keyed_service/content",
"//components/proxy_config", "//components/proxy_config",
"//components/security_state/content", "//components/security_state/content",
] ]
@ -66,4 +65,38 @@ source_set("chrome") {
] ]
deps += [ "//ui/snapshot" ] deps += [ "//ui/snapshot" ]
} }
if (enable_tts) {
sources += [
"//chrome/browser/speech/tts_controller.h",
"//chrome/browser/speech/tts_controller_impl.cc",
"//chrome/browser/speech/tts_controller_impl.h",
"//chrome/browser/speech/tts_mac.mm",
"//chrome/browser/speech/tts_message_filter.cc",
"//chrome/browser/speech/tts_message_filter.h",
"//chrome/browser/speech/tts_platform.cc",
"//chrome/browser/speech/tts_platform.h",
"//chrome/browser/speech/tts_win.cc",
"//chrome/common/tts_messages.h",
"//chrome/common/tts_utterance_request.cc",
"//chrome/common/tts_utterance_request.h",
"//chrome/renderer/tts_dispatcher.cc",
"//chrome/renderer/tts_dispatcher.h",
]
if (is_linux) {
sources += [ "//chrome/browser/speech/tts_linux.cc" ]
deps += [ "//third_party/speech-dispatcher" ]
}
}
if (enable_widevine) {
sources += [
"//chrome/renderer/media/chrome_key_systems.cc",
"//chrome/renderer/media/chrome_key_systems.h",
"//chrome/renderer/media/chrome_key_systems_provider.cc",
"//chrome/renderer/media/chrome_key_systems_provider.h",
]
deps += [ "//components/cdm/renderer" ]
}
} }

View file

@ -1,31 +0,0 @@
// Copyright (c) 2012 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.
#include "chrome/browser/browser_process.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "ui/base/l10n/l10n_util.h"
BrowserProcess* g_browser_process = NULL;
BrowserProcess::BrowserProcess()
: print_job_manager_(new printing::PrintJobManager) {
g_browser_process = this;
}
BrowserProcess::~BrowserProcess() {
g_browser_process = NULL;
}
void BrowserProcess::SetApplicationLocale(const std::string& locale) {
locale_ = locale;
}
std::string BrowserProcess::GetApplicationLocale() {
return locale_;
}
printing::PrintJobManager* BrowserProcess::print_job_manager() {
return print_job_manager_.get();
}

View file

@ -1,43 +0,0 @@
// Copyright (c) 2012 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.
// This interface is for managing the global services of the application. Each
// service is lazily created when requested the first time. The service getters
// will return NULL if the service is not available, so callers must check for
// this condition.
#ifndef CHROME_BROWSER_BROWSER_PROCESS_H_
#define CHROME_BROWSER_BROWSER_PROCESS_H_
#include <memory>
#include <string>
#include "base/macros.h"
namespace printing {
class PrintJobManager;
}
// NOT THREAD SAFE, call only from the main thread.
// These functions shouldn't return NULL unless otherwise noted.
class BrowserProcess {
public:
BrowserProcess();
~BrowserProcess();
void SetApplicationLocale(const std::string& locale);
std::string GetApplicationLocale();
printing::PrintJobManager* print_job_manager();
private:
std::unique_ptr<printing::PrintJobManager> print_job_manager_;
std::string locale_;
DISALLOW_COPY_AND_ASSIGN(BrowserProcess);
};
extern BrowserProcess* g_browser_process;
#endif // CHROME_BROWSER_BROWSER_PROCESS_H_

View file

@ -0,0 +1,217 @@
// Copyright (c) 2012 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.
#include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "ui/base/l10n/l10n_util.h"
BrowserProcessImpl::BrowserProcessImpl()
: print_job_manager_(new printing::PrintJobManager) {
g_browser_process = this;
}
BrowserProcessImpl::~BrowserProcessImpl() {
g_browser_process = nullptr;
}
bool BrowserProcessImpl::IsShuttingDown() {
return false;
}
metrics_services_manager::MetricsServicesManager*
BrowserProcessImpl::GetMetricsServicesManager() {
return nullptr;
}
metrics::MetricsService* BrowserProcessImpl::metrics_service() {
return nullptr;
}
rappor::RapporServiceImpl* BrowserProcessImpl::rappor_service() {
return nullptr;
}
ProfileManager* BrowserProcessImpl::profile_manager() {
return nullptr;
}
PrefService* BrowserProcessImpl::local_state() {
return nullptr;
}
net::URLRequestContextGetter* BrowserProcessImpl::system_request_context() {
return nullptr;
}
scoped_refptr<network::SharedURLLoaderFactory>
BrowserProcessImpl::shared_url_loader_factory() {
return nullptr;
}
variations::VariationsService* BrowserProcessImpl::variations_service() {
return nullptr;
}
BrowserProcessPlatformPart* BrowserProcessImpl::platform_part() {
return nullptr;
}
extensions::EventRouterForwarder*
BrowserProcessImpl::extension_event_router_forwarder() {
return nullptr;
}
NotificationUIManager* BrowserProcessImpl::notification_ui_manager() {
return nullptr;
}
NotificationPlatformBridge* BrowserProcessImpl::notification_platform_bridge() {
return nullptr;
}
IOThread* BrowserProcessImpl::io_thread() {
return nullptr;
}
SystemNetworkContextManager*
BrowserProcessImpl::system_network_context_manager() {
return nullptr;
}
network::NetworkQualityTracker* BrowserProcessImpl::network_quality_tracker() {
return nullptr;
}
WatchDogThread* BrowserProcessImpl::watchdog_thread() {
return nullptr;
}
policy::ChromeBrowserPolicyConnector*
BrowserProcessImpl::browser_policy_connector() {
return nullptr;
}
policy::PolicyService* BrowserProcessImpl::policy_service() {
return nullptr;
}
IconManager* BrowserProcessImpl::icon_manager() {
return nullptr;
}
GpuModeManager* BrowserProcessImpl::gpu_mode_manager() {
return nullptr;
}
printing::PrintPreviewDialogController*
BrowserProcessImpl::print_preview_dialog_controller() {
return nullptr;
}
printing::BackgroundPrintingManager*
BrowserProcessImpl::background_printing_manager() {
return nullptr;
}
IntranetRedirectDetector* BrowserProcessImpl::intranet_redirect_detector() {
return nullptr;
}
DownloadStatusUpdater* BrowserProcessImpl::download_status_updater() {
return nullptr;
}
DownloadRequestLimiter* BrowserProcessImpl::download_request_limiter() {
return nullptr;
}
BackgroundModeManager* BrowserProcessImpl::background_mode_manager() {
return nullptr;
}
StatusTray* BrowserProcessImpl::status_tray() {
return nullptr;
}
safe_browsing::SafeBrowsingService*
BrowserProcessImpl::safe_browsing_service() {
return nullptr;
}
safe_browsing::ClientSideDetectionService*
BrowserProcessImpl::safe_browsing_detection_service() {
return nullptr;
}
subresource_filter::ContentRulesetService*
BrowserProcessImpl::subresource_filter_ruleset_service() {
return nullptr;
}
optimization_guide::OptimizationGuideService*
BrowserProcessImpl::optimization_guide_service() {
return nullptr;
}
net_log::ChromeNetLog* BrowserProcessImpl::net_log() {
return nullptr;
}
component_updater::ComponentUpdateService*
BrowserProcessImpl::component_updater() {
return nullptr;
}
component_updater::SupervisedUserWhitelistInstaller*
BrowserProcessImpl::supervised_user_whitelist_installer() {
return nullptr;
}
MediaFileSystemRegistry* BrowserProcessImpl::media_file_system_registry() {
return nullptr;
}
WebRtcLogUploader* BrowserProcessImpl::webrtc_log_uploader() {
return nullptr;
}
network_time::NetworkTimeTracker* BrowserProcessImpl::network_time_tracker() {
return nullptr;
}
gcm::GCMDriver* BrowserProcessImpl::gcm_driver() {
return nullptr;
}
resource_coordinator::TabManager* BrowserProcessImpl::GetTabManager() {
return nullptr;
}
shell_integration::DefaultWebClientState
BrowserProcessImpl::CachedDefaultWebClientState() {
return shell_integration::UNKNOWN_DEFAULT;
}
prefs::InProcessPrefServiceFactory* BrowserProcessImpl::pref_service_factory()
const {
return nullptr;
}
content::NetworkConnectionTracker*
BrowserProcessImpl::network_connection_tracker() {
return nullptr;
}
void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) {
locale_ = locale;
}
const std::string& BrowserProcessImpl::GetApplicationLocale() {
return locale_;
}
printing::PrintJobManager* BrowserProcessImpl::print_job_manager() {
return print_job_manager_.get();
}

View file

@ -0,0 +1,107 @@
// Copyright (c) 2012 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.
// This interface is for managing the global services of the application. Each
// service is lazily created when requested the first time. The service getters
// will return NULL if the service is not available, so callers must check for
// this condition.
#ifndef CHROME_BROWSER_BROWSER_PROCESS_IMPL_H_
#define CHROME_BROWSER_BROWSER_PROCESS_IMPL_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "chrome/browser/browser_process.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace printing {
class PrintJobManager;
}
// Empty definition for std::unique_ptr
class BackgroundModeManager {};
// NOT THREAD SAFE, call only from the main thread.
// These functions shouldn't return NULL unless otherwise noted.
class BrowserProcessImpl : public BrowserProcess {
public:
BrowserProcessImpl();
~BrowserProcessImpl() override;
void ResourceDispatcherHostCreated() override {}
void EndSession() override {}
void FlushLocalStateAndReply(base::OnceClosure reply) override {}
bool IsShuttingDown() override;
metrics_services_manager::MetricsServicesManager* GetMetricsServicesManager()
override;
metrics::MetricsService* metrics_service() override;
rappor::RapporServiceImpl* rappor_service() override;
ProfileManager* profile_manager() override;
PrefService* local_state() override;
net::URLRequestContextGetter* system_request_context() override;
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory()
override;
variations::VariationsService* variations_service() override;
BrowserProcessPlatformPart* platform_part() override;
extensions::EventRouterForwarder* extension_event_router_forwarder() override;
NotificationUIManager* notification_ui_manager() override;
NotificationPlatformBridge* notification_platform_bridge() override;
IOThread* io_thread() override;
SystemNetworkContextManager* system_network_context_manager() override;
network::NetworkQualityTracker* network_quality_tracker() override;
WatchDogThread* watchdog_thread() override;
policy::ChromeBrowserPolicyConnector* browser_policy_connector() override;
policy::PolicyService* policy_service() override;
IconManager* icon_manager() override;
GpuModeManager* gpu_mode_manager() override;
printing::PrintPreviewDialogController* print_preview_dialog_controller()
override;
printing::BackgroundPrintingManager* background_printing_manager() override;
IntranetRedirectDetector* intranet_redirect_detector() override;
DownloadStatusUpdater* download_status_updater() override;
DownloadRequestLimiter* download_request_limiter() override;
BackgroundModeManager* background_mode_manager() override;
StatusTray* status_tray() override;
safe_browsing::SafeBrowsingService* safe_browsing_service() override;
safe_browsing::ClientSideDetectionService* safe_browsing_detection_service()
override;
subresource_filter::ContentRulesetService*
subresource_filter_ruleset_service() override;
optimization_guide::OptimizationGuideService* optimization_guide_service()
override;
net_log::ChromeNetLog* net_log() override;
component_updater::ComponentUpdateService* component_updater() override;
component_updater::SupervisedUserWhitelistInstaller*
supervised_user_whitelist_installer() override;
MediaFileSystemRegistry* media_file_system_registry() override;
WebRtcLogUploader* webrtc_log_uploader() override;
network_time::NetworkTimeTracker* network_time_tracker() override;
gcm::GCMDriver* gcm_driver() override;
resource_coordinator::TabManager* GetTabManager() override;
shell_integration::DefaultWebClientState CachedDefaultWebClientState()
override;
prefs::InProcessPrefServiceFactory* pref_service_factory() const override;
content::NetworkConnectionTracker* network_connection_tracker() override;
void CreateDevToolsProtocolHandler() override {}
void CreateDevToolsAutoOpener() override {}
void set_background_mode_manager_for_test(
std::unique_ptr<BackgroundModeManager> manager) override {}
#if (defined(OS_WIN) || defined(OS_LINUX))
void StartAutoupdateTimer() override {}
#endif
void SetApplicationLocale(const std::string& locale) override;
const std::string& GetApplicationLocale() override;
printing::PrintJobManager* print_job_manager() override;
private:
std::unique_ptr<printing::PrintJobManager> print_job_manager_;
std::string locale_;
DISALLOW_COPY_AND_ASSIGN(BrowserProcessImpl);
};
#endif // CHROME_BROWSER_BROWSER_PROCESS_IMPL_H_

View file

@ -1,336 +0,0 @@
// Copyright (c) 2012 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.
#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
#include <memory>
#include <queue>
#include <set>
#include <string>
#include <vector>
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "url/gurl.h"
class Utterance;
class TtsPlatformImpl;
namespace base {
class Value;
}
namespace content {
class BrowserContext;
}
// Events sent back from the TTS engine indicating the progress.
enum TtsEventType {
TTS_EVENT_START,
TTS_EVENT_END,
TTS_EVENT_WORD,
TTS_EVENT_SENTENCE,
TTS_EVENT_MARKER,
TTS_EVENT_INTERRUPTED,
TTS_EVENT_CANCELLED,
TTS_EVENT_ERROR,
TTS_EVENT_PAUSE,
TTS_EVENT_RESUME
};
enum TtsGenderType { TTS_GENDER_NONE, TTS_GENDER_MALE, TTS_GENDER_FEMALE };
// Returns true if this event type is one that indicates an utterance
// is finished and can be destroyed.
bool IsFinalTtsEventType(TtsEventType event_type);
// The continuous parameters that apply to a given utterance.
struct UtteranceContinuousParameters {
UtteranceContinuousParameters();
double rate;
double pitch;
double volume;
};
// Information about one voice.
struct VoiceData {
VoiceData();
VoiceData(const VoiceData&);
~VoiceData();
std::string name;
std::string lang;
TtsGenderType gender;
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;
std::string native_voice_identifier;
};
// Interface that delegates TTS requests to user-installed extensions.
class TtsEngineDelegate {
public:
virtual ~TtsEngineDelegate() {}
// Return a list of all available voices registered.
virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) = 0;
// Speak the given utterance by sending an event to the given TTS engine.
virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0;
// Stop speaking the given utterance by sending an event to the target
// associated with this utterance.
virtual void Stop(Utterance* utterance) = 0;
// Pause in the middle of speaking this utterance.
virtual void Pause(Utterance* utterance) = 0;
// Resume speaking this utterance.
virtual void Resume(Utterance* utterance) = 0;
// Load the built-in component extension for ChromeOS.
virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) = 0;
};
// Class that wants to receive events on utterances.
class UtteranceEventDelegate {
public:
virtual ~UtteranceEventDelegate() {}
virtual void OnTtsEvent(Utterance* utterance,
TtsEventType event_type,
int char_index,
const std::string& error_message) = 0;
};
// Class that wants to be notified when the set of
// voices has changed.
class VoicesChangedDelegate {
public:
virtual ~VoicesChangedDelegate() {}
virtual void OnVoicesChanged() = 0;
};
// One speech utterance.
class Utterance {
public:
// Construct an utterance given a profile and a completion task to call
// when the utterance is done speaking. Before speaking this utterance,
// its other parameters like text, rate, pitch, etc. should all be set.
explicit Utterance(content::BrowserContext* browser_context);
~Utterance();
// Sends an event to the delegate. If the event type is TTS_EVENT_END
// or TTS_EVENT_ERROR, deletes the utterance. If |char_index| is -1,
// uses the last good value.
void OnTtsEvent(TtsEventType event_type,
int char_index,
const std::string& error_message);
// Finish an utterance without sending an event to the delegate.
void Finish();
// Getters and setters for the text to speak and other speech options.
void set_text(const std::string& text) { text_ = text; }
const std::string& text() const { return text_; }
void set_options(const base::Value* options);
const base::Value* options() const { return options_.get(); }
void set_src_extension_id(const std::string& src_extension_id) {
src_extension_id_ = src_extension_id;
}
const std::string& src_extension_id() { return src_extension_id_; }
void set_src_id(int src_id) { src_id_ = src_id; }
int src_id() { return src_id_; }
void set_src_url(const GURL& src_url) { src_url_ = src_url; }
const GURL& src_url() { return src_url_; }
void set_voice_name(const std::string& voice_name) {
voice_name_ = voice_name;
}
const std::string& voice_name() const { return voice_name_; }
void set_lang(const std::string& lang) { lang_ = lang; }
const std::string& lang() const { return lang_; }
void set_gender(TtsGenderType gender) { gender_ = gender; }
TtsGenderType gender() const { return gender_; }
void set_continuous_parameters(const UtteranceContinuousParameters& params) {
continuous_parameters_ = params;
}
const UtteranceContinuousParameters& continuous_parameters() {
return continuous_parameters_;
}
void set_can_enqueue(bool can_enqueue) { can_enqueue_ = can_enqueue; }
bool can_enqueue() const { return can_enqueue_; }
void set_required_event_types(const std::set<TtsEventType>& types) {
required_event_types_ = types;
}
const std::set<TtsEventType>& required_event_types() const {
return required_event_types_;
}
void set_desired_event_types(const std::set<TtsEventType>& types) {
desired_event_types_ = types;
}
const std::set<TtsEventType>& desired_event_types() const {
return desired_event_types_;
}
const std::string& extension_id() const { return extension_id_; }
void set_extension_id(const std::string& extension_id) {
extension_id_ = extension_id;
}
UtteranceEventDelegate* event_delegate() const {
return event_delegate_.get();
}
void set_event_delegate(
base::WeakPtr<UtteranceEventDelegate> event_delegate) {
event_delegate_ = event_delegate;
}
// Getters and setters for internal state.
content::BrowserContext* browser_context() const { return browser_context_; }
int id() const { return id_; }
bool finished() const { return finished_; }
private:
// The BrowserContext that initiated this utterance.
content::BrowserContext* browser_context_;
// The extension ID of the extension providing TTS for this utterance, or
// empty if native TTS is being used.
std::string extension_id_;
// The unique ID of this utterance, used to associate callback functions
// with utterances.
int id_;
// The id of the next utterance, so we can associate requests with
// responses.
static int next_utterance_id_;
// The text to speak.
std::string text_;
// The full options arg passed to tts.speak, which may include fields
// other than the ones we explicitly parse, below.
std::unique_ptr<base::Value> options_;
// The extension ID of the extension that called speak() and should
// receive events.
std::string src_extension_id_;
// The source extension's ID of this utterance, so that it can associate
// events with the appropriate callback.
int src_id_;
// The URL of the page where the source extension called speak.
GURL src_url_;
// The delegate to be called when an utterance event is fired.
base::WeakPtr<UtteranceEventDelegate> event_delegate_;
// The parsed options.
std::string voice_name_;
std::string lang_;
TtsGenderType gender_;
UtteranceContinuousParameters continuous_parameters_;
bool can_enqueue_;
std::set<TtsEventType> required_event_types_;
std::set<TtsEventType> desired_event_types_;
// The index of the current char being spoken.
int char_index_;
// True if this utterance received an event indicating it's done.
bool finished_;
};
// Singleton class that manages text-to-speech for the TTS and TTS engine
// extension APIs, maintaining a queue of pending utterances and keeping
// track of all state.
class TtsController {
public:
// Get the single instance of this class.
static TtsController* GetInstance();
// Returns true if we're currently speaking an utterance.
virtual bool IsSpeaking() = 0;
// Speak the given utterance. If the utterance's can_enqueue flag is true
// and another utterance is in progress, adds it to the end of the queue.
// Otherwise, interrupts any current utterance and speaks this one
// immediately.
virtual void SpeakOrEnqueue(Utterance* utterance) = 0;
// Stop all utterances and flush the queue. Implies leaving pause mode
// as well.
virtual void Stop() = 0;
// Pause the speech queue. Some engines may support pausing in the middle
// of an utterance.
virtual void Pause() = 0;
// Resume speaking.
virtual void Resume() = 0;
// Handle events received from the speech engine. Events are forwarded to
// the callback function, and in addition, completion and error events
// trigger finishing the current utterance and starting the next one, if
// any.
virtual void OnTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
const std::string& error_message) = 0;
// Return a list of all available voices, including the native voice,
// if supported, and all voices registered by extensions.
virtual void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) = 0;
// Called by the extension system or platform implementation when the
// list of voices may have changed and should be re-queried.
virtual void VoicesChanged() = 0;
// Add a delegate that wants to be notified when the set of voices changes.
virtual void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0;
// Remove delegate that wants to be notified when the set of voices changes.
virtual void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0;
// Set the delegate that processes TTS requests with user-installed
// extensions.
virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) = 0;
// Get the delegate that processes TTS requests with user-installed
// extensions.
virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0;
// For unit testing.
virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) = 0;
virtual int QueueSize() = 0;
protected:
virtual ~TtsController() {}
};
#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_

View file

@ -1,447 +0,0 @@
// Copyright 2014 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.
#include "chrome/browser/speech/tts_controller_impl.h"
#include <string>
#include <vector>
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/speech/tts_platform.h"
namespace {
// A value to be used to indicate that there is no char index available.
const int kInvalidCharIndex = -1;
// Given a language/region code of the form 'fr-FR', returns just the basic
// language portion, e.g. 'fr'.
std::string TrimLanguageCode(std::string lang) {
if (lang.size() >= 5 && lang[2] == '-')
return lang.substr(0, 2);
else
return lang;
}
} // namespace
bool IsFinalTtsEventType(TtsEventType event_type) {
return (event_type == TTS_EVENT_END || event_type == TTS_EVENT_INTERRUPTED ||
event_type == TTS_EVENT_CANCELLED || event_type == TTS_EVENT_ERROR);
}
//
// UtteranceContinuousParameters
//
UtteranceContinuousParameters::UtteranceContinuousParameters()
: rate(-1), pitch(-1), volume(-1) {}
//
// VoiceData
//
VoiceData::VoiceData()
: gender(TTS_GENDER_NONE), remote(false), native(false) {}
VoiceData::VoiceData(const VoiceData&) = default;
VoiceData::~VoiceData() = default;
//
// Utterance
//
// static
int Utterance::next_utterance_id_ = 0;
Utterance::Utterance(content::BrowserContext* browser_context)
: browser_context_(browser_context),
id_(next_utterance_id_++),
src_id_(-1),
gender_(TTS_GENDER_NONE),
can_enqueue_(false),
char_index_(0),
finished_(false) {
options_.reset(new base::DictionaryValue());
}
Utterance::~Utterance() {
DCHECK(finished_);
}
void Utterance::OnTtsEvent(TtsEventType event_type,
int char_index,
const std::string& error_message) {
if (char_index >= 0)
char_index_ = char_index;
if (IsFinalTtsEventType(event_type))
finished_ = true;
if (event_delegate_)
event_delegate_->OnTtsEvent(this, event_type, char_index, error_message);
if (finished_)
event_delegate_.reset();
}
void Utterance::Finish() {
finished_ = true;
}
void Utterance::set_options(const base::Value* options) {
options_.reset(options->DeepCopy());
}
TtsController* TtsController::GetInstance() {
return TtsControllerImpl::GetInstance();
}
//
// TtsControllerImpl
//
// static
TtsControllerImpl* TtsControllerImpl::GetInstance() {
return base::Singleton<TtsControllerImpl>::get();
}
TtsControllerImpl::TtsControllerImpl()
: current_utterance_(NULL),
paused_(false),
platform_impl_(NULL),
tts_engine_delegate_(NULL) {}
TtsControllerImpl::~TtsControllerImpl() {
if (current_utterance_) {
current_utterance_->Finish();
delete current_utterance_;
}
// Clear any queued utterances too.
ClearUtteranceQueue(false); // Don't sent events.
}
void TtsControllerImpl::SpeakOrEnqueue(Utterance* utterance) {
// If we're paused and we get an utterance that can't be queued,
// flush the queue but stay in the paused state.
if (paused_ && !utterance->can_enqueue()) {
Stop();
paused_ = true;
delete utterance;
return;
}
if (paused_ || (IsSpeaking() && utterance->can_enqueue())) {
utterance_queue_.push(utterance);
} else {
Stop();
SpeakNow(utterance);
}
}
void TtsControllerImpl::SpeakNow(Utterance* utterance) {
// Ensure we have all built-in voices loaded. This is a no-op if already
// loaded.
bool loaded_built_in =
GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context());
// Get all available voices and try to find a matching voice.
std::vector<VoiceData> voices;
GetVoices(utterance->browser_context(), &voices);
int index = GetMatchingVoice(utterance, voices);
VoiceData voice;
if (index != -1) {
// Select the matching voice.
voice = voices[index];
} else {
// However, if no match was found on a platform without native tts voices,
// attempt to get a voice based only on the current locale without respect
// to any supplied voice names.
std::vector<VoiceData> native_voices;
if (GetPlatformImpl()->PlatformImplAvailable())
GetPlatformImpl()->GetVoices(&native_voices);
if (native_voices.empty() && !voices.empty()) {
// TODO(dtseng): Notify extension caller of an error.
utterance->set_voice_name("");
// TODO(gaochun): Replace the global variable g_browser_process with
// GetContentClient()->browser() to eliminate the dependency of browser
// once TTS implementation was moved to content.
utterance->set_lang(g_browser_process->GetApplicationLocale());
index = GetMatchingVoice(utterance, voices);
// If even that fails, just take the first available voice.
if (index == -1)
index = 0;
voice = voices[index];
} else {
// Otherwise, simply give native voices a chance to handle this utterance.
voice.native = true;
}
}
GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice);
if (!voice.native) {
#if !defined(OS_ANDROID)
DCHECK(!voice.extension_id.empty());
current_utterance_ = utterance;
utterance->set_extension_id(voice.extension_id);
if (tts_engine_delegate_)
tts_engine_delegate_->Speak(utterance, voice);
bool sends_end_event =
voice.events.find(TTS_EVENT_END) != voice.events.end();
if (!sends_end_event) {
utterance->Finish();
delete utterance;
current_utterance_ = NULL;
SpeakNextUtterance();
}
#endif
} else {
// It's possible for certain platforms to send start events immediately
// during |speak|.
current_utterance_ = utterance;
GetPlatformImpl()->clear_error();
bool success = GetPlatformImpl()->Speak(utterance->id(), utterance->text(),
utterance->lang(), voice,
utterance->continuous_parameters());
if (!success)
current_utterance_ = NULL;
// If the native voice wasn't able to process this speech, see if
// the browser has built-in TTS that isn't loaded yet.
if (!success && loaded_built_in) {
utterance_queue_.push(utterance);
return;
}
if (!success) {
utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex,
GetPlatformImpl()->error());
delete utterance;
return;
}
}
}
void TtsControllerImpl::Stop() {
paused_ = false;
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
#if !defined(OS_ANDROID)
if (tts_engine_delegate_)
tts_engine_delegate_->Stop(current_utterance_);
#endif
} else {
GetPlatformImpl()->clear_error();
GetPlatformImpl()->StopSpeaking();
}
if (current_utterance_)
current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
std::string());
FinishCurrentUtterance();
ClearUtteranceQueue(true); // Send events.
}
void TtsControllerImpl::Pause() {
paused_ = true;
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
#if !defined(OS_ANDROID)
if (tts_engine_delegate_)
tts_engine_delegate_->Pause(current_utterance_);
#endif
} else if (current_utterance_) {
GetPlatformImpl()->clear_error();
GetPlatformImpl()->Pause();
}
}
void TtsControllerImpl::Resume() {
paused_ = false;
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
#if !defined(OS_ANDROID)
if (tts_engine_delegate_)
tts_engine_delegate_->Resume(current_utterance_);
#endif
} else if (current_utterance_) {
GetPlatformImpl()->clear_error();
GetPlatformImpl()->Resume();
} else {
SpeakNextUtterance();
}
}
void TtsControllerImpl::OnTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
const std::string& error_message) {
// We may sometimes receive completion callbacks "late", after we've
// already finished the utterance (for example because another utterance
// interrupted or we got a call to Stop). This is normal and we can
// safely just ignore these events.
if (!current_utterance_ || utterance_id != current_utterance_->id()) {
return;
}
current_utterance_->OnTtsEvent(event_type, char_index, error_message);
if (current_utterance_->finished()) {
FinishCurrentUtterance();
SpeakNextUtterance();
}
}
void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) {
#if !defined(OS_ANDROID)
if (browser_context && tts_engine_delegate_)
tts_engine_delegate_->GetVoices(browser_context, out_voices);
#endif
TtsPlatformImpl* platform_impl = GetPlatformImpl();
if (platform_impl) {
// Ensure we have all built-in voices loaded. This is a no-op if already
// loaded.
platform_impl->LoadBuiltInTtsExtension(browser_context);
if (platform_impl->PlatformImplAvailable())
platform_impl->GetVoices(out_voices);
}
}
bool TtsControllerImpl::IsSpeaking() {
return current_utterance_ != NULL || GetPlatformImpl()->IsSpeaking();
}
void TtsControllerImpl::FinishCurrentUtterance() {
if (current_utterance_) {
if (!current_utterance_->finished())
current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
std::string());
delete current_utterance_;
current_utterance_ = NULL;
}
}
void TtsControllerImpl::SpeakNextUtterance() {
if (paused_)
return;
// Start speaking the next utterance in the queue. Keep trying in case
// one fails but there are still more in the queue to try.
while (!utterance_queue_.empty() && !current_utterance_) {
Utterance* utterance = utterance_queue_.front();
utterance_queue_.pop();
SpeakNow(utterance);
}
}
void TtsControllerImpl::ClearUtteranceQueue(bool send_events) {
while (!utterance_queue_.empty()) {
Utterance* utterance = utterance_queue_.front();
utterance_queue_.pop();
if (send_events)
utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex,
std::string());
else
utterance->Finish();
delete utterance;
}
}
void TtsControllerImpl::SetPlatformImpl(TtsPlatformImpl* platform_impl) {
platform_impl_ = platform_impl;
}
int TtsControllerImpl::QueueSize() {
return static_cast<int>(utterance_queue_.size());
}
TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() {
if (!platform_impl_)
platform_impl_ = TtsPlatformImpl::GetInstance();
return platform_impl_;
}
int TtsControllerImpl::GetMatchingVoice(const Utterance* utterance,
std::vector<VoiceData>& voices) {
// Make two passes: the first time, do strict language matching
// ('fr-FR' does not match 'fr-CA'). The second time, do prefix
// language matching ('fr-FR' matches 'fr' and 'fr-CA')
for (int pass = 0; pass < 2; ++pass) {
for (size_t i = 0; i < voices.size(); ++i) {
const VoiceData& voice = voices[i];
if (!utterance->extension_id().empty() &&
utterance->extension_id() != voice.extension_id) {
continue;
}
if (!voice.name.empty() && !utterance->voice_name().empty() &&
voice.name != utterance->voice_name()) {
continue;
}
if (!voice.lang.empty() && !utterance->lang().empty()) {
std::string voice_lang = voice.lang;
std::string utterance_lang = utterance->lang();
if (pass == 1) {
voice_lang = TrimLanguageCode(voice_lang);
utterance_lang = TrimLanguageCode(utterance_lang);
}
if (voice_lang != utterance_lang) {
continue;
}
}
if (voice.gender != TTS_GENDER_NONE &&
utterance->gender() != TTS_GENDER_NONE &&
voice.gender != utterance->gender()) {
continue;
}
if (utterance->required_event_types().size() > 0) {
bool has_all_required_event_types = true;
for (std::set<TtsEventType>::const_iterator iter =
utterance->required_event_types().begin();
iter != utterance->required_event_types().end(); ++iter) {
if (voice.events.find(*iter) == voice.events.end()) {
has_all_required_event_types = false;
break;
}
}
if (!has_all_required_event_types)
continue;
}
return static_cast<int>(i);
}
}
return -1;
}
void TtsControllerImpl::VoicesChanged() {
for (std::set<VoicesChangedDelegate*>::iterator iter =
voices_changed_delegates_.begin();
iter != voices_changed_delegates_.end(); ++iter) {
(*iter)->OnVoicesChanged();
}
}
void TtsControllerImpl::AddVoicesChangedDelegate(
VoicesChangedDelegate* delegate) {
voices_changed_delegates_.insert(delegate);
}
void TtsControllerImpl::RemoveVoicesChangedDelegate(
VoicesChangedDelegate* delegate) {
voices_changed_delegates_.erase(delegate);
}
void TtsControllerImpl::SetTtsEngineDelegate(TtsEngineDelegate* delegate) {
tts_engine_delegate_ = delegate;
}
TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() {
return tts_engine_delegate_;
}

View file

@ -1,102 +0,0 @@
// Copyright 2014 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.
#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
#include <memory>
#include <queue>
#include <set>
#include <string>
#include <vector>
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/speech/tts_controller.h"
#include "url/gurl.h"
namespace content {
class BrowserContext;
}
// Singleton class that manages text-to-speech for the TTS and TTS engine
// extension APIs, maintaining a queue of pending utterances and keeping
// track of all state.
class TtsControllerImpl : public TtsController {
public:
// Get the single instance of this class.
static TtsControllerImpl* GetInstance();
// TtsController methods
bool IsSpeaking() override;
void SpeakOrEnqueue(Utterance* utterance) override;
void Stop() override;
void Pause() override;
void Resume() override;
void OnTtsEvent(int utterance_id,
TtsEventType event_type,
int char_index,
const std::string& error_message) override;
void GetVoices(content::BrowserContext* browser_context,
std::vector<VoiceData>* out_voices) override;
void VoicesChanged() override;
void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override;
TtsEngineDelegate* GetTtsEngineDelegate() override;
void SetPlatformImpl(TtsPlatformImpl* platform_impl) override;
int QueueSize() override;
protected:
TtsControllerImpl();
~TtsControllerImpl() override;
private:
// Get the platform TTS implementation (or injected mock).
TtsPlatformImpl* GetPlatformImpl();
// Start speaking the given utterance. Will either take ownership of
// |utterance| or delete it if there's an error. Returns true on success.
void SpeakNow(Utterance* utterance);
// Clear the utterance queue. If send_events is true, will send
// TTS_EVENT_CANCELLED events on each one.
void ClearUtteranceQueue(bool send_events);
// Finalize and delete the current utterance.
void FinishCurrentUtterance();
// Start speaking the next utterance in the queue.
void SpeakNextUtterance();
// Given an utterance and a vector of voices, return the
// index of the voice that best matches the utterance.
int GetMatchingVoice(const Utterance* utterance,
std::vector<VoiceData>& voices);
friend struct base::DefaultSingletonTraits<TtsControllerImpl>;
// The current utterance being spoken.
Utterance* current_utterance_;
// Whether the queue is paused or not.
bool paused_;
// A queue of utterances to speak after the current one finishes.
std::queue<Utterance*> utterance_queue_;
// A set of delegates that want to be notified when the voices change.
std::set<VoicesChangedDelegate*> voices_changed_delegates_;
// A pointer to the platform implementation of text-to-speech, for
// dependency injection.
TtsPlatformImpl* platform_impl_;
// The delegate that processes TTS requests with user-installed extensions.
TtsEngineDelegate* tts_engine_delegate_;
DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl);
};
#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_

View file

@ -1,349 +0,0 @@
// Copyright (c) 2012 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.
#include <math.h>
#include <map>
#include <memory>
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/speech/tts_platform.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "library_loaders/libspeechd.h"
using content::BrowserThread;
namespace {
const char kNotSupportedError[] =
"Native speech synthesis not supported on this platform.";
struct SPDChromeVoice {
std::string name;
std::string module;
};
} // namespace
class TtsPlatformImplLinux : public TtsPlatformImpl {
public:
bool PlatformImplAvailable() override;
bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) override;
bool StopSpeaking() override;
void Pause() override;
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
void OnSpeechEvent(SPDNotificationType type);
// Get the single instance of this class.
static TtsPlatformImplLinux* GetInstance();
private:
TtsPlatformImplLinux();
~TtsPlatformImplLinux() override;
// Initiate the connection with the speech dispatcher.
void Initialize();
// Resets the connection with speech dispatcher.
void Reset();
static void NotificationCallback(size_t msg_id,
size_t client_id,
SPDNotificationType type);
static void IndexMarkCallback(size_t msg_id,
size_t client_id,
SPDNotificationType state,
char* index_mark);
static SPDNotificationType current_notification_;
base::Lock initialization_lock_;
LibSpeechdLoader libspeechd_loader_;
SPDConnection* conn_;
// These apply to the current utterance only.
std::string utterance_;
int utterance_id_;
// Map a string composed of a voicename and module to the voicename. Used to
// uniquely identify a voice across all available modules.
std::unique_ptr<std::map<std::string, SPDChromeVoice>> all_native_voices_;
friend struct base::DefaultSingletonTraits<TtsPlatformImplLinux>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
};
// static
SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END;
TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
return;
base::PostTaskWithTraits(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
base::Bind(&TtsPlatformImplLinux::Initialize, base::Unretained(this)));
}
void TtsPlatformImplLinux::Initialize() {
base::AutoLock lock(initialization_lock_);
if (!libspeechd_loader_.Load("libspeechd.so.2"))
return;
{
// spd_open has memory leaks which are hard to suppress.
// http://crbug.com/317360
ANNOTATE_SCOPED_MEMORY_LEAK;
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
SPD_MODE_THREADED);
}
if (!conn_)
return;
// Register callbacks for all events.
conn_->callback_begin = conn_->callback_end = conn_->callback_cancel =
conn_->callback_pause = conn_->callback_resume = &NotificationCallback;
conn_->callback_im = &IndexMarkCallback;
libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_END);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE);
libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
}
TtsPlatformImplLinux::~TtsPlatformImplLinux() {
base::AutoLock lock(initialization_lock_);
if (conn_) {
libspeechd_loader_.spd_close(conn_);
conn_ = NULL;
}
}
void TtsPlatformImplLinux::Reset() {
base::AutoLock lock(initialization_lock_);
if (conn_)
libspeechd_loader_.spd_close(conn_);
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
SPD_MODE_THREADED);
}
bool TtsPlatformImplLinux::PlatformImplAvailable() {
if (!initialization_lock_.Try())
return false;
bool result = libspeechd_loader_.loaded() && (conn_ != NULL);
initialization_lock_.Release();
return result;
}
bool TtsPlatformImplLinux::Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
if (!PlatformImplAvailable()) {
error_ = kNotSupportedError;
return false;
}
// Speech dispatcher's speech params are around 3x at either limit.
float rate = params.rate > 3 ? 3 : params.rate;
rate = params.rate < 0.334 ? 0.334 : rate;
float pitch = params.pitch > 3 ? 3 : params.pitch;
pitch = params.pitch < 0.334 ? 0.334 : pitch;
std::map<std::string, SPDChromeVoice>::iterator it =
all_native_voices_->find(voice.name);
if (it != all_native_voices_->end()) {
libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str());
libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
}
// Map our multiplicative range to Speech Dispatcher's linear range.
// .334 = -100.
// 3 = 100.
libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
// Support languages other than the default
if (!lang.empty())
libspeechd_loader_.spd_set_language(conn_, lang.c_str());
utterance_ = utterance;
utterance_id_ = utterance_id;
if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
Reset();
return false;
}
return true;
}
bool TtsPlatformImplLinux::StopSpeaking() {
if (!PlatformImplAvailable())
return false;
if (libspeechd_loader_.spd_stop(conn_) == -1) {
Reset();
return false;
}
return true;
}
void TtsPlatformImplLinux::Pause() {
if (!PlatformImplAvailable())
return;
libspeechd_loader_.spd_pause(conn_);
}
void TtsPlatformImplLinux::Resume() {
if (!PlatformImplAvailable())
return;
libspeechd_loader_.spd_resume(conn_);
}
bool TtsPlatformImplLinux::IsSpeaking() {
return current_notification_ == SPD_EVENT_BEGIN;
}
void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
if (!all_native_voices_.get()) {
all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>());
char** modules = libspeechd_loader_.spd_list_modules(conn_);
if (!modules)
return;
for (int i = 0; modules[i]; i++) {
char* module = modules[i];
libspeechd_loader_.spd_set_output_module(conn_, module);
SPDVoice** native_voices =
libspeechd_loader_.spd_list_synthesis_voices(conn_);
if (!native_voices) {
free(module);
continue;
}
for (int j = 0; native_voices[j]; j++) {
SPDVoice* native_voice = native_voices[j];
SPDChromeVoice native_data;
native_data.name = native_voice->name;
native_data.module = module;
std::string key;
key.append(native_data.name);
key.append(" ");
key.append(native_data.module);
all_native_voices_->insert(
std::pair<std::string, SPDChromeVoice>(key, native_data));
free(native_voices[j]);
}
free(modules[i]);
}
}
for (std::map<std::string, SPDChromeVoice>::iterator it =
all_native_voices_->begin();
it != all_native_voices_->end(); it++) {
out_voices->push_back(VoiceData());
VoiceData& voice = out_voices->back();
voice.native = true;
voice.name = it->first;
voice.events.insert(TTS_EVENT_START);
voice.events.insert(TTS_EVENT_END);
voice.events.insert(TTS_EVENT_CANCELLED);
voice.events.insert(TTS_EVENT_MARKER);
voice.events.insert(TTS_EVENT_PAUSE);
voice.events.insert(TTS_EVENT_RESUME);
}
}
void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
TtsController* controller = TtsController::GetInstance();
switch (type) {
case SPD_EVENT_BEGIN:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
break;
case SPD_EVENT_RESUME:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
break;
case SPD_EVENT_END:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(),
std::string());
break;
case SPD_EVENT_PAUSE:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(),
std::string());
break;
case SPD_EVENT_CANCEL:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0,
std::string());
break;
case SPD_EVENT_INDEX_MARK:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
break;
}
}
// static
void TtsPlatformImplLinux::NotificationCallback(size_t msg_id,
size_t client_id,
SPDNotificationType type) {
// We run Speech Dispatcher in threaded mode, so these callbacks should always
// be in a separate thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = type;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
type));
}
}
// static
void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id,
size_t client_id,
SPDNotificationType state,
char* index_mark) {
// TODO(dtseng): index_mark appears to specify an index type supplied by a
// client. Need to explore how this is used before hooking it up with existing
// word, sentence events.
// We run Speech Dispatcher in threaded mode, so these callbacks should always
// be in a separate thread.
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
current_notification_ = state;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
base::Unretained(TtsPlatformImplLinux::GetInstance()),
state));
}
}
// static
TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() {
return base::Singleton<
TtsPlatformImplLinux,
base::LeakySingletonTraits<TtsPlatformImplLinux>>::get();
}
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplLinux::GetInstance();
}

View file

@ -1,344 +0,0 @@
// Copyright (c) 2012 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.
#include <string>
#include "base/mac/scoped_nsobject.h"
#include "base/memory/singleton.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/browser/speech/tts_platform.h"
#import <Cocoa/Cocoa.h>
class TtsPlatformImplMac;
@interface ChromeTtsDelegate : NSObject <NSSpeechSynthesizerDelegate> {
@private
TtsPlatformImplMac* ttsImplMac_; // weak.
}
- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac;
@end
// Subclass of NSSpeechSynthesizer that takes an utterance
// string on initialization, retains it and only allows it
// to be spoken once.
//
// We construct a new NSSpeechSynthesizer for each utterance, for
// two reasons:
// 1. To associate delegate callbacks with a particular utterance,
// without assuming anything undocumented about the protocol.
// 2. To work around http://openradar.appspot.com/radar?id=2854403,
// where Nuance voices don't retain the utterance string and
// crash when trying to call willSpeakWord.
@interface SingleUseSpeechSynthesizer : NSSpeechSynthesizer {
@private
base::scoped_nsobject<NSString> utterance_;
bool didSpeak_;
}
- (id)initWithUtterance:(NSString*)utterance;
- (bool)startSpeakingRetainedUtterance;
- (bool)startSpeakingString:(NSString*)utterance;
@end
class TtsPlatformImplMac : public TtsPlatformImpl {
public:
bool PlatformImplAvailable() override { return true; }
bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) override;
bool StopSpeaking() override;
void Pause() override;
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
// Called by ChromeTtsDelegate when we get a callback from the
// native speech engine.
void OnSpeechEvent(NSSpeechSynthesizer* sender,
TtsEventType event_type,
int char_index,
const std::string& error_message);
// Get the single instance of this class.
static TtsPlatformImplMac* GetInstance();
private:
TtsPlatformImplMac();
~TtsPlatformImplMac() override;
base::scoped_nsobject<SingleUseSpeechSynthesizer> speech_synthesizer_;
base::scoped_nsobject<ChromeTtsDelegate> delegate_;
int utterance_id_;
std::string utterance_;
int last_char_index_;
bool paused_;
friend struct base::DefaultSingletonTraits<TtsPlatformImplMac>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac);
};
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplMac::GetInstance();
}
bool TtsPlatformImplMac::Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
// TODO: convert SSML to SAPI xml. http://crbug.com/88072
utterance_ = utterance;
paused_ = false;
NSString* utterance_nsstring =
[NSString stringWithUTF8String:utterance_.c_str()];
// Deliberately construct a new speech synthesizer every time Speak is
// called, otherwise there's no way to know whether calls to the delegate
// apply to the current utterance or a previous utterance. In
// experimentation, the overhead of constructing and destructing a
// NSSpeechSynthesizer is minimal.
speech_synthesizer_.reset([[SingleUseSpeechSynthesizer alloc]
initWithUtterance:utterance_nsstring]);
[speech_synthesizer_ setDelegate:delegate_];
if (!voice.native_voice_identifier.empty()) {
NSString* native_voice_identifier =
[NSString stringWithUTF8String:voice.native_voice_identifier.c_str()];
[speech_synthesizer_ setVoice:native_voice_identifier];
}
utterance_id_ = utterance_id;
// TODO: support languages other than the default: crbug.com/88059
if (params.rate >= 0.0) {
// The TTS api defines rate via words per minute. Let 200 be the default.
[speech_synthesizer_ setObject:[NSNumber numberWithInt:params.rate * 200]
forProperty:NSSpeechRateProperty
error:nil];
}
if (params.pitch >= 0.0) {
// The input is a float from 0.0 to 2.0, with 1.0 being the default.
// Get the default pitch for this voice and modulate it by 50% - 150%.
NSError* errorCode;
NSNumber* defaultPitchObj =
[speech_synthesizer_ objectForProperty:NSSpeechPitchBaseProperty
error:&errorCode];
int defaultPitch = defaultPitchObj ? [defaultPitchObj intValue] : 48;
int newPitch = static_cast<int>(defaultPitch * (0.5 * params.pitch + 0.5));
[speech_synthesizer_ setObject:[NSNumber numberWithInt:newPitch]
forProperty:NSSpeechPitchBaseProperty
error:nil];
}
if (params.volume >= 0.0) {
[speech_synthesizer_ setObject:[NSNumber numberWithFloat:params.volume]
forProperty:NSSpeechVolumeProperty
error:nil];
}
bool success = [speech_synthesizer_ startSpeakingRetainedUtterance];
if (success) {
TtsController* controller = TtsController::GetInstance();
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, "");
}
return success;
}
bool TtsPlatformImplMac::StopSpeaking() {
if (speech_synthesizer_.get()) {
[speech_synthesizer_ stopSpeaking];
speech_synthesizer_.reset(nil);
}
paused_ = false;
return true;
}
void TtsPlatformImplMac::Pause() {
if (speech_synthesizer_.get() && utterance_id_ && !paused_) {
[speech_synthesizer_ pauseSpeakingAtBoundary:NSSpeechImmediateBoundary];
paused_ = true;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
last_char_index_, "");
}
}
void TtsPlatformImplMac::Resume() {
if (speech_synthesizer_.get() && utterance_id_ && paused_) {
[speech_synthesizer_ continueSpeaking];
paused_ = false;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
last_char_index_, "");
}
}
bool TtsPlatformImplMac::IsSpeaking() {
if (speech_synthesizer_)
return [speech_synthesizer_ isSpeaking];
return false;
}
void TtsPlatformImplMac::GetVoices(std::vector<VoiceData>* outVoices) {
NSArray* voices = [NSSpeechSynthesizer availableVoices];
// Create a new temporary array of the available voices with
// the default voice first.
NSMutableArray* orderedVoices =
[NSMutableArray arrayWithCapacity:[voices count]];
NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice];
if (defaultVoice) {
[orderedVoices addObject:defaultVoice];
}
for (NSString* voiceIdentifier in voices) {
if (![voiceIdentifier isEqualToString:defaultVoice])
[orderedVoices addObject:voiceIdentifier];
}
for (NSString* voiceIdentifier in orderedVoices) {
outVoices->push_back(VoiceData());
VoiceData& data = outVoices->back();
NSDictionary* attributes =
[NSSpeechSynthesizer attributesForVoice:voiceIdentifier];
NSString* name = [attributes objectForKey:NSVoiceName];
NSString* gender = [attributes objectForKey:NSVoiceGender];
NSString* localeIdentifier =
[attributes objectForKey:NSVoiceLocaleIdentifier];
data.native = true;
data.native_voice_identifier = base::SysNSStringToUTF8(voiceIdentifier);
data.name = base::SysNSStringToUTF8(name);
NSDictionary* localeComponents =
[NSLocale componentsFromLocaleIdentifier:localeIdentifier];
NSString* language = [localeComponents objectForKey:NSLocaleLanguageCode];
NSString* country = [localeComponents objectForKey:NSLocaleCountryCode];
if (language && country) {
data.lang =
[[NSString stringWithFormat:@"%@-%@", language, country] UTF8String];
} else {
data.lang = base::SysNSStringToUTF8(language);
}
if ([gender isEqualToString:NSVoiceGenderMale])
data.gender = TTS_GENDER_MALE;
else if ([gender isEqualToString:NSVoiceGenderFemale])
data.gender = TTS_GENDER_FEMALE;
else
data.gender = TTS_GENDER_NONE;
data.events.insert(TTS_EVENT_START);
data.events.insert(TTS_EVENT_END);
data.events.insert(TTS_EVENT_WORD);
data.events.insert(TTS_EVENT_ERROR);
data.events.insert(TTS_EVENT_CANCELLED);
data.events.insert(TTS_EVENT_INTERRUPTED);
data.events.insert(TTS_EVENT_PAUSE);
data.events.insert(TTS_EVENT_RESUME);
}
}
void TtsPlatformImplMac::OnSpeechEvent(NSSpeechSynthesizer* sender,
TtsEventType event_type,
int char_index,
const std::string& error_message) {
// Don't send events from an utterance that's already completed.
// This depends on the fact that we construct a new NSSpeechSynthesizer
// each time we call Speak.
if (sender != speech_synthesizer_.get())
return;
if (event_type == TTS_EVENT_END)
char_index = utterance_.size();
TtsController* controller = TtsController::GetInstance();
controller->OnTtsEvent(utterance_id_, event_type, char_index, error_message);
last_char_index_ = char_index;
}
TtsPlatformImplMac::TtsPlatformImplMac() {
utterance_id_ = -1;
paused_ = false;
delegate_.reset([[ChromeTtsDelegate alloc] initWithPlatformImplMac:this]);
}
TtsPlatformImplMac::~TtsPlatformImplMac() {}
// static
TtsPlatformImplMac* TtsPlatformImplMac::GetInstance() {
return base::Singleton<TtsPlatformImplMac>::get();
}
@implementation ChromeTtsDelegate
- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac {
if ((self = [super init])) {
ttsImplMac_ = ttsImplMac;
}
return self;
}
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
didFinishSpeaking:(BOOL)finished_speaking {
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_END, 0, "");
}
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
willSpeakWord:(NSRange)character_range
ofString:(NSString*)string {
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_WORD, character_range.location,
"");
}
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
didEncounterErrorAtIndex:(NSUInteger)character_index
ofString:(NSString*)string
message:(NSString*)message {
std::string message_utf8 = base::SysNSStringToUTF8(message);
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index,
message_utf8);
}
@end
@implementation SingleUseSpeechSynthesizer
- (id)initWithUtterance:(NSString*)utterance {
self = [super init];
if (self) {
utterance_.reset([utterance retain]);
didSpeak_ = false;
}
return self;
}
- (bool)startSpeakingRetainedUtterance {
CHECK(!didSpeak_);
CHECK(utterance_);
didSpeak_ = true;
return [super startSpeakingString:utterance_];
}
- (bool)startSpeakingString:(NSString*)utterance {
CHECK(false);
return false;
}
@end

View file

@ -1,176 +0,0 @@
// Copyright (c) 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.
#include "chrome/browser/speech/tts_message_filter.h"
#include "base/bind.h"
#include "base/logging.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"
using content::BrowserThread;
TtsMessageFilter::TtsMessageFilter(int render_process_id,
content::BrowserContext* browser_context)
: BrowserMessageFilter(TtsMsgStart),
render_process_id_(render_process_id),
browser_context_(browser_context),
weak_ptr_factory_(this) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->AddVoicesChangedDelegate(this);
// Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero
// until all WeakPtr's are invalidated.
AddRef();
}
void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
BrowserThread::ID* thread) {
switch (message.type()) {
case TtsHostMsg_InitializeVoiceList::ID:
case TtsHostMsg_Speak::ID:
case TtsHostMsg_Pause::ID:
case TtsHostMsg_Resume::ID:
case TtsHostMsg_Cancel::ID:
*thread = BrowserThread::UI;
break;
}
}
bool TtsMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(TtsMessageFilter, message)
IPC_MESSAGE_HANDLER(TtsHostMsg_InitializeVoiceList, OnInitializeVoiceList)
IPC_MESSAGE_HANDLER(TtsHostMsg_Speak, OnSpeak)
IPC_MESSAGE_HANDLER(TtsHostMsg_Pause, OnPause)
IPC_MESSAGE_HANDLER(TtsHostMsg_Resume, OnResume)
IPC_MESSAGE_HANDLER(TtsHostMsg_Cancel, OnCancel)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void TtsMessageFilter::OnChannelClosing() {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&TtsMessageFilter::OnChannelClosingInUIThread, this));
}
void TtsMessageFilter::OnDestruct() const {
BrowserThread::DeleteOnUIThread::Destruct(this);
}
void TtsMessageFilter::OnInitializeVoiceList() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController* tts_controller = TtsController::GetInstance();
std::vector<VoiceData> voices;
tts_controller->GetVoices(browser_context_, &voices);
std::vector<TtsVoice> out_voices;
out_voices.resize(voices.size());
for (size_t i = 0; i < voices.size(); ++i) {
TtsVoice& out_voice = out_voices[i];
out_voice.voice_uri = voices[i].name;
out_voice.name = voices[i].name;
out_voice.lang = voices[i].lang;
out_voice.local_service = !voices[i].remote;
out_voice.is_default = (i == 0);
}
Send(new TtsMsg_SetVoiceList(out_voices));
}
void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::unique_ptr<Utterance> utterance(new Utterance(browser_context_));
utterance->set_src_id(request.id);
utterance->set_text(request.text);
utterance->set_lang(request.lang);
utterance->set_voice_name(request.voice);
utterance->set_can_enqueue(true);
UtteranceContinuousParameters params;
params.rate = request.rate;
params.pitch = request.pitch;
params.volume = request.volume;
utterance->set_continuous_parameters(params);
utterance->set_event_delegate(weak_ptr_factory_.GetWeakPtr());
TtsController::GetInstance()->SpeakOrEnqueue(utterance.release());
}
void TtsMessageFilter::OnPause() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->Pause();
}
void TtsMessageFilter::OnResume() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->Resume();
}
void TtsMessageFilter::OnCancel() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->Stop();
}
void TtsMessageFilter::OnTtsEvent(Utterance* utterance,
TtsEventType event_type,
int char_index,
const std::string& error_message) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
switch (event_type) {
case TTS_EVENT_START:
Send(new TtsMsg_DidStartSpeaking(utterance->src_id()));
break;
case TTS_EVENT_END:
Send(new TtsMsg_DidFinishSpeaking(utterance->src_id()));
break;
case TTS_EVENT_WORD:
Send(new TtsMsg_WordBoundary(utterance->src_id(), char_index));
break;
case TTS_EVENT_SENTENCE:
Send(new TtsMsg_SentenceBoundary(utterance->src_id(), char_index));
break;
case TTS_EVENT_MARKER:
Send(new TtsMsg_MarkerEvent(utterance->src_id(), char_index));
break;
case TTS_EVENT_INTERRUPTED:
Send(new TtsMsg_WasInterrupted(utterance->src_id()));
break;
case TTS_EVENT_CANCELLED:
Send(new TtsMsg_WasCancelled(utterance->src_id()));
break;
case TTS_EVENT_ERROR:
Send(
new TtsMsg_SpeakingErrorOccurred(utterance->src_id(), error_message));
break;
case TTS_EVENT_PAUSE:
Send(new TtsMsg_DidPauseSpeaking(utterance->src_id()));
break;
case TTS_EVENT_RESUME:
Send(new TtsMsg_DidResumeSpeaking(utterance->src_id()));
break;
}
}
void TtsMessageFilter::OnVoicesChanged() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
OnInitializeVoiceList();
}
void TtsMessageFilter::OnChannelClosingInUIThread() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
weak_ptr_factory_.InvalidateWeakPtrs();
Release(); // Balanced in TtsMessageFilter().
}
TtsMessageFilter::~TtsMessageFilter() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!weak_ptr_factory_.HasWeakPtrs());
TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
}

View file

@ -1,62 +0,0 @@
// Copyright (c) 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.
#ifndef CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
#define CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
#include "base/memory/weak_ptr.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/common/tts_messages.h"
#include "content/public/browser/browser_message_filter.h"
namespace content {
class BrowserContext;
}
class TtsMessageFilter : public content::BrowserMessageFilter,
public UtteranceEventDelegate,
public VoicesChangedDelegate {
public:
explicit TtsMessageFilter(int render_process_id,
content::BrowserContext* browser_context);
// content::BrowserMessageFilter implementation.
void OverrideThreadForMessage(const IPC::Message& message,
content::BrowserThread::ID* thread) override;
bool OnMessageReceived(const IPC::Message& message) override;
void OnChannelClosing() override;
void OnDestruct() const override;
// UtteranceEventDelegate implementation.
void OnTtsEvent(Utterance* utterance,
TtsEventType event_type,
int char_index,
const std::string& error_message) override;
// VoicesChangedDelegate implementation.
void OnVoicesChanged() override;
private:
friend class content::BrowserThread;
friend class base::DeleteHelper<TtsMessageFilter>;
~TtsMessageFilter() override;
void OnInitializeVoiceList();
void OnSpeak(const TtsUtteranceRequest& utterance);
void OnPause();
void OnResume();
void OnCancel();
void OnChannelClosingInUIThread();
int render_process_id_;
content::BrowserContext* browser_context_;
base::WeakPtrFactory<TtsMessageFilter> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter);
};
#endif // CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_

View file

@ -1,28 +0,0 @@
// Copyright (c) 2012 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.
#include "chrome/browser/speech/tts_platform.h"
#include <string>
bool TtsPlatformImpl::LoadBuiltInTtsExtension(
content::BrowserContext* browser_context) {
return false;
}
std::string TtsPlatformImpl::error() {
return error_;
}
void TtsPlatformImpl::clear_error() {
error_ = std::string();
}
void TtsPlatformImpl::set_error(const std::string& error) {
error_ = error;
}
void TtsPlatformImpl::WillSpeakUtteranceWithVoice(const Utterance* utterance,
const VoiceData& voice_data) {
}

View file

@ -1,80 +0,0 @@
// Copyright (c) 2012 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.
#ifndef CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
#define CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
#include <string>
#include "chrome/browser/speech/tts_controller.h"
// Abstract class that defines the native platform TTS interface,
// subclassed by specific implementations on Win, Mac, etc.
class TtsPlatformImpl {
public:
static TtsPlatformImpl* GetInstance();
// Returns true if this platform implementation is supported and available.
virtual bool PlatformImplAvailable() = 0;
// Some platforms may provide a built-in TTS extension. Returns true
// if the extension was not previously loaded and is now loading, and
// false if it's already loaded or if there's no extension to load.
// Will call TtsController::RetrySpeakingQueuedUtterances when
// the extension finishes loading.
virtual bool LoadBuiltInTtsExtension(
content::BrowserContext* browser_context);
// Speak the given utterance with the given parameters if possible,
// and return true on success. Utterance will always be nonempty.
// If rate, pitch, or volume are -1.0, they will be ignored.
//
// The TtsController will only try to speak one utterance at
// a time. If it wants to interrupt speech, it will always call Stop
// before speaking again.
virtual bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) = 0;
// Stop speaking immediately and return true on success.
virtual bool StopSpeaking() = 0;
// Returns whether any speech is on going.
virtual bool IsSpeaking() = 0;
// Append information about voices provided by this platform implementation
// to |out_voices|.
virtual void GetVoices(std::vector<VoiceData>* out_voices) = 0;
// Pause the current utterance, if any, until a call to Resume,
// Speak, or StopSpeaking.
virtual void Pause() = 0;
// Resume speaking the current utterance, if it was paused.
virtual void Resume() = 0;
// Allows the platform to monitor speech commands and the voices used
// for each one.
virtual void WillSpeakUtteranceWithVoice(const Utterance* utterance,
const VoiceData& voice_data);
virtual std::string error();
virtual void clear_error();
virtual void set_error(const std::string& error);
protected:
TtsPlatformImpl() {}
// On some platforms this may be a leaky singleton - do not rely on the
// destructor being called! http://crbug.com/122026
virtual ~TtsPlatformImpl() {}
std::string error_;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl);
};
#endif // CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_

View file

@ -1,311 +0,0 @@
// Copyright (c) 2012 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.
#include <math.h>
#include <objbase.h>
#include <sapi.h>
#include <sphelper.h>
#include <wrl/client.h>
#include "base/memory/singleton.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/scoped_co_mem.h"
#include "chrome/browser/speech/tts_controller.h"
#include "chrome/browser/speech/tts_platform.h"
namespace {
// ISpObjectToken key and value names.
const wchar_t kAttributesKey[] = L"Attributes";
const wchar_t kGenderValue[] = L"Gender";
const wchar_t kLanguageValue[] = L"Language";
} // anonymous namespace.
class TtsPlatformImplWin : public TtsPlatformImpl {
public:
bool PlatformImplAvailable() override { return true; }
bool Speak(int utterance_id,
const std::string& utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) override;
bool StopSpeaking() override;
void Pause() override;
void Resume() override;
bool IsSpeaking() override;
void GetVoices(std::vector<VoiceData>* out_voices) override;
// Get the single instance of this class.
static TtsPlatformImplWin* GetInstance();
static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param);
private:
TtsPlatformImplWin();
~TtsPlatformImplWin() override {}
void OnSpeechEvent();
void SetVoiceFromName(const std::string& name);
Microsoft::WRL::ComPtr<ISpVoice> speech_synthesizer_;
// These apply to the current utterance only.
std::wstring utterance_;
int utterance_id_;
int prefix_len_;
ULONG stream_number_;
int char_position_;
bool paused_;
std::string last_voice_name_;
friend struct base::DefaultSingletonTraits<TtsPlatformImplWin>;
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin);
};
// static
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
return TtsPlatformImplWin::GetInstance();
}
bool TtsPlatformImplWin::Speak(int utterance_id,
const std::string& src_utterance,
const std::string& lang,
const VoiceData& voice,
const UtteranceContinuousParameters& params) {
std::wstring prefix;
std::wstring suffix;
if (!speech_synthesizer_.Get())
return false;
SetVoiceFromName(voice.name);
if (params.rate >= 0.0) {
// Map our multiplicative range of 0.1x to 10.0x onto Microsoft's
// linear range of -10 to 10:
// 0.1 -> -10
// 1.0 -> 0
// 10.0 -> 10
speech_synthesizer_->SetRate(static_cast<int32_t>(10 * log10(params.rate)));
}
if (params.pitch >= 0.0) {
// The TTS api allows a range of -10 to 10 for speech pitch.
// TODO(dtseng): cleanup if we ever use any other properties that
// require xml.
std::wstring pitch_value =
base::IntToString16(static_cast<int>(params.pitch * 10 - 10));
prefix = L"<pitch absmiddle=\"" + pitch_value + L"\">";
suffix = L"</pitch>";
}
if (params.volume >= 0.0) {
// The TTS api allows a range of 0 to 100 for speech volume.
speech_synthesizer_->SetVolume(static_cast<uint16_t>(params.volume * 100));
}
// TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072
utterance_ = base::UTF8ToWide(src_utterance);
utterance_id_ = utterance_id;
char_position_ = 0;
std::wstring merged_utterance = prefix + utterance_ + suffix;
prefix_len_ = prefix.size();
HRESULT result = speech_synthesizer_->Speak(merged_utterance.c_str(),
SPF_ASYNC, &stream_number_);
return (result == S_OK);
}
bool TtsPlatformImplWin::StopSpeaking() {
if (speech_synthesizer_.Get()) {
// Clear the stream number so that any further events relating to this
// utterance are ignored.
stream_number_ = 0;
if (IsSpeaking()) {
// Stop speech by speaking the empty string with the purge flag.
speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
}
if (paused_) {
speech_synthesizer_->Resume();
paused_ = false;
}
}
return true;
}
void TtsPlatformImplWin::Pause() {
if (speech_synthesizer_.Get() && utterance_id_ && !paused_) {
speech_synthesizer_->Pause();
paused_ = true;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
char_position_, "");
}
}
void TtsPlatformImplWin::Resume() {
if (speech_synthesizer_.Get() && utterance_id_ && paused_) {
speech_synthesizer_->Resume();
paused_ = false;
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
char_position_, "");
}
}
bool TtsPlatformImplWin::IsSpeaking() {
if (speech_synthesizer_.Get()) {
SPVOICESTATUS status;
HRESULT result = speech_synthesizer_->GetStatus(&status, NULL);
if (result == S_OK) {
if (status.dwRunningState == 0 || // 0 == waiting to speak
status.dwRunningState == SPRS_IS_SPEAKING) {
return true;
}
}
}
return false;
}
void TtsPlatformImplWin::GetVoices(std::vector<VoiceData>* out_voices) {
Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
unsigned long voice_count;
if (S_OK !=
SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
return;
if (S_OK != voice_tokens->GetCount(&voice_count))
return;
for (unsigned i = 0; i < voice_count; i++) {
VoiceData voice;
Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
return;
base::win::ScopedCoMem<WCHAR> description;
if (S_OK != SpGetDescription(voice_token.Get(), &description))
continue;
voice.name = base::WideToUTF8(description.get());
Microsoft::WRL::ComPtr<ISpDataKey> attributes;
if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.GetAddressOf()))
continue;
base::win::ScopedCoMem<WCHAR> gender;
if (S_OK == attributes->GetStringValue(kGenderValue, &gender)) {
if (0 == _wcsicmp(gender.get(), L"male"))
voice.gender = TTS_GENDER_MALE;
else if (0 == _wcsicmp(gender.get(), L"female"))
voice.gender = TTS_GENDER_FEMALE;
}
base::win::ScopedCoMem<WCHAR> language;
if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) {
int lcid_value;
base::HexStringToInt(base::WideToUTF8(language.get()), &lcid_value);
LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT);
WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0};
LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0);
voice.lang = base::WideToUTF8(locale_name);
}
voice.native = true;
voice.events.insert(TTS_EVENT_START);
voice.events.insert(TTS_EVENT_END);
voice.events.insert(TTS_EVENT_MARKER);
voice.events.insert(TTS_EVENT_WORD);
voice.events.insert(TTS_EVENT_SENTENCE);
voice.events.insert(TTS_EVENT_PAUSE);
voice.events.insert(TTS_EVENT_RESUME);
out_voices->push_back(voice);
}
}
void TtsPlatformImplWin::OnSpeechEvent() {
TtsController* controller = TtsController::GetInstance();
SPEVENT event;
while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) {
if (event.ulStreamNum != stream_number_)
continue;
switch (event.eEventId) {
case SPEI_START_INPUT_STREAM:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
std::string());
break;
case SPEI_END_INPUT_STREAM:
char_position_ = utterance_.size();
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, char_position_,
std::string());
break;
case SPEI_TTS_BOOKMARK:
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, char_position_,
std::string());
break;
case SPEI_WORD_BOUNDARY:
char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
controller->OnTtsEvent(utterance_id_, TTS_EVENT_WORD, char_position_,
std::string());
break;
case SPEI_SENTENCE_BOUNDARY:
char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
controller->OnTtsEvent(utterance_id_, TTS_EVENT_SENTENCE,
char_position_, std::string());
break;
default:
break;
}
}
}
void TtsPlatformImplWin::SetVoiceFromName(const std::string& name) {
if (name.empty() || name == last_voice_name_)
return;
last_voice_name_ = name;
Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
unsigned long voice_count;
if (S_OK !=
SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
return;
if (S_OK != voice_tokens->GetCount(&voice_count))
return;
for (unsigned i = 0; i < voice_count; i++) {
Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
return;
base::win::ScopedCoMem<WCHAR> description;
if (S_OK != SpGetDescription(voice_token.Get(), &description))
continue;
if (name == base::WideToUTF8(description.get())) {
speech_synthesizer_->SetVoice(voice_token.Get());
break;
}
}
}
TtsPlatformImplWin::TtsPlatformImplWin()
: utterance_id_(0),
prefix_len_(0),
stream_number_(0),
char_position_(0),
paused_(false) {
::CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&speech_synthesizer_));
if (speech_synthesizer_.Get()) {
ULONGLONG event_mask =
SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_TTS_BOOKMARK) |
SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_SENTENCE_BOUNDARY) |
SPFEI(SPEI_END_INPUT_STREAM);
speech_synthesizer_->SetInterest(event_mask, event_mask);
speech_synthesizer_->SetNotifyCallbackFunction(
TtsPlatformImplWin::SpeechEventCallback, 0, 0);
}
}
// static
TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() {
return base::Singleton<TtsPlatformImplWin,
base::LeakySingletonTraits<TtsPlatformImplWin>>::get();
}
// static
void TtsPlatformImplWin::SpeechEventCallback(WPARAM w_param, LPARAM l_param) {
GetInstance()->OnSpeechEvent();
}

View file

@ -1,61 +0,0 @@
// Copyright (c) 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.
// Multiply-included message file, hence no include guard.
#include <vector>
#include "chrome/common/tts_utterance_request.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_param_traits.h"
#define IPC_MESSAGE_START TtsMsgStart
IPC_STRUCT_TRAITS_BEGIN(TtsUtteranceRequest)
IPC_STRUCT_TRAITS_MEMBER(id)
IPC_STRUCT_TRAITS_MEMBER(text)
IPC_STRUCT_TRAITS_MEMBER(lang)
IPC_STRUCT_TRAITS_MEMBER(voice)
IPC_STRUCT_TRAITS_MEMBER(volume)
IPC_STRUCT_TRAITS_MEMBER(rate)
IPC_STRUCT_TRAITS_MEMBER(pitch)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(TtsVoice)
IPC_STRUCT_TRAITS_MEMBER(voice_uri)
IPC_STRUCT_TRAITS_MEMBER(name)
IPC_STRUCT_TRAITS_MEMBER(lang)
IPC_STRUCT_TRAITS_MEMBER(local_service)
IPC_STRUCT_TRAITS_MEMBER(is_default)
IPC_STRUCT_TRAITS_END()
// Renderer -> Browser messages.
IPC_MESSAGE_CONTROL0(TtsHostMsg_InitializeVoiceList)
IPC_MESSAGE_CONTROL1(TtsHostMsg_Speak, TtsUtteranceRequest)
IPC_MESSAGE_CONTROL0(TtsHostMsg_Pause)
IPC_MESSAGE_CONTROL0(TtsHostMsg_Resume)
IPC_MESSAGE_CONTROL0(TtsHostMsg_Cancel)
// Browser -> Renderer messages.
IPC_MESSAGE_CONTROL1(TtsMsg_SetVoiceList, std::vector<TtsVoice>)
IPC_MESSAGE_CONTROL1(TtsMsg_DidStartSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_DidFinishSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_DidPauseSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_DidResumeSpeaking, int /* utterance id */)
IPC_MESSAGE_CONTROL2(TtsMsg_WordBoundary,
int /* utterance id */,
int /* char index */)
IPC_MESSAGE_CONTROL2(TtsMsg_SentenceBoundary,
int /* utterance id */,
int /* char index */)
IPC_MESSAGE_CONTROL2(TtsMsg_MarkerEvent,
int /* utterance id */,
int /* char index */)
IPC_MESSAGE_CONTROL1(TtsMsg_WasInterrupted, int /* utterance id */)
IPC_MESSAGE_CONTROL1(TtsMsg_WasCancelled, int /* utterance id */)
IPC_MESSAGE_CONTROL2(TtsMsg_SpeakingErrorOccurred,
int /* utterance id */,
std::string /* error message */)

View file

@ -1,20 +0,0 @@
// Copyright (c) 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.
#include "chrome/common/tts_utterance_request.h"
TtsUtteranceRequest::TtsUtteranceRequest()
: id(0), volume(1.0), rate(1.0), pitch(1.0) {}
TtsUtteranceRequest::~TtsUtteranceRequest() {}
TtsVoice::TtsVoice() : local_service(true), is_default(false) {}
TtsVoice::TtsVoice(const TtsVoice&) = default;
TtsVoice::~TtsVoice() {}
TtsUtteranceResponse::TtsUtteranceResponse() : id(0) {}
TtsUtteranceResponse::~TtsUtteranceResponse() {}

View file

@ -1,45 +0,0 @@
// Copyright (c) 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.
#ifndef CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
#define CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
#include <vector>
#include "base/macros.h"
#include "base/strings/string16.h"
struct TtsUtteranceRequest {
TtsUtteranceRequest();
~TtsUtteranceRequest();
int id;
std::string text;
std::string lang;
std::string voice;
float volume;
float rate;
float pitch;
};
struct TtsVoice {
TtsVoice();
TtsVoice(const TtsVoice&);
~TtsVoice();
std::string voice_uri;
std::string name;
std::string lang;
bool local_service;
bool is_default;
};
struct TtsUtteranceResponse {
TtsUtteranceResponse();
~TtsUtteranceResponse();
int id;
};
#endif // CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_

View file

@ -1,198 +0,0 @@
// Copyright (c) 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.
#include "chrome/renderer/tts_dispatcher.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/common/tts_messages.h"
#include "chrome/common/tts_utterance_request.h"
#include "content/public/renderer/render_thread.h"
#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
#include "third_party/blink/public/platform/web_speech_synthesis_voice.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
using blink::WebSpeechSynthesisUtterance;
using blink::WebSpeechSynthesisVoice;
using blink::WebSpeechSynthesizerClient;
using blink::WebString;
using blink::WebVector;
using content::RenderThread;
int TtsDispatcher::next_utterance_id_ = 1;
TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client)
: synthesizer_client_(client) {
RenderThread::Get()->AddObserver(this);
}
TtsDispatcher::~TtsDispatcher() {
RenderThread::Get()->RemoveObserver(this);
}
bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message)
IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList)
IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking)
IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary)
IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary)
IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent)
IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted)
IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled)
IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred)
IPC_END_MESSAGE_MAP()
// Always return false because there may be multiple TtsDispatchers
// and we want them all to have a chance to handle this message.
return false;
}
void TtsDispatcher::UpdateVoiceList() {
RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList());
}
void TtsDispatcher::Speak(const WebSpeechSynthesisUtterance& web_utterance) {
int id = next_utterance_id_++;
utterance_id_map_[id] = web_utterance;
TtsUtteranceRequest utterance;
utterance.id = id;
utterance.text = web_utterance.GetText().Utf8();
utterance.lang = web_utterance.Lang().Utf8();
utterance.voice = web_utterance.Voice().Utf8();
utterance.volume = web_utterance.Volume();
utterance.rate = web_utterance.Rate();
utterance.pitch = web_utterance.Pitch();
RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance));
}
void TtsDispatcher::Pause() {
RenderThread::Get()->Send(new TtsHostMsg_Pause());
}
void TtsDispatcher::Resume() {
RenderThread::Get()->Send(new TtsHostMsg_Resume());
}
void TtsDispatcher::Cancel() {
RenderThread::Get()->Send(new TtsHostMsg_Cancel());
}
WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) {
base::hash_map<int, WebSpeechSynthesisUtterance>::const_iterator iter =
utterance_id_map_.find(utterance_id);
if (iter == utterance_id_map_.end())
return WebSpeechSynthesisUtterance();
return iter->second;
}
void TtsDispatcher::OnSetVoiceList(const std::vector<TtsVoice>& voices) {
WebVector<WebSpeechSynthesisVoice> out_voices(voices.size());
for (size_t i = 0; i < voices.size(); ++i) {
out_voices[i] = WebSpeechSynthesisVoice();
out_voices[i].SetVoiceURI(WebString::FromUTF8(voices[i].voice_uri));
out_voices[i].SetName(WebString::FromUTF8(voices[i].name));
out_voices[i].SetLanguage(WebString::FromUTF8(voices[i].lang));
out_voices[i].SetIsLocalService(voices[i].local_service);
out_voices[i].SetIsDefault(voices[i].is_default);
}
synthesizer_client_->SetVoiceList(out_voices);
}
void TtsDispatcher::OnDidStartSpeaking(int utterance_id) {
if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end())
return;
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidStartSpeaking(utterance);
}
void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidFinishSpeaking(utterance);
utterance_id_map_.erase(utterance_id);
}
void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidPauseSpeaking(utterance);
}
void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->DidResumeSpeaking(utterance);
}
void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) {
CHECK(char_index >= 0);
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->WordBoundaryEventOccurred(
utterance, static_cast<unsigned>(char_index));
}
void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) {
CHECK(char_index >= 0);
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
synthesizer_client_->SentenceBoundaryEventOccurred(
utterance, static_cast<unsigned>(char_index));
}
void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) {
// Not supported yet.
}
void TtsDispatcher::OnWasInterrupted(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
// The web speech API doesn't support "interrupted".
synthesizer_client_->DidFinishSpeaking(utterance);
utterance_id_map_.erase(utterance_id);
}
void TtsDispatcher::OnWasCancelled(int utterance_id) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
// The web speech API doesn't support "cancelled".
synthesizer_client_->DidFinishSpeaking(utterance);
utterance_id_map_.erase(utterance_id);
}
void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id,
const std::string& error_message) {
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
if (utterance.IsNull())
return;
// The web speech API doesn't support an error message.
synthesizer_client_->SpeakingErrorOccurred(utterance);
utterance_id_map_.erase(utterance_id);
}

View file

@ -1,73 +0,0 @@
// Copyright (c) 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.
#ifndef CHROME_RENDERER_TTS_DISPATCHER_H_
#define CHROME_RENDERER_TTS_DISPATCHER_H_
#include <vector>
#include "base/containers/hash_tables.h"
#include "content/public/renderer/render_thread_observer.h"
#include "third_party/blink/public/platform/web_speech_synthesizer.h"
#include "third_party/blink/public/platform/web_speech_synthesizer_client.h"
namespace IPC {
class Message;
}
struct TtsVoice;
// TtsDispatcher is a delegate for methods used by Blink for speech synthesis
// APIs. It's the complement of TtsDispatcherHost (owned by RenderViewHost).
// Each TtsDispatcher is owned by the WebSpeechSynthesizerClient in Blink;
// it registers itself to listen to IPC upon construction and unregisters
// itself when deleted. There can be multiple TtsDispatchers alive at once,
// so each one routes IPC messages to its WebSpeechSynthesizerClient only if
// the utterance id (which is globally unique) matches.
class TtsDispatcher : public blink::WebSpeechSynthesizer,
public content::RenderThreadObserver {
public:
explicit TtsDispatcher(blink::WebSpeechSynthesizerClient* client);
~TtsDispatcher() override;
private:
// RenderProcessObserver override.
bool OnControlMessageReceived(const IPC::Message& message) override;
// blink::WebSpeechSynthesizer implementation.
void UpdateVoiceList() override;
void Speak(const blink::WebSpeechSynthesisUtterance& utterance) override;
void Pause() override;
void Resume() override;
void Cancel() override;
blink::WebSpeechSynthesisUtterance FindUtterance(int utterance_id);
void OnSetVoiceList(const std::vector<TtsVoice>& voices);
void OnDidStartSpeaking(int utterance_id);
void OnDidFinishSpeaking(int utterance_id);
void OnDidPauseSpeaking(int utterance_id);
void OnDidResumeSpeaking(int utterance_id);
void OnWordBoundary(int utterance_id, int char_index);
void OnSentenceBoundary(int utterance_id, int char_index);
void OnMarkerEvent(int utterance_id, int char_index);
void OnWasInterrupted(int utterance_id);
void OnWasCancelled(int utterance_id);
void OnSpeakingErrorOccurred(int utterance_id,
const std::string& error_message);
// The WebKit client class that we use to send events back to the JS world.
// Weak reference, this will be valid as long as this object exists.
blink::WebSpeechSynthesizerClient* synthesizer_client_;
// Next utterance id, used to map response IPCs to utterance objects.
static int next_utterance_id_;
// Map from id to utterance objects.
base::hash_map<int, blink::WebSpeechSynthesisUtterance> utterance_id_map_;
DISALLOW_COPY_AND_ASSIGN(TtsDispatcher);
};
#endif // CHROME_RENDERER_TTS_DISPATCHER_H_

View file

@ -1,52 +0,0 @@
// This is generated file. Do not modify directly.
// Path to the code generator:
// tools/generate_library_loader/generate_library_loader.py .
#ifndef LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
#include "third_party/speech-dispatcher/libspeechd.h"
#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN
#include <string>
class LibSpeechdLoader {
public:
LibSpeechdLoader();
~LibSpeechdLoader();
bool Load(const std::string& library_name)
__attribute__((warn_unused_result));
bool loaded() const { return loaded_; }
decltype(&::spd_open) spd_open;
decltype(&::spd_say) spd_say;
decltype(&::spd_stop) spd_stop;
decltype(&::spd_close) spd_close;
decltype(&::spd_pause) spd_pause;
decltype(&::spd_resume) spd_resume;
decltype(&::spd_set_notification_on) spd_set_notification_on;
decltype(&::spd_set_voice_rate) spd_set_voice_rate;
decltype(&::spd_set_voice_pitch) spd_set_voice_pitch;
decltype(&::spd_list_synthesis_voices) spd_list_synthesis_voices;
decltype(&::spd_set_synthesis_voice) spd_set_synthesis_voice;
decltype(&::spd_list_modules) spd_list_modules;
decltype(&::spd_set_output_module) spd_set_output_module;
decltype(&::spd_set_language) spd_set_language;
private:
void CleanUp(bool unload);
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
void* library_;
#endif
bool loaded_;
// Disallow copy constructor and assignment operator.
LibSpeechdLoader(const LibSpeechdLoader&);
void operator=(const LibSpeechdLoader&);
};
#endif // LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H

View file

@ -1,252 +0,0 @@
// This is generated file. Do not modify directly.
// Path to the code generator:
// tools/generate_library_loader/generate_library_loader.py .
#include "library_loaders/libspeechd.h"
#include <dlfcn.h>
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \
!defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
#error neither LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN nor LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \
defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
#error both LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN and LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
#endif
LibSpeechdLoader::LibSpeechdLoader() : loaded_(false) {}
LibSpeechdLoader::~LibSpeechdLoader() {
CleanUp(loaded_);
}
bool LibSpeechdLoader::Load(const std::string& library_name) {
if (loaded_)
return false;
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_)
return false;
#endif
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_open =
reinterpret_cast<decltype(this->spd_open)>(dlsym(library_, "spd_open"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_open = &::spd_open;
#endif
if (!spd_open) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_say =
reinterpret_cast<decltype(this->spd_say)>(dlsym(library_, "spd_say"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_say = &::spd_say;
#endif
if (!spd_say) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_stop =
reinterpret_cast<decltype(this->spd_stop)>(dlsym(library_, "spd_stop"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_stop = &::spd_stop;
#endif
if (!spd_stop) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_close =
reinterpret_cast<decltype(this->spd_close)>(dlsym(library_, "spd_close"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_close = &::spd_close;
#endif
if (!spd_close) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_pause =
reinterpret_cast<decltype(this->spd_pause)>(dlsym(library_, "spd_pause"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_pause = &::spd_pause;
#endif
if (!spd_pause) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_resume = reinterpret_cast<decltype(this->spd_resume)>(
dlsym(library_, "spd_resume"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_resume = &::spd_resume;
#endif
if (!spd_resume) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_notification_on =
reinterpret_cast<decltype(this->spd_set_notification_on)>(
dlsym(library_, "spd_set_notification_on"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_notification_on = &::spd_set_notification_on;
#endif
if (!spd_set_notification_on) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_voice_rate = reinterpret_cast<decltype(this->spd_set_voice_rate)>(
dlsym(library_, "spd_set_voice_rate"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_voice_rate = &::spd_set_voice_rate;
#endif
if (!spd_set_voice_rate) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_voice_pitch = reinterpret_cast<decltype(this->spd_set_voice_pitch)>(
dlsym(library_, "spd_set_voice_pitch"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_voice_pitch = &::spd_set_voice_pitch;
#endif
if (!spd_set_voice_pitch) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_list_synthesis_voices =
reinterpret_cast<decltype(this->spd_list_synthesis_voices)>(
dlsym(library_, "spd_list_synthesis_voices"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_list_synthesis_voices = &::spd_list_synthesis_voices;
#endif
if (!spd_list_synthesis_voices) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_synthesis_voice =
reinterpret_cast<decltype(this->spd_set_synthesis_voice)>(
dlsym(library_, "spd_set_synthesis_voice"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_synthesis_voice = &::spd_set_synthesis_voice;
#endif
if (!spd_set_synthesis_voice) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_list_modules = reinterpret_cast<decltype(this->spd_list_modules)>(
dlsym(library_, "spd_list_modules"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_list_modules = &::spd_list_modules;
#endif
if (!spd_list_modules) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_output_module =
reinterpret_cast<decltype(this->spd_set_output_module)>(
dlsym(library_, "spd_set_output_module"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_output_module = &::spd_set_output_module;
#endif
if (!spd_set_output_module) {
CleanUp(true);
return false;
}
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
spd_set_language = reinterpret_cast<decltype(this->spd_set_language)>(
dlsym(library_, "spd_set_language"));
#endif
#if defined( \
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
spd_set_language = &::spd_set_language;
#endif
if (!spd_set_language) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
void LibSpeechdLoader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
spd_open = NULL;
spd_say = NULL;
spd_stop = NULL;
spd_close = NULL;
spd_pause = NULL;
spd_resume = NULL;
spd_set_notification_on = NULL;
spd_set_voice_rate = NULL;
spd_set_voice_pitch = NULL;
spd_list_synthesis_voices = NULL;
spd_set_synthesis_voice = NULL;
spd_list_modules = NULL;
spd_set_output_module = NULL;
spd_set_language = NULL;
}

View file

@ -577,8 +577,8 @@ filenames = {
"atom/renderer/web_worker_observer.h", "atom/renderer/web_worker_observer.h",
"atom/utility/atom_content_utility_client.cc", "atom/utility/atom_content_utility_client.cc",
"atom/utility/atom_content_utility_client.h", "atom/utility/atom_content_utility_client.h",
"chromium_src/chrome/browser/browser_process.cc", "chromium_src/chrome/browser/browser_process_impl.cc",
"chromium_src/chrome/browser/browser_process.h", "chromium_src/chrome/browser/browser_process_impl.h",
"chromium_src/chrome/browser/chrome_process_finder_win.cc", "chromium_src/chrome/browser/chrome_process_finder_win.cc",
"chromium_src/chrome/browser/chrome_process_finder_win.h", "chromium_src/chrome/browser/chrome_process_finder_win.h",
"chromium_src/chrome/browser/chrome_notification_types.h", "chromium_src/chrome/browser/chrome_notification_types.h",
@ -604,16 +604,6 @@ filenames = {
"chromium_src/chrome/browser/process_singleton_posix.cc", "chromium_src/chrome/browser/process_singleton_posix.cc",
"chromium_src/chrome/browser/process_singleton_win.cc", "chromium_src/chrome/browser/process_singleton_win.cc",
"chromium_src/chrome/browser/process_singleton.h", "chromium_src/chrome/browser/process_singleton.h",
"chromium_src/chrome/browser/speech/tts_controller.h",
"chromium_src/chrome/browser/speech/tts_controller_impl.cc",
"chromium_src/chrome/browser/speech/tts_controller_impl.h",
"chromium_src/chrome/browser/speech/tts_linux.cc",
"chromium_src/chrome/browser/speech/tts_mac.mm",
"chromium_src/chrome/browser/speech/tts_message_filter.cc",
"chromium_src/chrome/browser/speech/tts_message_filter.h",
"chromium_src/chrome/browser/speech/tts_platform.cc",
"chromium_src/chrome/browser/speech/tts_platform.h",
"chromium_src/chrome/browser/speech/tts_win.cc",
"chromium_src/chrome/browser/ui/browser_dialogs.h", "chromium_src/chrome/browser/ui/browser_dialogs.h",
"chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm", "chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm",
"chromium_src/chrome/browser/ui/views/color_chooser_aura.cc", "chromium_src/chrome/browser/ui/views/color_chooser_aura.cc",
@ -622,9 +612,6 @@ filenames = {
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h", "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
"chromium_src/chrome/common/print_messages.cc", "chromium_src/chrome/common/print_messages.cc",
"chromium_src/chrome/common/print_messages.h", "chromium_src/chrome/common/print_messages.h",
"chromium_src/chrome/common/tts_messages.h",
"chromium_src/chrome/common/tts_utterance_request.cc",
"chromium_src/chrome/common/tts_utterance_request.h",
"chromium_src/chrome/renderer/printing/print_web_view_helper.cc", "chromium_src/chrome/renderer/printing/print_web_view_helper.cc",
"chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc", "chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc",
"chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm", "chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm",
@ -632,11 +619,7 @@ filenames = {
"chromium_src/chrome/renderer/printing/print_web_view_helper.h", "chromium_src/chrome/renderer/printing/print_web_view_helper.h",
"chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc", "chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc",
"chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h", "chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h",
"chromium_src/chrome/renderer/tts_dispatcher.cc",
"chromium_src/chrome/renderer/tts_dispatcher.h",
"chromium_src/chrome/utility/utility_message_handler.h", "chromium_src/chrome/utility/utility_message_handler.h",
"chromium_src/library_loaders/libspeechd_loader.cc",
"chromium_src/library_loaders/libspeechd.h",
] ]
lib_sources_nss = [ lib_sources_nss = [

View file

@ -380,7 +380,7 @@ patches:
file: allow_nested_error_trackers.patch file: allow_nested_error_trackers.patch
description: | description: |
Only one X11ErrorTracker should exist at a time, but upstream has a bug Only one X11ErrorTracker should exist at a time, but upstream has a bug
where two can exist if running in headless mode -- where two can exist if running in headless mode --
ui::(anonymous namespace)::SupportsEWMH() [inner tracker is created] ui::(anonymous namespace)::SupportsEWMH() [inner tracker is created]
ui::WmSupportsHint() ui::WmSupportsHint()
ui::IsX11WindowFullScreen() ui::IsX11WindowFullScreen()
@ -430,11 +430,6 @@ patches:
description: | description: |
Compilation of those files fails with the Chromium 68. Compilation of those files fails with the Chromium 68.
Remove the patch during the Chromium 69 upgrade. Remove the patch during the Chromium 69 upgrade.
-
author: deepak1556 <hop2deep@gmail.com>
file: disable_extensions_gn.patch
description: |
Fix build files generation when chrome extensions are disabled.
- -
author: Jeremy Apthorp <nornagon@nornagon.net> author: Jeremy Apthorp <nornagon@nornagon.net>
file: crashpad_http_status.patch file: crashpad_http_status.patch
@ -503,3 +498,12 @@ patches:
(and aren't compiling command.cc), it's safe to duplicate the (and aren't compiling command.cc), it's safe to duplicate the
definition. A candidate for upstreaming would be to move the IsMediaKey definition. A candidate for upstreaming would be to move the IsMediaKey
function into //ui. function into //ui.
-
author: Heilig Benedek <benecene@gmail.com>
file: tts.patch
description: |
* Adds patch in //chrome/browser/speech/tts_controller_impl.cc
to disable calls using chrome profile class.
* Adds patch in //chrome/browser/speech/tts_message_filter.cc
to remove reference to browser context when its signaled for
destruction from content layer.

View file

@ -1,65 +0,0 @@
From 790e1e19af133252e95e3a4de8c8e7a3a011bf02 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Thu, 20 Sep 2018 17:50:48 -0700
Subject: disable_extensions_gn.patch
Fix build files generation when chrome extensions are disabled.
diff --git a/chrome/browser/apps/app_shim/BUILD.gn b/chrome/browser/apps/app_shim/BUILD.gn
index f8a6d1868788..350c3572ec54 100644
--- a/chrome/browser/apps/app_shim/BUILD.gn
+++ b/chrome/browser/apps/app_shim/BUILD.gn
@@ -1,6 +1,7 @@
# Copyright 2015 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.
+import("//extensions/buildflags/buildflags.gni")
# This is the part of the Chrome browser process responsible for launching and
# communicating with app_shim processes on Mac.
@@ -14,8 +15,6 @@ source_set("app_shim") {
"app_shim_host_manager_mac.mm",
"apps_page_shim_handler.h",
"apps_page_shim_handler.mm",
- "extension_app_shim_handler_mac.cc",
- "extension_app_shim_handler_mac.h",
"unix_domain_socket_acceptor.cc",
"unix_domain_socket_acceptor.h",
]
@@ -24,7 +23,16 @@ source_set("app_shim") {
"//chrome/common:mojo_bindings",
"//content/public/browser",
"//content/public/common",
- "//extensions/browser",
- "//extensions/common",
]
+
+ if (enable_extensions) {
+ sources += [
+ "extension_app_shim_handler_mac.cc",
+ "extension_app_shim_handler_mac.h",
+ ]
+ deps += [
+ "//extensions/browser",
+ "//extensions/common",
+ ]
+ }
}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 1b14d628a63b..00401612c191 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2570,7 +2570,10 @@ split_static_library("ui") {
"views/tabs/window_finder_mac.mm",
]
- deps += [ "//extensions/components/native_app_window" ]
+
+ if (enable_extensions) {
+ deps += [ "//extensions/components/native_app_window" ]
+ }
# Truly cocoa-browser-specific sources. These are secondary UI pieces that
# are obsolete before mac_views_browser will ever ship, so they aren't
--
2.17.0

View file

@ -0,0 +1,164 @@
diff --git a/chrome/browser/speech/tts_controller_impl.cc b/chrome/browser/speech/tts_controller_impl.cc
index 2ca56c3a247e..f58e33b3c019 100644
--- a/chrome/browser/speech/tts_controller_impl.cc
+++ b/chrome/browser/speech/tts_controller_impl.cc
@@ -624,12 +624,14 @@ const PrefService* TtsControllerImpl::GetPrefService(
const Utterance* utterance) {
const PrefService* prefs = nullptr;
// The utterance->browser_context() is null in tests.
+#if 0
if (utterance->browser_context()) {
const Profile* profile =
Profile::FromBrowserContext(utterance->browser_context());
if (profile)
prefs = profile->GetPrefs();
}
+#endif
return prefs;
}
diff --git a/chrome/browser/speech/tts_message_filter.cc b/chrome/browser/speech/tts_message_filter.cc
index 013c7a9c60f9..73a244a726e3 100644
--- a/chrome/browser/speech/tts_message_filter.cc
+++ b/chrome/browser/speech/tts_message_filter.cc
@@ -9,14 +9,40 @@
#include "base/bind.h"
#include "base/logging.h"
#include "chrome/browser/chrome_notification_types.h"
+#if 0
#include "chrome/browser/profiles/profile.h"
+#endif
#include "chrome/common/tts_messages.h"
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
using content::BrowserThread;
+namespace {
+
+class TtsMessageFilterShutdownNotifierFactory
+ : public BrowserContextKeyedServiceShutdownNotifierFactory {
+ public:
+ static TtsMessageFilterShutdownNotifierFactory* GetInstance() {
+ return base::Singleton<TtsMessageFilterShutdownNotifierFactory>::get();
+ }
+
+ private:
+ friend struct base::DefaultSingletonTraits<
+ TtsMessageFilterShutdownNotifierFactory>;
+
+ TtsMessageFilterShutdownNotifierFactory()
+ : BrowserContextKeyedServiceShutdownNotifierFactory("TtsMessageFilter") {}
+
+ ~TtsMessageFilterShutdownNotifierFactory() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(TtsMessageFilterShutdownNotifierFactory);
+};
+
+} // namespace
+
TtsMessageFilter::TtsMessageFilter(content::BrowserContext* browser_context)
: BrowserMessageFilter(TtsMsgStart),
browser_context_(browser_context),
@@ -24,28 +50,27 @@ TtsMessageFilter::TtsMessageFilter(content::BrowserContext* browser_context)
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TtsController::GetInstance()->AddVoicesChangedDelegate(this);
- // TODO(dmazzoni): make it so that we can listen for a BrowserContext
- // being destroyed rather than a Profile. http://crbug.com/444668
- Profile* profile = Profile::FromBrowserContext(browser_context);
- notification_registrar_.Add(this,
- chrome::NOTIFICATION_PROFILE_DESTROYED,
- content::Source<Profile>(profile));
+ browser_context_shutdown_notifier_ =
+ TtsMessageFilterShutdownNotifierFactory::GetInstance()
+ ->Get(browser_context)
+ ->Subscribe(base::Bind(&TtsMessageFilter::BrowserContextDestroyed,
+ base::RetainedRef(this)));
// Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero
// until all pointers to this class are invalidated.
AddRef();
}
-void TtsMessageFilter::OverrideThreadForMessage(
- const IPC::Message& message, BrowserThread::ID* thread) {
+void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread) {
switch (message.type()) {
- case TtsHostMsg_InitializeVoiceList::ID:
- case TtsHostMsg_Speak::ID:
- case TtsHostMsg_Pause::ID:
- case TtsHostMsg_Resume::ID:
- case TtsHostMsg_Cancel::ID:
- *thread = BrowserThread::UI;
- break;
+ case TtsHostMsg_InitializeVoiceList::ID:
+ case TtsHostMsg_Speak::ID:
+ case TtsHostMsg_Pause::ID:
+ case TtsHostMsg_Resume::ID:
+ case TtsHostMsg_Cancel::ID:
+ *thread = BrowserThread::UI;
+ break;
}
}
@@ -207,10 +232,8 @@ void TtsMessageFilter::Cleanup() {
TtsController::GetInstance()->RemoveUtteranceEventDelegate(this);
}
-void TtsMessageFilter::Observe(
- int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
+void TtsMessageFilter::BrowserContextDestroyed() {
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
browser_context_ = nullptr;
- notification_registrar_.RemoveAll();
+ browser_context_shutdown_notifier_.reset();
}
diff --git a/chrome/browser/speech/tts_message_filter.h b/chrome/browser/speech/tts_message_filter.h
index cc9e2806b5c3..d21fb42f1aca 100644
--- a/chrome/browser/speech/tts_message_filter.h
+++ b/chrome/browser/speech/tts_message_filter.h
@@ -9,10 +9,9 @@
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/speech/tts_controller.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
namespace content {
class BrowserContext;
@@ -22,7 +21,6 @@ struct TtsUtteranceRequest;
class TtsMessageFilter
: public content::BrowserMessageFilter,
- public content::NotificationObserver,
public UtteranceEventDelegate,
public VoicesChangedDelegate {
public:
@@ -64,15 +62,13 @@ class TtsMessageFilter
// about to be deleted.
bool Valid();
- // content::NotificationObserver implementation.
- void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) override;
+ void BrowserContextDestroyed();
+ std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
+ browser_context_shutdown_notifier_;
content::BrowserContext* browser_context_;
mutable base::Lock mutex_;
mutable bool valid_;
- content::NotificationRegistrar notification_registrar_;
DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter);
};

View file

@ -1,4 +1,6 @@
const assert = require('assert') const assert = require('assert')
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const fs = require('fs') const fs = require('fs')
const http = require('http') const http = require('http')
const path = require('path') const path = require('path')
@ -12,6 +14,9 @@ const { app, BrowserWindow, ipcMain, protocol, session, webContents } = remote
const isCI = remote.getGlobal('isCi') const isCI = remote.getGlobal('isCi')
const features = process.atomBinding('features') const features = process.atomBinding('features')
const { expect } = chai
chai.use(dirtyChai)
/* Most of the APIs here don't use standard callbacks */ /* Most of the APIs here don't use standard callbacks */
/* eslint-disable standard/no-callback-literal */ /* eslint-disable standard/no-callback-literal */
@ -1269,4 +1274,41 @@ describe('chromium feature', () => {
}) })
}) })
}) })
describe('SpeechSynthesis', () => {
before(function () {
if (isCI || !features.isTtsEnabled()) {
this.skip()
}
})
it('should emit lifecycle events', async () => {
const sentence = `long sentence which will take at least a few seconds to
utter so that it's possible to pause and resume before the end`
const utter = new SpeechSynthesisUtterance(sentence)
// Create a dummy utterence so that speech synthesis state
// is initialized for later calls.
speechSynthesis.speak(new SpeechSynthesisUtterance())
speechSynthesis.cancel()
speechSynthesis.speak(utter)
// paused state after speak()
expect(speechSynthesis.paused).to.be.false()
await new Promise((resolve) => { utter.onstart = resolve })
// paused state after start event
expect(speechSynthesis.paused).to.be.false()
speechSynthesis.pause()
// paused state changes async, right before the pause event
expect(speechSynthesis.paused).to.be.false()
await new Promise((resolve) => { utter.onpause = resolve })
expect(speechSynthesis.paused).to.be.true()
speechSynthesis.resume()
await new Promise((resolve) => { utter.onresume = resolve })
// paused state after resume event
expect(speechSynthesis.paused).to.be.false()
await new Promise((resolve) => { utter.onend = resolve })
})
})
}) })