mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
172 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b5dd2ed4b | ||
|
|
85afa851dd | ||
|
|
57acdc1bf6 | ||
|
|
ae76657e17 | ||
|
|
4ca6ac34ac | ||
|
|
e4a71b86df | ||
|
|
4dd7848084 | ||
|
|
a95679c212 | ||
|
|
b41d356143 | ||
|
|
feb6a93881 | ||
|
|
940b40439f | ||
|
|
ed07bd202f | ||
|
|
8c29ffd084 | ||
|
|
dba2fa31b6 | ||
|
|
2de80571d8 | ||
|
|
51acba594b | ||
|
|
a194d45dfa | ||
|
|
9356296a84 | ||
|
|
2fa0b1117c | ||
|
|
49814e1919 | ||
|
|
74da83a0bb | ||
|
|
643d1dcdd1 | ||
|
|
b717add81b | ||
|
|
c36c4e36c5 | ||
|
|
0b48c3ea90 | ||
|
|
68e28159bb | ||
|
|
69a89303d0 | ||
|
|
9e87037d34 | ||
|
|
7cd4d35778 | ||
|
|
8296178e33 | ||
|
|
32dff999a5 | ||
|
|
13e5cf32d5 | ||
|
|
11d9e522d5 | ||
|
|
4aac0d6d1c | ||
|
|
ddf4f14dba | ||
|
|
b560176aeb | ||
|
|
802f964cd3 | ||
|
|
0c349c047d | ||
|
|
d4e3c39fa5 | ||
|
|
3a177d55f8 | ||
|
|
b2741a8316 | ||
|
|
909f1bcf3c | ||
|
|
009412d738 | ||
|
|
33c622ec86 | ||
|
|
05b602d0ce | ||
|
|
8519ea3299 | ||
|
|
aad46b0894 | ||
|
|
5dfbaebd4c | ||
|
|
b2b70bb37c | ||
|
|
35d37c3463 | ||
|
|
eea82efbf8 | ||
|
|
7659edd139 | ||
|
|
9f8a5a7af3 | ||
|
|
cc4897f8c1 | ||
|
|
ee5335ca6e | ||
|
|
dde8e47add | ||
|
|
0040f07097 | ||
|
|
64b2c9b36c | ||
|
|
d754b2bda7 | ||
|
|
9dd68c7add | ||
|
|
1499d44584 | ||
|
|
039903c6b2 | ||
|
|
5df1716144 | ||
|
|
f6d6a12c1a | ||
|
|
e316e4a267 | ||
|
|
9d84f139eb | ||
|
|
ad70cb27bd | ||
|
|
98fec2f317 | ||
|
|
72fc1e8dc6 | ||
|
|
301014e4a6 | ||
|
|
927ec6ab7a | ||
|
|
37d45e2881 | ||
|
|
915c1b19d3 | ||
|
|
b87dfb964c | ||
|
|
885ac53a48 | ||
|
|
d77bf0440c | ||
|
|
5a0be6672d | ||
|
|
724cae7de1 | ||
|
|
6c9769999b | ||
|
|
2bf2ad094c | ||
|
|
3eaf0fe82b | ||
|
|
013e7fb611 | ||
|
|
e24976c59f | ||
|
|
e3ae062c5c | ||
|
|
5e6e173d59 | ||
|
|
3fcd571db0 | ||
|
|
988fa73696 | ||
|
|
3c412e1cb8 | ||
|
|
a579f58454 | ||
|
|
35e867820e | ||
|
|
a757c62da5 | ||
|
|
370dd26745 | ||
|
|
e20697b870 | ||
|
|
4d01aa2772 | ||
|
|
4a485f9819 | ||
|
|
cebafeae40 | ||
|
|
150739e19e | ||
|
|
38f83cacf9 | ||
|
|
fc8ff314e2 | ||
|
|
8acf96d268 | ||
|
|
c49a44f944 | ||
|
|
390b804ca0 | ||
|
|
05317ad81e | ||
|
|
d559275711 | ||
|
|
909ff085ac | ||
|
|
dbbfef38b1 | ||
|
|
4006b6407c | ||
|
|
c95a93ef1c | ||
|
|
e5e1e207b6 | ||
|
|
e0c469183d | ||
|
|
4d2e4ed573 | ||
|
|
0cab034dab | ||
|
|
9f9d209e3d | ||
|
|
8740147aa2 | ||
|
|
9b755620d3 | ||
|
|
fa287c2422 | ||
|
|
b6cded379e | ||
|
|
8199ad2ae6 | ||
|
|
0d09143a77 | ||
|
|
7081f7799b | ||
|
|
b6583635d4 | ||
|
|
b01db4aa09 | ||
|
|
6d712da7e3 | ||
|
|
9b71117171 | ||
|
|
50ea0f0b45 | ||
|
|
fa8e158587 | ||
|
|
2768b1ff64 | ||
|
|
b3770bc407 | ||
|
|
8f44046f9a | ||
|
|
a717235212 | ||
|
|
805215be78 | ||
|
|
e7fbe84644 | ||
|
|
9653f20995 | ||
|
|
e959a40b49 | ||
|
|
d9ce3f0ca3 | ||
|
|
d8f57a0ecc | ||
|
|
c5e0b65cc7 | ||
|
|
b5e82dac6f | ||
|
|
1381d16f9c | ||
|
|
268508764f | ||
|
|
34109fa741 | ||
|
|
925ff2da5b | ||
|
|
b8a6658ba9 | ||
|
|
4a4814b41c | ||
|
|
f952dae0d0 | ||
|
|
cba155bcfb | ||
|
|
0f714c81cd | ||
|
|
92b5dab3f9 | ||
|
|
6ca238852a | ||
|
|
d2368d2d3b | ||
|
|
88269a613a | ||
|
|
5696fe8ec8 | ||
|
|
ba439b6824 | ||
|
|
c8a8576970 | ||
|
|
67cbecaba0 | ||
|
|
ec1db0c7bb | ||
|
|
4330d67e0d | ||
|
|
db8de9e60d | ||
|
|
9c9a306095 | ||
|
|
bda317b000 | ||
|
|
700510d63a | ||
|
|
ab2714fda9 | ||
|
|
33b94edcf0 | ||
|
|
44d3e58ddb | ||
|
|
f08c3f9134 | ||
|
|
8de90db429 | ||
|
|
81241b38eb | ||
|
|
1c07b9c85b | ||
|
|
1199224086 | ||
|
|
add4e3c6f5 | ||
|
|
eb55f1cf47 | ||
|
|
8367071dc6 |
60
atom.gyp
60
atom.gyp
@@ -39,6 +39,7 @@
|
||||
'atom/common/api/lib/screen.coffee',
|
||||
'atom/common/api/lib/shell.coffee',
|
||||
'atom/common/lib/init.coffee',
|
||||
'atom/common/lib/asar.coffee',
|
||||
'atom/renderer/lib/chrome-api.coffee',
|
||||
'atom/renderer/lib/init.coffee',
|
||||
'atom/renderer/lib/inspector.coffee',
|
||||
@@ -100,6 +101,8 @@
|
||||
'atom/browser/atom_javascript_dialog_manager.h',
|
||||
'atom/browser/atom_resource_dispatcher_host_delegate.cc',
|
||||
'atom/browser/atom_resource_dispatcher_host_delegate.h',
|
||||
'atom/browser/atom_speech_recognition_manager_delegate.cc',
|
||||
'atom/browser/atom_speech_recognition_manager_delegate.h',
|
||||
'atom/browser/browser.cc',
|
||||
'atom/browser/browser.h',
|
||||
'atom/browser/browser_linux.cc',
|
||||
@@ -121,6 +124,10 @@
|
||||
'atom/browser/native_window_observer.h',
|
||||
'atom/browser/net/adapter_request_job.cc',
|
||||
'atom/browser/net/adapter_request_job.h',
|
||||
'atom/browser/net/asar/asar_protocol_handler.cc',
|
||||
'atom/browser/net/asar/asar_protocol_handler.h',
|
||||
'atom/browser/net/asar/url_request_asar_job.cc',
|
||||
'atom/browser/net/asar/url_request_asar_job.h',
|
||||
'atom/browser/net/atom_url_request_job_factory.cc',
|
||||
'atom/browser/net/atom_url_request_job_factory.h',
|
||||
'atom/browser/net/url_request_string_job.cc',
|
||||
@@ -174,6 +181,7 @@
|
||||
'atom/browser/window_list.h',
|
||||
'atom/browser/window_list_observer.h',
|
||||
'atom/common/api/api_messages.h',
|
||||
'atom/common/api/atom_api_asar.cc',
|
||||
'atom/common/api/atom_api_clipboard.cc',
|
||||
'atom/common/api/atom_api_crash_reporter.cc',
|
||||
'atom/common/api/atom_api_id_weak_map.cc',
|
||||
@@ -186,6 +194,10 @@
|
||||
'atom/common/api/atom_bindings.h',
|
||||
'atom/common/api/object_life_monitor.cc',
|
||||
'atom/common/api/object_life_monitor.h',
|
||||
'atom/common/asar/archive.cc',
|
||||
'atom/common/asar/archive.h',
|
||||
'atom/common/asar/scoped_temporary_file.cc',
|
||||
'atom/common/asar/scoped_temporary_file.h',
|
||||
'atom/common/common_message_generator.cc',
|
||||
'atom/common/common_message_generator.h',
|
||||
'atom/common/crash_reporter/crash_reporter.cc',
|
||||
@@ -204,6 +216,7 @@
|
||||
'atom/common/crash_reporter/win/crash_service_main.h',
|
||||
'atom/common/draggable_region.cc',
|
||||
'atom/common/draggable_region.h',
|
||||
'atom/common/google_api_key.h',
|
||||
'atom/common/linux/application_info.cc',
|
||||
'atom/common/native_mate_converters/accelerator_converter.cc',
|
||||
'atom/common/native_mate_converters/accelerator_converter.h',
|
||||
@@ -269,6 +282,16 @@
|
||||
'chromium_src/chrome/browser/printing/printing_message_filter.h',
|
||||
'chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.cc',
|
||||
'chromium_src/chrome/browser/printing/printing_ui_web_contents_observer.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/libgtk2ui/app_indicator_icon_menu.cc',
|
||||
'chromium_src/chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h',
|
||||
'chromium_src/chrome/browser/ui/libgtk2ui/gtk2_status_icon.cc',
|
||||
@@ -279,11 +302,17 @@
|
||||
'chromium_src/chrome/browser/ui/views/status_icons/status_tray_state_changer_win.h',
|
||||
'chromium_src/chrome/common/print_messages.cc',
|
||||
'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_linux.cc',
|
||||
'chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm',
|
||||
'chromium_src/chrome/renderer/printing/print_web_view_helper_win.cc',
|
||||
'chromium_src/chrome/renderer/printing/print_web_view_helper.h',
|
||||
'chromium_src/chrome/renderer/tts_dispatcher.cc',
|
||||
'chromium_src/chrome/renderer/tts_dispatcher.h',
|
||||
'chromium_src/library_loaders/libspeechd_loader.cc',
|
||||
'<@(native_mate_files)',
|
||||
],
|
||||
'framework_sources': [
|
||||
@@ -539,6 +568,7 @@
|
||||
'vendor/breakpad/src',
|
||||
],
|
||||
'cflags': [
|
||||
'-Wno-deprecated-register',
|
||||
'-Wno-empty-body',
|
||||
],
|
||||
'dependencies': [
|
||||
@@ -688,6 +718,36 @@
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}, # target <(project_name>_dump_symbols
|
||||
{
|
||||
'target_name': 'copy_chromedriver',
|
||||
'type': 'none',
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Copy ChromeDriver Binary',
|
||||
'variables': {
|
||||
'conditions': [
|
||||
['OS=="win"', {
|
||||
'chromedriver_binary': 'chromedriver.exe',
|
||||
},{
|
||||
'chromedriver_binary': 'chromedriver',
|
||||
}],
|
||||
],
|
||||
},
|
||||
'inputs': [
|
||||
'<(libchromiumcontent_library_dir)/<(chromedriver_binary)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/<(chromedriver_binary)',
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/copy_binary.py',
|
||||
'<@(_inputs)',
|
||||
'<@(_outputs)',
|
||||
],
|
||||
}
|
||||
],
|
||||
}, # copy_chromedriver
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/chrome_version.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomContentClient::AtomContentClient() {
|
||||
@@ -15,6 +17,10 @@ AtomContentClient::AtomContentClient() {
|
||||
AtomContentClient::~AtomContentClient() {
|
||||
}
|
||||
|
||||
std::string AtomContentClient::GetProduct() const {
|
||||
return "Chrome/" CHROME_VERSION_STRING;
|
||||
}
|
||||
|
||||
void AtomContentClient::AddAdditionalSchemes(
|
||||
std::vector<std::string>* standard_schemes,
|
||||
std::vector<std::string>* savable_schemes) {
|
||||
|
||||
@@ -19,6 +19,7 @@ class AtomContentClient : public brightray::ContentClient {
|
||||
|
||||
protected:
|
||||
// content::ContentClient:
|
||||
virtual std::string GetProduct() const OVERRIDE;
|
||||
virtual void AddAdditionalSchemes(
|
||||
std::vector<std::string>* standard_schemes,
|
||||
std::vector<std::string>* savable_schemes) OVERRIDE;
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#include "atom/app/atom_content_client.h"
|
||||
#include "atom/browser/atom_browser_client.h"
|
||||
#include "atom/common/google_api_key.h"
|
||||
#include "atom/renderer/atom_renderer_client.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/environment.h"
|
||||
#include "base/logging.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
@@ -52,6 +54,11 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
|
||||
void AtomMainDelegate::PreSandboxStartup() {
|
||||
brightray::MainDelegate::PreSandboxStartup();
|
||||
|
||||
// Set google API key.
|
||||
scoped_ptr<base::Environment> env(base::Environment::Create());
|
||||
if (!env->HasVar("GOOGLE_API_KEY"))
|
||||
env->SetVar("GOOGLE_API_KEY", GOOGLEAPIS_API_KEY);
|
||||
|
||||
CommandLine* command_line = CommandLine::ForCurrentProcess();
|
||||
std::string process_type = command_line->GetSwitchValueASCII(
|
||||
switches::kProcessType);
|
||||
@@ -73,6 +80,11 @@ void AtomMainDelegate::PreSandboxStartup() {
|
||||
// Disable renderer sandbox for most of node's functions.
|
||||
command_line->AppendSwitch(switches::kNoSandbox);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// Enable AVFoundation.
|
||||
command_line->AppendSwitch("enable-avfoundation");
|
||||
#endif
|
||||
|
||||
// Add a flag to mark the end of switches added by atom-shell.
|
||||
command_line->AppendSwitch("atom-shell-switches-end");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "native_mate/callback.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/proxy/proxy_service.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
@@ -46,10 +47,10 @@ class ResolveProxyHelper {
|
||||
|
||||
// Start the request.
|
||||
int result = proxy_service->ResolveProxy(
|
||||
url, &proxy_info_,
|
||||
url, net::LOAD_NORMAL, &proxy_info_,
|
||||
base::Bind(&ResolveProxyHelper::OnResolveProxyCompleted,
|
||||
base::Unretained(this)),
|
||||
&pac_req_, net::BoundNetLog());
|
||||
&pac_req_, nullptr, net::BoundNetLog());
|
||||
|
||||
// Completed synchronously.
|
||||
if (result != net::ERR_IO_PENDING)
|
||||
@@ -91,6 +92,10 @@ void App::OnWindowAllClosed() {
|
||||
Emit("window-all-closed");
|
||||
}
|
||||
|
||||
void App::OnQuit() {
|
||||
Emit("quit");
|
||||
}
|
||||
|
||||
void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
|
||||
base::ListValue args;
|
||||
args.AppendString(file_path);
|
||||
@@ -134,6 +139,13 @@ void App::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
||||
new ResolveProxyHelper(url, callback);
|
||||
}
|
||||
|
||||
void App::SetDesktopName(const std::string& desktop_name) {
|
||||
#if defined(OS_LINUX)
|
||||
scoped_ptr<base::Environment> env(base::Environment::Create());
|
||||
env->SetVar("CHROME_DESKTOP", desktop_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
Browser* browser = Browser::Get();
|
||||
@@ -151,7 +163,8 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
|
||||
.SetMethod("setName", base::Bind(&Browser::SetName,
|
||||
base::Unretained(browser)))
|
||||
.SetMethod("getDataPath", &App::GetDataPath)
|
||||
.SetMethod("resolveProxy", &App::ResolveProxy);
|
||||
.SetMethod("resolveProxy", &App::ResolveProxy)
|
||||
.SetMethod("setDesktopName", &App::SetDesktopName);
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -36,6 +36,7 @@ class App : public mate::EventEmitter,
|
||||
// BrowserObserver implementations:
|
||||
virtual void OnWillQuit(bool* prevent_default) OVERRIDE;
|
||||
virtual void OnWindowAllClosed() OVERRIDE;
|
||||
virtual void OnQuit() OVERRIDE;
|
||||
virtual void OnOpenFile(bool* prevent_default,
|
||||
const std::string& file_path) OVERRIDE;
|
||||
virtual void OnOpenURL(const std::string& url) OVERRIDE;
|
||||
@@ -50,6 +51,7 @@ class App : public mate::EventEmitter,
|
||||
private:
|
||||
base::FilePath GetDataPath();
|
||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||
void SetDesktopName(const std::string& desktop_name);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(App);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "base/bind.h"
|
||||
@@ -31,17 +32,30 @@ struct Converter<std::set<T> > {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<TracingController::Options> {
|
||||
struct Converter<base::debug::CategoryFilter> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val,
|
||||
TracingController::Options* out) {
|
||||
if (!val->IsNumber())
|
||||
base::debug::CategoryFilter* out) {
|
||||
std::string filter;
|
||||
if (!ConvertFromV8(isolate, val, &filter))
|
||||
return false;
|
||||
*out = static_cast<TracingController::Options>(val->IntegerValue());
|
||||
*out = base::debug::CategoryFilter(filter);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<base::debug::TraceOptions> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val,
|
||||
base::debug::TraceOptions* out) {
|
||||
std::string options;
|
||||
if (!ConvertFromV8(isolate, val, &options))
|
||||
return false;
|
||||
return out->SetFromString(options);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -23,7 +23,7 @@ struct Converter<file_dialog::Filter> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val,
|
||||
file_dialog::Filter* out) {
|
||||
mate::Dictionary dict(isolate);
|
||||
mate::Dictionary dict;
|
||||
if (!ConvertFromV8(isolate, val, &dict))
|
||||
return false;
|
||||
if (!dict.Get("name", &(out->first)))
|
||||
|
||||
@@ -17,14 +17,15 @@ MenuViews::MenuViews() {
|
||||
|
||||
void MenuViews::Popup(Window* window) {
|
||||
gfx::Point cursor = gfx::Screen::GetNativeScreen()->GetCursorScreenPoint();
|
||||
views::MenuRunner menu_runner(model());
|
||||
views::MenuRunner menu_runner(
|
||||
model(),
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
static_cast<NativeWindowViews*>(window->window())->widget(),
|
||||
NULL,
|
||||
gfx::Rect(cursor, gfx::Size()),
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE,
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
|
||||
ui::MENU_SOURCE_MOUSE));
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -36,6 +36,10 @@ void Tray::OnClicked() {
|
||||
Emit("clicked");
|
||||
}
|
||||
|
||||
void Tray::OnDoubleClicked() {
|
||||
Emit("double-clicked");
|
||||
}
|
||||
|
||||
void Tray::SetImage(const gfx::ImageSkia& image) {
|
||||
tray_icon_->SetImage(image);
|
||||
}
|
||||
@@ -48,6 +52,14 @@ void Tray::SetToolTip(const std::string& tool_tip) {
|
||||
tray_icon_->SetToolTip(tool_tip);
|
||||
}
|
||||
|
||||
void Tray::SetTitle(const std::string& title) {
|
||||
tray_icon_->SetTitle(title);
|
||||
}
|
||||
|
||||
void Tray::SetHighlightMode(bool highlight) {
|
||||
tray_icon_->SetHighlightMode(highlight);
|
||||
}
|
||||
|
||||
void Tray::SetContextMenu(Menu* menu) {
|
||||
tray_icon_->SetContextMenu(menu->model());
|
||||
}
|
||||
@@ -59,6 +71,8 @@ void Tray::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("setImage", &Tray::SetImage)
|
||||
.SetMethod("setPressedImage", &Tray::SetPressedImage)
|
||||
.SetMethod("setToolTip", &Tray::SetToolTip)
|
||||
.SetMethod("setTitle", &Tray::SetTitle)
|
||||
.SetMethod("setHighlightMode", &Tray::SetHighlightMode)
|
||||
.SetMethod("_setContextMenu", &Tray::SetContextMenu);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,13 @@ class Tray : public mate::EventEmitter,
|
||||
|
||||
// TrayIcon implementations:
|
||||
virtual void OnClicked() OVERRIDE;
|
||||
virtual void OnDoubleClicked() OVERRIDE;
|
||||
|
||||
void SetImage(const gfx::ImageSkia& image);
|
||||
void SetPressedImage(const gfx::ImageSkia& image);
|
||||
void SetToolTip(const std::string& tool_tip);
|
||||
void SetTitle(const std::string& title);
|
||||
void SetHighlightMode(bool highlight);
|
||||
void SetContextMenu(Menu* menu);
|
||||
|
||||
private:
|
||||
|
||||
@@ -35,10 +35,10 @@ void WebContents::RenderProcessGone(base::TerminationStatus status) {
|
||||
Emit("crashed");
|
||||
}
|
||||
|
||||
void WebContents::DidFinishLoad(int64 frame_id,
|
||||
const GURL& validated_url,
|
||||
bool is_main_frame,
|
||||
content::RenderViewHost* render_view_host) {
|
||||
void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
const GURL& validated_url) {
|
||||
bool is_main_frame = !render_frame_host->GetParent();
|
||||
|
||||
base::ListValue args;
|
||||
args.AppendBoolean(is_main_frame);
|
||||
Emit("did-frame-finish-load", args);
|
||||
|
||||
@@ -52,11 +52,8 @@ class WebContents : public mate::EventEmitter,
|
||||
// content::WebContentsObserver implementations:
|
||||
virtual void RenderViewDeleted(content::RenderViewHost*) OVERRIDE;
|
||||
virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE;
|
||||
virtual void DidFinishLoad(
|
||||
int64 frame_id,
|
||||
const GURL& validated_url,
|
||||
bool is_main_frame,
|
||||
content::RenderViewHost* render_view_host) OVERRIDE;
|
||||
virtual void DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
const GURL& validated_url) OVERRIDE;
|
||||
virtual void DidStartLoading(
|
||||
content::RenderViewHost* render_view_host) OVERRIDE;
|
||||
virtual void DidStopLoading(
|
||||
|
||||
@@ -366,6 +366,10 @@ void Window::Print(mate::Arguments* args) {
|
||||
window_->Print(settings.silent, settings.print_backgournd);
|
||||
}
|
||||
|
||||
void Window::SetProgressBar(double progress) {
|
||||
window_->SetProgressBar(progress);
|
||||
}
|
||||
|
||||
mate::Handle<WebContents> Window::GetWebContents(v8::Isolate* isolate) const {
|
||||
return WebContents::Create(isolate, window_->GetWebContents());
|
||||
}
|
||||
@@ -428,6 +432,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
||||
.SetMethod("isWebViewFocused", &Window::IsWebViewFocused)
|
||||
.SetMethod("capturePage", &Window::CapturePage)
|
||||
.SetMethod("print", &Window::Print)
|
||||
.SetMethod("setProgressBar", &Window::SetProgressBar)
|
||||
.SetMethod("_getWebContents", &Window::GetWebContents)
|
||||
.SetMethod("_getDevToolsWebContents", &Window::GetDevToolsWebContents);
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ class Window : public mate::EventEmitter,
|
||||
bool IsDocumentEdited();
|
||||
void CapturePage(mate::Arguments* args);
|
||||
void Print(mate::Arguments* args);
|
||||
void SetProgressBar(double progress);
|
||||
|
||||
// APIs for WebContents.
|
||||
mate::Handle<WebContents> GetWebContents(v8::Isolate* isolate) const;
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace {
|
||||
|
||||
v8::Persistent<v8::ObjectTemplate> template_;
|
||||
|
||||
} // namespace
|
||||
|
||||
Event::Event()
|
||||
: sender_(NULL),
|
||||
message_(NULL),
|
||||
@@ -21,9 +27,14 @@ Event::~Event() {
|
||||
}
|
||||
|
||||
ObjectTemplateBuilder Event::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||
return ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("preventDefault", &Event::PreventDefault)
|
||||
.SetMethod("sendReply", &Event::SendReply);
|
||||
if (template_.IsEmpty())
|
||||
template_.Reset(isolate, ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("preventDefault", &Event::PreventDefault)
|
||||
.SetMethod("sendReply", &Event::SendReply)
|
||||
.Build());
|
||||
|
||||
return ObjectTemplateBuilder(
|
||||
isolate, v8::Local<v8::ObjectTemplate>::New(isolate, template_));
|
||||
}
|
||||
|
||||
void Event::SetSenderAndMessage(content::WebContents* sender,
|
||||
|
||||
@@ -10,11 +10,37 @@
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "native_mate/arguments.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace {
|
||||
|
||||
v8::Persistent<v8::ObjectTemplate> event_template;
|
||||
|
||||
void PreventDefault(mate::Arguments* args) {
|
||||
args->GetThis()->SetHiddenValue(
|
||||
StringToV8(args->isolate(), "prevent_default"),
|
||||
v8::True(args->isolate()));
|
||||
}
|
||||
|
||||
// Create a pure JavaScript Event object.
|
||||
v8::Local<v8::Object> CreateEventObject(v8::Isolate* isolate) {
|
||||
if (event_template.IsEmpty()) {
|
||||
event_template.Reset(isolate, ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("preventDefault", &PreventDefault)
|
||||
.Build());
|
||||
}
|
||||
|
||||
return v8::Local<v8::ObjectTemplate>::New(
|
||||
isolate, event_template)->NewInstance();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EventEmitter::EventEmitter() {
|
||||
}
|
||||
|
||||
@@ -38,15 +64,22 @@ bool EventEmitter::Emit(const base::StringPiece& name,
|
||||
v8::Handle<v8::Context> context = isolate->GetCurrentContext();
|
||||
scoped_ptr<atom::V8ValueConverter> converter(new atom::V8ValueConverter);
|
||||
|
||||
mate::Handle<mate::Event> event = mate::Event::Create(isolate);
|
||||
if (sender && message)
|
||||
event->SetSenderAndMessage(sender, message);
|
||||
v8::Handle<v8::Object> event;
|
||||
bool use_native_event = sender && message;
|
||||
|
||||
if (use_native_event) {
|
||||
mate::Handle<mate::Event> native_event = mate::Event::Create(isolate);
|
||||
native_event->SetSenderAndMessage(sender, message);
|
||||
event = v8::Handle<v8::Object>::Cast(native_event.ToV8());
|
||||
} else {
|
||||
event = CreateEventObject(isolate);
|
||||
}
|
||||
|
||||
// v8_args = [name, event, args...];
|
||||
std::vector<v8::Handle<v8::Value>> v8_args;
|
||||
v8_args.reserve(args.GetSize() + 2);
|
||||
v8_args.push_back(mate::StringToV8(isolate, name));
|
||||
v8_args.push_back(event.ToV8());
|
||||
v8_args.push_back(event);
|
||||
for (size_t i = 0; i < args.GetSize(); i++) {
|
||||
const base::Value* value(NULL);
|
||||
if (args.Get(i, &value))
|
||||
@@ -57,7 +90,17 @@ bool EventEmitter::Emit(const base::StringPiece& name,
|
||||
node::MakeCallback(isolate, GetWrapper(isolate), "emit", v8_args.size(),
|
||||
&v8_args[0]);
|
||||
|
||||
return event->prevent_default();
|
||||
if (use_native_event) {
|
||||
Handle<Event> native_event;
|
||||
if (ConvertFromV8(isolate, event, &native_event))
|
||||
return native_event->prevent_default();
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> prevent_default =
|
||||
event->GetHiddenValue(StringToSymbol(isolate, "prevent_default"));
|
||||
if (prevent_default.IsEmpty())
|
||||
return false;
|
||||
return prevent_default->BooleanValue();
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
||||
@@ -51,7 +51,7 @@ module.exports =
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
showSaveDialog: (window, options, callback) ->
|
||||
showSaveDialog: (args...) ->
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= title: 'Save'
|
||||
@@ -71,7 +71,7 @@ module.exports =
|
||||
window,
|
||||
wrappedCallback
|
||||
|
||||
showMessageBox: (window, options, callback) ->
|
||||
showMessageBox: (args...) ->
|
||||
[window, options, callback] = parseArgs args...
|
||||
|
||||
options ?= type: 'none'
|
||||
|
||||
@@ -7,10 +7,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
|
||||
#ifndef GOOGLEAPIS_API_KEY
|
||||
#define GOOGLEAPIS_API_KEY "AIzaSyAQfxPJiounkhOjODEO5ZieffeBv6yft2Q"
|
||||
#endif
|
||||
#include "atom/common/google_api_key.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
|
||||
@@ -8,16 +8,18 @@
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
|
||||
#include "atom/browser/atom_speech_recognition_manager_delegate.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/window_list.h"
|
||||
#include "chrome/browser/printing/printing_message_filter.h"
|
||||
#include "chrome/browser/speech/tts_message_filter.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/resource_dispatcher_host.h"
|
||||
#include "content/public/browser/site_instance.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/web_preferences.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "webkit/common/webpreferences.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -51,7 +53,9 @@ AtomBrowserClient::~AtomBrowserClient() {
|
||||
|
||||
void AtomBrowserClient::RenderProcessWillLaunch(
|
||||
content::RenderProcessHost* host) {
|
||||
int id = host->GetID();
|
||||
host->AddFilter(new PrintingMessageFilter(host->GetID()));
|
||||
host->AddFilter(new TtsMessageFilter(id, host->GetBrowserContext()));
|
||||
}
|
||||
|
||||
void AtomBrowserClient::ResourceDispatcherHostCreated() {
|
||||
@@ -60,6 +64,11 @@ void AtomBrowserClient::ResourceDispatcherHostCreated() {
|
||||
resource_dispatcher_delegate_.get());
|
||||
}
|
||||
|
||||
content::SpeechRecognitionManagerDelegate*
|
||||
AtomBrowserClient::GetSpeechRecognitionManagerDelegate() {
|
||||
return new AtomSpeechRecognitionManagerDelegate;
|
||||
}
|
||||
|
||||
content::AccessTokenStore* AtomBrowserClient::CreateAccessTokenStore() {
|
||||
return new AtomAccessTokenStore;
|
||||
}
|
||||
@@ -67,7 +76,7 @@ content::AccessTokenStore* AtomBrowserClient::CreateAccessTokenStore() {
|
||||
void AtomBrowserClient::OverrideWebkitPrefs(
|
||||
content::RenderViewHost* render_view_host,
|
||||
const GURL& url,
|
||||
WebPreferences* prefs) {
|
||||
content::WebPreferences* prefs) {
|
||||
prefs->javascript_enabled = true;
|
||||
prefs->web_security_enabled = true;
|
||||
prefs->javascript_can_open_windows_automatically = true;
|
||||
|
||||
@@ -23,10 +23,12 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
||||
virtual void RenderProcessWillLaunch(
|
||||
content::RenderProcessHost* host) OVERRIDE;
|
||||
virtual void ResourceDispatcherHostCreated() OVERRIDE;
|
||||
virtual content::SpeechRecognitionManagerDelegate*
|
||||
GetSpeechRecognitionManagerDelegate() override;
|
||||
virtual content::AccessTokenStore* CreateAccessTokenStore() OVERRIDE;
|
||||
virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
|
||||
const GURL& url,
|
||||
WebPreferences* prefs) OVERRIDE;
|
||||
content::WebPreferences* prefs) OVERRIDE;
|
||||
virtual bool ShouldSwapBrowsingInstancesForNavigation(
|
||||
content::SiteInstance* site_instance,
|
||||
const GURL& current_url,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
#include "base/threading/sequenced_worker_pool.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
@@ -20,6 +21,12 @@ using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* kAsarScheme = "asar";
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomBrowserContext::AtomBrowserContext()
|
||||
: fake_browser_process_(new BrowserProcess),
|
||||
job_factory_(new AtomURLRequestJobFactory) {
|
||||
@@ -44,6 +51,10 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory(
|
||||
url::kFileScheme, new net::FileProtocolHandler(
|
||||
BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
|
||||
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
|
||||
job_factory->SetProtocolHandler(
|
||||
kAsarScheme, new asar::AsarProtocolHandler(
|
||||
BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
|
||||
base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)));
|
||||
|
||||
// Set up interceptors in the reverse order.
|
||||
scoped_ptr<net::URLRequestJobFactory> top_job_factory =
|
||||
|
||||
@@ -28,7 +28,8 @@ AtomBrowserMainParts* AtomBrowserMainParts::self_ = NULL;
|
||||
AtomBrowserMainParts::AtomBrowserMainParts()
|
||||
: browser_(new Browser),
|
||||
node_bindings_(NodeBindings::Create(true)),
|
||||
atom_bindings_(new AtomBindings) {
|
||||
atom_bindings_(new AtomBindings),
|
||||
gc_timer_(true, true) {
|
||||
DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";
|
||||
self_ = this;
|
||||
}
|
||||
@@ -75,6 +76,13 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||
node_bindings_->PrepareMessageLoop();
|
||||
node_bindings_->RunMessageLoop();
|
||||
|
||||
// Start idle gc.
|
||||
gc_timer_.Start(
|
||||
FROM_HERE, base::TimeDelta::FromMinutes(1),
|
||||
base::Bind(base::IgnoreResult(&v8::Isolate::IdleNotification),
|
||||
base::Unretained(js_env_->isolate()),
|
||||
1000));
|
||||
|
||||
brightray::BrowserMainParts::PreMainMessageLoopRun();
|
||||
|
||||
#if defined(USE_X11)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#ifndef ATOM_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_
|
||||
#define ATOM_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_
|
||||
|
||||
#include "base/timer/timer.h"
|
||||
#include "brightray/browser/browser_main_parts.h"
|
||||
|
||||
namespace atom {
|
||||
@@ -43,6 +44,8 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
||||
scoped_ptr<AtomBindings> atom_bindings_;
|
||||
scoped_ptr<NodeDebugger> node_debugger_;
|
||||
|
||||
base::Timer gc_timer_;
|
||||
|
||||
static AtomBrowserMainParts* self_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomBrowserMainParts);
|
||||
|
||||
74
atom/browser/atom_speech_recognition_manager_delegate.cc
Normal file
74
atom/browser/atom_speech_recognition_manager_delegate.cc
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/atom_speech_recognition_manager_delegate.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomSpeechRecognitionManagerDelegate::AtomSpeechRecognitionManagerDelegate() {
|
||||
}
|
||||
|
||||
AtomSpeechRecognitionManagerDelegate::~AtomSpeechRecognitionManagerDelegate() {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnRecognitionStart(int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete(
|
||||
int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnRecognitionResults(
|
||||
int session_id, const content::SpeechRecognitionResults& result) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnRecognitionError(
|
||||
int session_id, const content::SpeechRecognitionError& error) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::OnAudioLevelsChange(
|
||||
int session_id, float volume, float noise_volume) {
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::GetDiagnosticInformation(
|
||||
bool* can_report_metrics, std::string* hardware_info) {
|
||||
*can_report_metrics = false;
|
||||
}
|
||||
|
||||
void AtomSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed(
|
||||
int session_id,
|
||||
base::Callback<void(bool ask_user, bool is_allowed)> callback) {
|
||||
callback.Run(true, true);
|
||||
}
|
||||
|
||||
content::SpeechRecognitionEventListener*
|
||||
AtomSpeechRecognitionManagerDelegate::GetEventListener() {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool AtomSpeechRecognitionManagerDelegate::FilterProfanities(
|
||||
int render_process_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
52
atom/browser/atom_speech_recognition_manager_delegate.h
Normal file
52
atom/browser/atom_speech_recognition_manager_delegate.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_ATOM_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
|
||||
#define ATOM_BROWSER_ATOM_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "content/public/browser/speech_recognition_event_listener.h"
|
||||
#include "content/public/browser/speech_recognition_manager_delegate.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomSpeechRecognitionManagerDelegate
|
||||
: public content::SpeechRecognitionManagerDelegate,
|
||||
public content::SpeechRecognitionEventListener {
|
||||
public:
|
||||
AtomSpeechRecognitionManagerDelegate();
|
||||
virtual ~AtomSpeechRecognitionManagerDelegate();
|
||||
|
||||
// content::SpeechRecognitionEventListener:
|
||||
virtual void OnRecognitionStart(int session_id) override;
|
||||
virtual void OnAudioStart(int session_id) override;
|
||||
virtual void OnEnvironmentEstimationComplete(int session_id) override;
|
||||
virtual void OnSoundStart(int session_id) override;
|
||||
virtual void OnSoundEnd(int session_id) override;
|
||||
virtual void OnAudioEnd(int session_id) override;
|
||||
virtual void OnRecognitionEnd(int session_id) override;
|
||||
virtual void OnRecognitionResults(
|
||||
int session_id, const content::SpeechRecognitionResults& result) override;
|
||||
virtual void OnRecognitionError(
|
||||
int session_id, const content::SpeechRecognitionError& error) override;
|
||||
virtual void OnAudioLevelsChange(int session_id, float volume,
|
||||
float noise_volume) override;
|
||||
|
||||
// content::SpeechRecognitionManagerDelegate:
|
||||
virtual void GetDiagnosticInformation(bool* can_report_metrics,
|
||||
std::string* hardware_info) override;
|
||||
virtual void CheckRecognitionIsAllowed(
|
||||
int session_id,
|
||||
base::Callback<void(bool ask_user, bool is_allowed)> callback) override;
|
||||
virtual content::SpeechRecognitionEventListener* GetEventListener() override;
|
||||
virtual bool FilterProfanities(int render_process_id) override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomSpeechRecognitionManagerDelegate);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_ATOM_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_
|
||||
@@ -37,6 +37,8 @@ void Browser::Quit() {
|
||||
}
|
||||
|
||||
void Browser::Shutdown() {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
||||
|
||||
is_quiting_ = true;
|
||||
base::MessageLoop::current()->Quit();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ class BrowserObserver {
|
||||
// method will not be called, instead it will call OnWillQuit.
|
||||
virtual void OnWindowAllClosed() {}
|
||||
|
||||
// The browser is quitting.
|
||||
virtual void OnQuit() {}
|
||||
|
||||
// The browser has opened a file by double clicking in Finder or dragging the
|
||||
// file to the Dock icon. (OS X only)
|
||||
virtual void OnOpenFile(bool* prevent_default,
|
||||
|
||||
@@ -12,15 +12,15 @@ app.on('window-all-closed', function() {
|
||||
});
|
||||
|
||||
app.on('ready', function() {
|
||||
app.commandLine.appendSwitch('js-flags', '--harmony_collections');
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
resizable: false,
|
||||
'auto-hide-menu-bar': true,
|
||||
'use-content-size': true,
|
||||
});
|
||||
mainWindow.loadUrl('file://' + __dirname + '/index.html');
|
||||
mainWindow.focus();
|
||||
|
||||
if (process.platform == 'darwin') {
|
||||
var template = [
|
||||
@@ -151,33 +151,33 @@ app.on('ready', function() {
|
||||
} else {
|
||||
var template = [
|
||||
{
|
||||
label: 'File',
|
||||
label: '&File',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Open',
|
||||
label: '&Open',
|
||||
accelerator: 'Ctrl+O',
|
||||
},
|
||||
{
|
||||
label: 'Close',
|
||||
label: '&Close',
|
||||
accelerator: 'Ctrl+W',
|
||||
click: function() { mainWindow.close(); }
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'View',
|
||||
label: '&View',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Reload',
|
||||
label: '&Reload',
|
||||
accelerator: 'Ctrl+R',
|
||||
click: function() { mainWindow.restart(); }
|
||||
},
|
||||
{
|
||||
label: 'Enter Fullscreen',
|
||||
label: '&Enter Fullscreen',
|
||||
click: function() { mainWindow.setFullScreen(true); }
|
||||
},
|
||||
{
|
||||
label: 'Toggle DevTools',
|
||||
label: '&Toggle DevTools',
|
||||
accelerator: 'Alt+Ctrl+I',
|
||||
click: function() { mainWindow.toggleDevTools(); }
|
||||
},
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
<head>
|
||||
<title>Atom Shell</title>
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #555;
|
||||
font-family: 'Open Sans',Helvetica,Arial,sans-serif;
|
||||
|
||||
@@ -11,11 +11,13 @@ app.on('window-all-closed', function() {
|
||||
|
||||
// Parse command line options.
|
||||
var argv = process.argv.slice(1);
|
||||
var option = { file: null, version: null };
|
||||
var option = { file: null, version: null, webdriver: null };
|
||||
for (var i in argv) {
|
||||
if (argv[i] == '--version' || argv[i] == '-v') {
|
||||
option.version = true;
|
||||
break;
|
||||
} else if (argv[i] == '--test-type=webdriver') {
|
||||
option.webdriver = true;
|
||||
} else if (argv[i][0] == '-') {
|
||||
continue;
|
||||
} else {
|
||||
@@ -26,7 +28,7 @@ for (var i in argv) {
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
if (option.file) {
|
||||
if (option.file && !option.webdriver) {
|
||||
try {
|
||||
// Override app name and version.
|
||||
var packagePath = path.resolve(option.file);
|
||||
|
||||
@@ -52,20 +52,26 @@ setImmediate ->
|
||||
detail: message
|
||||
buttons: ['OK']
|
||||
|
||||
# Emit 'exit' event on quit.
|
||||
require('app').on 'quit', ->
|
||||
process.emit 'exit'
|
||||
|
||||
# Load the RPC server.
|
||||
require './rpc-server.js'
|
||||
|
||||
# Now we try to load app's package.json.
|
||||
packageJson = null
|
||||
|
||||
packagePath = path.join process.resourcesPath, 'app'
|
||||
try
|
||||
# First we try to load process.resourcesPath/app
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
catch error
|
||||
# If not found then we load browser/default_app
|
||||
packagePath = path.join process.resourcesPath, 'default_app'
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
searchPaths = [ 'app', 'app.asar', 'default_app' ]
|
||||
for packagePath in searchPaths
|
||||
try
|
||||
packagePath = path.join process.resourcesPath, packagePath
|
||||
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
|
||||
break
|
||||
catch e
|
||||
continue
|
||||
|
||||
throw new Error("Unable to find a valid app") unless packageJson?
|
||||
|
||||
# Set application's version.
|
||||
app = require 'app'
|
||||
@@ -77,6 +83,12 @@ setImmediate ->
|
||||
else if packageJson.name?
|
||||
app.setName packageJson.name
|
||||
|
||||
# Set application's desktop name.
|
||||
if packageJson.desktopName?
|
||||
app.setDesktopName packageJson.desktopName
|
||||
else
|
||||
app.setDesktopName '#{app.getName()}.desktop'
|
||||
|
||||
# Load the chrome extension support.
|
||||
require './chrome-extension.js'
|
||||
|
||||
|
||||
@@ -41,20 +41,43 @@
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "content/public/common/user_agent.h"
|
||||
#include "content/public/common/web_preferences.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "ui/gfx/codec/png_codec.h"
|
||||
#include "ui/gfx/point.h"
|
||||
#include "ui/gfx/rect.h"
|
||||
#include "ui/gfx/size.h"
|
||||
#include "webkit/common/webpreferences.h"
|
||||
|
||||
using content::NavigationEntry;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Array of available web runtime features.
|
||||
const char* kWebRuntimeFeatures[] = {
|
||||
switches::kExperimentalFeatures,
|
||||
switches::kExperimentalCanvasFeatures,
|
||||
switches::kSubpixelFontScaling,
|
||||
switches::kOverlayScrollbars,
|
||||
switches::kOverlayFullscreenVideo,
|
||||
switches::kSharedWorker,
|
||||
};
|
||||
|
||||
std::string RemoveWhitespace(const std::string& str) {
|
||||
std::string trimmed;
|
||||
if (base::RemoveChars(str, " ", &trimmed))
|
||||
return trimmed;
|
||||
else
|
||||
return str;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindow::NativeWindow(content::WebContents* web_contents,
|
||||
const mate::Dictionary& options)
|
||||
: content::WebContentsObserver(web_contents),
|
||||
@@ -92,8 +115,8 @@ NativeWindow::NativeWindow(content::WebContents* web_contents,
|
||||
// Override the user agent to contain application and atom-shell's version.
|
||||
Browser* browser = Browser::Get();
|
||||
std::string product_name = base::StringPrintf(
|
||||
"%s/%s Chrome/%s Atom-Shell/" ATOM_VERSION_STRING,
|
||||
browser->GetName().c_str(),
|
||||
"%s/%s Chrome/%s AtomShell/" ATOM_VERSION_STRING,
|
||||
RemoveWhitespace(browser->GetName()).c_str(),
|
||||
browser->GetVersion().c_str(),
|
||||
CHROME_VERSION_STRING);
|
||||
web_contents->GetMutableRendererPrefs()->user_agent_override =
|
||||
@@ -225,9 +248,8 @@ bool NativeWindow::IsDevToolsOpened() {
|
||||
|
||||
void NativeWindow::InspectElement(int x, int y) {
|
||||
OpenDevTools();
|
||||
content::RenderViewHost* rvh = GetWebContents()->GetRenderViewHost();
|
||||
scoped_refptr<content::DevToolsAgentHost> agent(
|
||||
content::DevToolsAgentHost::GetOrCreateFor(rvh));
|
||||
content::DevToolsAgentHost::GetOrCreateFor(GetWebContents()));
|
||||
agent->InspectElement(x, y);
|
||||
}
|
||||
|
||||
@@ -264,7 +286,7 @@ void NativeWindow::CapturePage(const gfx::Rect& rect,
|
||||
base::Bind(&NativeWindow::OnCapturePageDone,
|
||||
weak_factory_.GetWeakPtr(),
|
||||
callback),
|
||||
SkBitmap::kARGB_8888_Config);
|
||||
kAlpha_8_SkColorType);
|
||||
}
|
||||
|
||||
void NativeWindow::DestroyWebContents() {
|
||||
@@ -325,33 +347,50 @@ void NativeWindow::AppendExtraCommandLineSwitches(
|
||||
if (zoom_factor_ != 1.0)
|
||||
command_line->AppendSwitchASCII(switches::kZoomFactor,
|
||||
base::DoubleToString(zoom_factor_));
|
||||
|
||||
if (web_preferences_.IsEmpty())
|
||||
return;
|
||||
|
||||
bool b;
|
||||
#if defined(OS_WIN)
|
||||
// Check if DirectWrite is disabled.
|
||||
if (web_preferences_.Get(switches::kDirectWrite, &b) && !b)
|
||||
command_line->AppendSwitch(::switches::kDisableDirectWrite);
|
||||
#endif
|
||||
|
||||
// This set of options are not availabe in WebPreferences, so we have to pass
|
||||
// them via command line and enable them in renderer procss.
|
||||
for (size_t i = 0; i < arraysize(kWebRuntimeFeatures); ++i) {
|
||||
const char* feature = kWebRuntimeFeatures[i];
|
||||
if (web_preferences_.Get(feature, &b))
|
||||
command_line->AppendSwitchASCII(feature, b ? "true" : "false");
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindow::OverrideWebkitPrefs(const GURL& url, WebPreferences* prefs) {
|
||||
void NativeWindow::OverrideWebkitPrefs(const GURL& url,
|
||||
content::WebPreferences* prefs) {
|
||||
if (web_preferences_.IsEmpty())
|
||||
return;
|
||||
|
||||
bool b;
|
||||
std::vector<base::FilePath> list;
|
||||
mate::Dictionary web_preferences(web_preferences_.isolate(),
|
||||
web_preferences_.NewHandle());
|
||||
if (web_preferences.Get("javascript", &b))
|
||||
if (web_preferences_.Get("javascript", &b))
|
||||
prefs->javascript_enabled = b;
|
||||
if (web_preferences.Get("web-security", &b))
|
||||
if (web_preferences_.Get("web-security", &b))
|
||||
prefs->web_security_enabled = b;
|
||||
if (web_preferences.Get("images", &b))
|
||||
if (web_preferences_.Get("images", &b))
|
||||
prefs->images_enabled = b;
|
||||
if (web_preferences.Get("java", &b))
|
||||
if (web_preferences_.Get("java", &b))
|
||||
prefs->java_enabled = b;
|
||||
if (web_preferences.Get("text-areas-are-resizable", &b))
|
||||
if (web_preferences_.Get("text-areas-are-resizable", &b))
|
||||
prefs->text_areas_are_resizable = b;
|
||||
if (web_preferences.Get("webgl", &b))
|
||||
if (web_preferences_.Get("webgl", &b))
|
||||
prefs->experimental_webgl_enabled = b;
|
||||
if (web_preferences.Get("webaudio", &b))
|
||||
if (web_preferences_.Get("webaudio", &b))
|
||||
prefs->webaudio_enabled = b;
|
||||
if (web_preferences.Get("plugins", &b))
|
||||
if (web_preferences_.Get("plugins", &b))
|
||||
prefs->plugins_enabled = b;
|
||||
if (web_preferences.Get("extra-plugin-dirs", &list))
|
||||
if (web_preferences_.Get("extra-plugin-dirs", &list))
|
||||
for (size_t i = 0; i < list.size(); ++i)
|
||||
content::PluginService::GetInstance()->AddExtraPluginDir(list[i]);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
#include "brightray/browser/inspectable_web_contents_impl.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "native_mate/scoped_persistent.h"
|
||||
#include "native_mate/persistent_dictionary.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
|
||||
struct WebPreferences;
|
||||
|
||||
namespace base {
|
||||
class CommandLine;
|
||||
}
|
||||
@@ -32,6 +30,7 @@ class CommandLine;
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
class WebContents;
|
||||
struct WebPreferences;
|
||||
}
|
||||
|
||||
namespace gfx {
|
||||
@@ -140,6 +139,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
virtual void SetMenu(ui::MenuModel* menu);
|
||||
virtual bool HasModalDialog();
|
||||
virtual gfx::NativeWindow GetNativeWindow() = 0;
|
||||
virtual void SetProgressBar(double progress) = 0;
|
||||
|
||||
virtual bool IsClosed() const { return is_closed_; }
|
||||
virtual void OpenDevTools();
|
||||
@@ -177,7 +177,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
// Called when renderer process is going to be started.
|
||||
void AppendExtraCommandLineSwitches(base::CommandLine* command_line,
|
||||
int child_process_id);
|
||||
void OverrideWebkitPrefs(const GURL& url, WebPreferences* prefs);
|
||||
void OverrideWebkitPrefs(const GURL& url, content::WebPreferences* prefs);
|
||||
|
||||
// Public API used by platform-dependent delegates and observers to send UI
|
||||
// related notifications.
|
||||
@@ -298,7 +298,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
base::CancelableClosure window_unresposive_closure_;
|
||||
|
||||
// Web preferences.
|
||||
mate::ScopedPersistent<v8::Object> web_preferences_;
|
||||
mate::PersistentDictionary web_preferences_;
|
||||
|
||||
// Page's default zoom factor.
|
||||
double zoom_factor_;
|
||||
|
||||
@@ -65,6 +65,7 @@ class NativeWindowMac : public NativeWindow {
|
||||
virtual bool IsDocumentEdited() OVERRIDE;
|
||||
virtual bool HasModalDialog() OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
virtual void SetProgressBar(double progress) OVERRIDE;
|
||||
|
||||
// Returns true if |point| in local Cocoa coordinate system falls within
|
||||
// the draggable region.
|
||||
|
||||
@@ -193,6 +193,39 @@ static const CGFloat kAtomWindowCornerRadius = 4.0;
|
||||
|
||||
@end
|
||||
|
||||
@interface AtomProgressBar : NSProgressIndicator
|
||||
@end
|
||||
|
||||
@implementation AtomProgressBar
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
if (self.style != NSProgressIndicatorBarStyle)
|
||||
return;
|
||||
// Draw edges of rounded rect.
|
||||
NSRect rect = NSInsetRect([self bounds], 1.0, 1.0);
|
||||
CGFloat radius = rect.size.height / 2;
|
||||
NSBezierPath* bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
|
||||
[bezier_path setLineWidth:2.0];
|
||||
[[NSColor grayColor] set];
|
||||
[bezier_path stroke];
|
||||
|
||||
// Fill the rounded rect.
|
||||
rect = NSInsetRect(rect, 2.0, 2.0);
|
||||
radius = rect.size.height / 2;
|
||||
bezier_path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:radius yRadius:radius];
|
||||
[bezier_path setLineWidth:1.0];
|
||||
[bezier_path addClip];
|
||||
|
||||
// Calculate the progress width.
|
||||
rect.size.width = floor(rect.size.width * ([self doubleValue] / [self maxValue]));
|
||||
|
||||
// Fill the progress bar with color blue.
|
||||
[[NSColor colorWithSRGBRed:0.2 green:0.6 blue:1 alpha:1] set];
|
||||
NSRectFill(rect);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace atom {
|
||||
|
||||
NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
|
||||
@@ -301,7 +334,7 @@ bool NativeWindowMac::IsFocused() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::Show() {
|
||||
[window_ makeKeyAndOrderFront:nil];
|
||||
[window_ orderFrontRegardless];
|
||||
}
|
||||
|
||||
void NativeWindowMac::Hide() {
|
||||
@@ -517,6 +550,42 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
|
||||
return window_;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetProgressBar(double progress) {
|
||||
NSDockTile* dock_tile = [NSApp dockTile];
|
||||
|
||||
// For the first time API invoked, we need to create a ContentView in DockTile.
|
||||
if (dock_tile.contentView == NULL) {
|
||||
NSImageView* image_view = [[NSImageView alloc] init];
|
||||
[image_view setImage:[NSApp applicationIconImage]];
|
||||
[dock_tile setContentView:image_view];
|
||||
|
||||
NSProgressIndicator* progress_indicator = [[AtomProgressBar alloc]
|
||||
initWithFrame:NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0)];
|
||||
[progress_indicator setStyle:NSProgressIndicatorBarStyle];
|
||||
[progress_indicator setIndeterminate:NO];
|
||||
[progress_indicator setBezeled:YES];
|
||||
[progress_indicator setMinValue:0];
|
||||
[progress_indicator setMaxValue:1];
|
||||
[progress_indicator setHidden:NO];
|
||||
[image_view addSubview:progress_indicator];
|
||||
}
|
||||
|
||||
NSProgressIndicator* progress_indicator =
|
||||
static_cast<NSProgressIndicator*>([[[dock_tile contentView] subviews]
|
||||
objectAtIndex:0]);
|
||||
if (progress < 0) {
|
||||
[progress_indicator setHidden:YES];
|
||||
} else if (progress > 1) {
|
||||
[progress_indicator setHidden:NO];
|
||||
[progress_indicator setIndeterminate:YES];
|
||||
[progress_indicator setDoubleValue:1];
|
||||
} else {
|
||||
[progress_indicator setHidden:NO];
|
||||
[progress_indicator setDoubleValue:progress];
|
||||
}
|
||||
[dock_tile display];
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsWithinDraggableRegion(NSPoint point) const {
|
||||
if (!draggable_region_)
|
||||
return false;
|
||||
|
||||
@@ -43,7 +43,9 @@
|
||||
#elif defined(OS_WIN)
|
||||
#include "atom/browser/ui/views/win_frame_view.h"
|
||||
#include "base/win/scoped_comptr.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#include "ui/views/win/hwnd_util.h"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
@@ -66,8 +68,12 @@ bool ShouldUseGlobalMenuBar() {
|
||||
// Some DE would pretend to be Unity but don't have global application menu,
|
||||
// so we can not trust unity::IsRunning().
|
||||
scoped_ptr<base::Environment> env(base::Environment::Create());
|
||||
return unity::IsRunning() && (base::nix::GetDesktopEnvironment(env.get()) ==
|
||||
base::nix::DESKTOP_ENVIRONMENT_UNITY);
|
||||
bool is_unity = unity::IsRunning() &&
|
||||
base::nix::GetDesktopEnvironment(env.get()) ==
|
||||
base::nix::DESKTOP_ENVIRONMENT_UNITY;
|
||||
std::string menu_proxy;
|
||||
return is_unity && env->GetVar("UBUNTU_MENUPROXY", &menu_proxy) &&
|
||||
menu_proxy.length() > 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -153,8 +159,8 @@ NativeWindowViews::NativeWindowViews(content::WebContents* web_contents,
|
||||
"%s/%s/%d", "Atom Shell", Browser::Get()->GetName().c_str(),
|
||||
++kWindowsCreated);
|
||||
// Set WM_CLASS.
|
||||
params.wm_class_name = "atom-shell";
|
||||
params.wm_class_class = "Atom Shell";
|
||||
params.wm_class_name = "atom";
|
||||
params.wm_class_class = "Atom";
|
||||
#endif
|
||||
|
||||
window_->Init(params);
|
||||
@@ -226,7 +232,7 @@ bool NativeWindowViews::IsFocused() {
|
||||
}
|
||||
|
||||
void NativeWindowViews::Show() {
|
||||
window_->Show();
|
||||
window_->ShowInactive();
|
||||
}
|
||||
|
||||
void NativeWindowViews::Hide() {
|
||||
@@ -479,6 +485,33 @@ gfx::NativeWindow NativeWindowViews::GetNativeWindow() {
|
||||
return window_->GetNativeWindow();
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetProgressBar(double progress) {
|
||||
#if defined(OS_WIN)
|
||||
if (base::win::GetVersion() < base::win::VERSION_WIN7)
|
||||
return;
|
||||
base::win::ScopedComPtr<ITaskbarList3> taskbar;
|
||||
if (FAILED(taskbar.CreateInstance(CLSID_TaskbarList, NULL,
|
||||
CLSCTX_INPROC_SERVER) ||
|
||||
FAILED(taskbar->HrInit()))) {
|
||||
return;
|
||||
}
|
||||
HWND frame = views::HWNDForNativeWindow(GetNativeWindow());
|
||||
if (progress > 1.0) {
|
||||
taskbar->SetProgressState(frame, TBPF_INDETERMINATE);
|
||||
} else if (progress < 0) {
|
||||
taskbar->SetProgressState(frame, TBPF_NOPROGRESS);
|
||||
} else if (progress >= 0) {
|
||||
taskbar->SetProgressValue(frame,
|
||||
static_cast<int>(progress * 100),
|
||||
100);
|
||||
}
|
||||
#elif defined(USE_X11)
|
||||
if (unity::IsRunning()) {
|
||||
unity::SetProgressFraction(progress);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gfx::AcceleratedWidget NativeWindowViews::GetAcceleratedWidget() {
|
||||
return GetNativeWindow()->GetHost()->GetAcceleratedWidget();
|
||||
}
|
||||
@@ -613,6 +646,10 @@ views::NonClientFrameView* NativeWindowViews::CreateNonClientFrameView(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gfx::ImageSkia NativeWindowViews::GetDevToolsWindowIcon() {
|
||||
return GetWindowAppIcon();
|
||||
}
|
||||
|
||||
void NativeWindowViews::HandleMouseDown() {
|
||||
// Hide menu bar when web view is clicked.
|
||||
if (menu_bar_autohide_ && menu_bar_visible_) {
|
||||
@@ -655,7 +692,10 @@ void NativeWindowViews::HandleKeyboardEvent(
|
||||
// When a single Alt is pressed:
|
||||
menu_bar_alt_pressed_ = true;
|
||||
} else if (event.type == blink::WebInputEvent::KeyUp && IsAltKey(event) &&
|
||||
event.modifiers == 0 && menu_bar_alt_pressed_) {
|
||||
#if defined(USE_X11)
|
||||
event.modifiers == 0 &&
|
||||
#endif
|
||||
menu_bar_alt_pressed_) {
|
||||
// When a single Alt is released right after a Alt is pressed:
|
||||
menu_bar_alt_pressed_ = false;
|
||||
SetMenuBarVisibility(!menu_bar_visible_);
|
||||
|
||||
@@ -71,6 +71,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
virtual bool IsKiosk() OVERRIDE;
|
||||
virtual void SetMenu(ui::MenuModel* menu_model) OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
virtual void SetProgressBar(double value) OVERRIDE;
|
||||
|
||||
gfx::AcceleratedWidget GetAcceleratedWidget();
|
||||
|
||||
@@ -105,6 +106,9 @@ class NativeWindowViews : public NativeWindow,
|
||||
virtual views::NonClientFrameView* CreateNonClientFrameView(
|
||||
views::Widget* widget) OVERRIDE;
|
||||
|
||||
// brightray::InspectableWebContentsDelegate:
|
||||
virtual gfx::ImageSkia GetDevToolsWindowIcon() OVERRIDE;
|
||||
|
||||
// content::WebContentsDelegate:
|
||||
virtual void HandleMouseDown() OVERRIDE;
|
||||
virtual void HandleKeyboardEvent(
|
||||
|
||||
91
atom/browser/net/asar/asar_protocol_handler.cc
Normal file
91
atom/browser/net/asar/asar_protocol_handler.cc
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request_error_job.h"
|
||||
#include "net/url_request/url_request_file_job.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
namespace {
|
||||
|
||||
const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar");
|
||||
|
||||
// Get the relative path in asar archive.
|
||||
bool GetAsarPath(const base::FilePath& full_path,
|
||||
base::FilePath* asar_path,
|
||||
base::FilePath* relative_path) {
|
||||
base::FilePath iter = full_path;
|
||||
while (true) {
|
||||
base::FilePath dirname = iter.DirName();
|
||||
if (iter.MatchesExtension(kAsarExtension))
|
||||
break;
|
||||
else if (iter == dirname)
|
||||
return false;
|
||||
iter = dirname;
|
||||
}
|
||||
|
||||
base::FilePath tail;
|
||||
if (!iter.AppendRelativePath(full_path, &tail))
|
||||
return false;
|
||||
|
||||
*asar_path = iter;
|
||||
*relative_path = tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AsarProtocolHandler::AsarProtocolHandler(
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner)
|
||||
: file_task_runner_(file_task_runner) {}
|
||||
|
||||
AsarProtocolHandler::~AsarProtocolHandler() {
|
||||
}
|
||||
|
||||
Archive* AsarProtocolHandler::GetOrCreateAsarArchive(
|
||||
const base::FilePath& path) const {
|
||||
if (!archives_.contains(path)) {
|
||||
scoped_ptr<Archive> archive(new Archive(path));
|
||||
if (!archive->Init())
|
||||
return nullptr;
|
||||
|
||||
archives_.set(path, archive.Pass());
|
||||
}
|
||||
|
||||
return archives_.get(path);
|
||||
}
|
||||
|
||||
net::URLRequestJob* AsarProtocolHandler::MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
base::FilePath full_path;
|
||||
net::FileURLToFilePath(request->url(), &full_path);
|
||||
|
||||
// Create asar:// job when the path contains "xxx.asar/", otherwise treat the
|
||||
// URL request as file://.
|
||||
base::FilePath asar_path, relative_path;
|
||||
if (!GetAsarPath(full_path, &asar_path, &relative_path))
|
||||
return new net::URLRequestFileJob(request, network_delegate, full_path,
|
||||
file_task_runner_);
|
||||
|
||||
Archive* archive = GetOrCreateAsarArchive(asar_path);
|
||||
if (!archive)
|
||||
return new net::URLRequestErrorJob(request, network_delegate,
|
||||
net::ERR_FILE_NOT_FOUND);
|
||||
|
||||
return new URLRequestAsarJob(request, network_delegate, archive,
|
||||
relative_path, file_task_runner_);
|
||||
}
|
||||
|
||||
bool AsarProtocolHandler::IsSafeRedirectTarget(const GURL& location) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace asar
|
||||
45
atom/browser/net/asar/asar_protocol_handler.h
Normal file
45
atom/browser/net/asar/asar_protocol_handler.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
|
||||
#include "base/containers/scoped_ptr_hash_map.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
class Archive;
|
||||
|
||||
class AsarProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
|
||||
public:
|
||||
explicit AsarProtocolHandler(
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner);
|
||||
virtual ~AsarProtocolHandler();
|
||||
|
||||
Archive* GetOrCreateAsarArchive(const base::FilePath& path) const;
|
||||
|
||||
// net::URLRequestJobFactory::ProtocolHandler:
|
||||
virtual net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const OVERRIDE;
|
||||
virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE;
|
||||
|
||||
private:
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
mutable base::ScopedPtrHashMap<base::FilePath, Archive> archives_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AsarProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_ASAR_PROTOCOL_HANDLER_H_
|
||||
142
atom/browser/net/asar/url_request_asar_job.cc
Normal file
142
atom/browser/net/asar/url_request_asar_job.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/asar/url_request_asar_job.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "net/base/file_stream.h"
|
||||
#include "net/base/io_buffer.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request_status.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
URLRequestAsarJob::URLRequestAsarJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
Archive* archive,
|
||||
const base::FilePath& file_path,
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner)
|
||||
: net::URLRequestJob(request, network_delegate),
|
||||
archive_(archive),
|
||||
file_path_(file_path),
|
||||
stream_(new net::FileStream(file_task_runner)),
|
||||
remaining_bytes_(0),
|
||||
file_task_runner_(file_task_runner),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
URLRequestAsarJob::~URLRequestAsarJob() {}
|
||||
|
||||
void URLRequestAsarJob::Start() {
|
||||
if (!archive_ || !archive_->GetFileInfo(file_path_, &file_info_)) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_FILE_NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
|
||||
remaining_bytes_ = static_cast<int64>(file_info_.size);
|
||||
|
||||
int flags = base::File::FLAG_OPEN |
|
||||
base::File::FLAG_READ |
|
||||
base::File::FLAG_ASYNC;
|
||||
int rv = stream_->Open(archive_->path(), flags,
|
||||
base::Bind(&URLRequestAsarJob::DidOpen,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
if (rv != net::ERR_IO_PENDING)
|
||||
DidOpen(rv);
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::Kill() {
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
URLRequestJob::Kill();
|
||||
}
|
||||
|
||||
bool URLRequestAsarJob::ReadRawData(net::IOBuffer* dest,
|
||||
int dest_size,
|
||||
int* bytes_read) {
|
||||
if (remaining_bytes_ < dest_size)
|
||||
dest_size = static_cast<int>(remaining_bytes_);
|
||||
|
||||
// If we should copy zero bytes because |remaining_bytes_| is zero, short
|
||||
// circuit here.
|
||||
if (!dest_size) {
|
||||
*bytes_read = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int rv = stream_->Read(dest,
|
||||
dest_size,
|
||||
base::Bind(&URLRequestAsarJob::DidRead,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
make_scoped_refptr(dest)));
|
||||
if (rv >= 0) {
|
||||
// Data is immediately available.
|
||||
*bytes_read = rv;
|
||||
remaining_bytes_ -= rv;
|
||||
DCHECK_GE(remaining_bytes_, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, a read error occured. We may just need to wait...
|
||||
if (rv == net::ERR_IO_PENDING) {
|
||||
SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
|
||||
} else {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, rv));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool URLRequestAsarJob::GetMimeType(std::string* mime_type) const {
|
||||
return net::GetMimeTypeFromFile(file_path_, mime_type);
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidOpen(int result) {
|
||||
if (result != net::OK) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
|
||||
return;
|
||||
}
|
||||
|
||||
int rv = stream_->Seek(base::File::FROM_BEGIN,
|
||||
file_info_.offset,
|
||||
base::Bind(&URLRequestAsarJob::DidSeek,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
if (rv != net::ERR_IO_PENDING) {
|
||||
// stream_->Seek() failed, so pass an intentionally erroneous value
|
||||
// into DidSeek().
|
||||
DidSeek(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidSeek(int64 result) {
|
||||
if (result != static_cast<int64>(file_info_.offset)) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
|
||||
net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
|
||||
return;
|
||||
}
|
||||
|
||||
set_expected_content_size(remaining_bytes_);
|
||||
NotifyHeadersComplete();
|
||||
}
|
||||
|
||||
void URLRequestAsarJob::DidRead(scoped_refptr<net::IOBuffer> buf, int result) {
|
||||
if (result > 0) {
|
||||
SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
|
||||
remaining_bytes_ -= result;
|
||||
DCHECK_GE(remaining_bytes_, 0);
|
||||
}
|
||||
|
||||
buf = NULL;
|
||||
|
||||
if (result == 0) {
|
||||
NotifyDone(net::URLRequestStatus());
|
||||
} else if (result < 0) {
|
||||
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
|
||||
}
|
||||
|
||||
NotifyReadComplete(result);
|
||||
}
|
||||
|
||||
} // namespace asar
|
||||
72
atom/browser/net/asar/url_request_asar_job.h
Normal file
72
atom/browser/net/asar/url_request_asar_job.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
#define ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class FileStream;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
class URLRequestAsarJob : public net::URLRequestJob {
|
||||
public:
|
||||
URLRequestAsarJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
Archive* archive,
|
||||
const base::FilePath& file_path,
|
||||
const scoped_refptr<base::TaskRunner>& file_task_runner);
|
||||
|
||||
// net::URLRequestJob:
|
||||
virtual void Start() OVERRIDE;
|
||||
virtual void Kill() OVERRIDE;
|
||||
virtual bool ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int* bytes_read) OVERRIDE;
|
||||
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual ~URLRequestAsarJob();
|
||||
|
||||
private:
|
||||
// Callback after opening file on a background thread.
|
||||
void DidOpen(int result);
|
||||
|
||||
// Callback after seeking to the beginning of |byte_range_| in the file
|
||||
// on a background thread.
|
||||
void DidSeek(int64 result);
|
||||
|
||||
// Callback after data is asynchronously read from the file into |buf|.
|
||||
void DidRead(scoped_refptr<net::IOBuffer> buf, int result);
|
||||
|
||||
Archive* archive_;
|
||||
Archive::FileInfo file_info_;
|
||||
base::FilePath file_path_;
|
||||
|
||||
scoped_ptr<net::FileStream> stream_;
|
||||
int64 remaining_bytes_;
|
||||
|
||||
const scoped_refptr<base::TaskRunner> file_task_runner_;
|
||||
|
||||
base::WeakPtrFactory<URLRequestAsarJob> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestAsarJob);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.16.0</string>
|
||||
<string>0.18.1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.8.0</string>
|
||||
<key>NSMainNibFile</key>
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,16,0,0
|
||||
PRODUCTVERSION 0,16,0,0
|
||||
FILEVERSION 0,18,1,0
|
||||
PRODUCTVERSION 0,18,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Atom-Shell"
|
||||
VALUE "FileVersion", "0.16.0"
|
||||
VALUE "FileVersion", "0.18.1"
|
||||
VALUE "InternalName", "atom.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2013 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "atom.exe"
|
||||
VALUE "ProductName", "Atom-Shell"
|
||||
VALUE "ProductVersion", "0.16.0"
|
||||
VALUE "ProductVersion", "0.18.1"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -91,7 +91,7 @@ bool StringToAccelerator(const std::string& description,
|
||||
LOG(ERROR) << "The accelerator string can only contain ASCII characters";
|
||||
return false;
|
||||
}
|
||||
std::string shortcut(StringToLowerASCII(description));
|
||||
std::string shortcut(base::StringToLowerASCII(description));
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
base::SplitString(shortcut, '+', &tokens);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
|
||||
#include "ui/aura/window.h"
|
||||
#include "ui/aura/window_tree_host.h"
|
||||
#include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
@@ -114,6 +115,11 @@ class FileChooserDialog {
|
||||
g_signal_connect(dialog_, "response",
|
||||
G_CALLBACK(OnFileDialogResponseThunk), this);
|
||||
gtk_widget_show_all(dialog_);
|
||||
|
||||
// We need to call gtk_window_present after making the widgets visible to
|
||||
// make sure window gets correctly raised and gets focus.
|
||||
int time = views::X11DesktopHandler::get()->wm_user_time_ms();
|
||||
gtk_window_present_with_time(GTK_WINDOW(dialog_), time);
|
||||
}
|
||||
|
||||
void RunSaveAsynchronous(const SaveDialogCallback& callback) {
|
||||
|
||||
@@ -75,6 +75,9 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
||||
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
NSString* title = base::SysUTF8ToNSString(buttons[i]);
|
||||
// An empty title causes crash on OS X.
|
||||
if (buttons[i].empty())
|
||||
title = @"(empty)";
|
||||
NSButton* button = [alert addButtonWithTitle:title];
|
||||
[button setTag:i];
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ MessageDialog::MessageDialog(NativeWindow* parent_window,
|
||||
views::LabelButton* button = new views::LabelButton(
|
||||
this, base::UTF8ToUTF16(buttons[i]));
|
||||
button->set_tag(i);
|
||||
button->set_min_size(gfx::Size(60, 30));
|
||||
button->SetMinSize(gfx::Size(60, 30));
|
||||
button->SetStyle(views::Button::STYLE_BUTTON);
|
||||
button->SetGroup(kButtonGroup);
|
||||
|
||||
|
||||
@@ -12,8 +12,18 @@ TrayIcon::TrayIcon() {
|
||||
TrayIcon::~TrayIcon() {
|
||||
}
|
||||
|
||||
void TrayIcon::SetTitle(const std::string& title) {
|
||||
}
|
||||
|
||||
void TrayIcon::SetHighlightMode(bool highlight) {
|
||||
}
|
||||
|
||||
void TrayIcon::NotifyClicked() {
|
||||
FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnClicked());
|
||||
}
|
||||
|
||||
void TrayIcon::NotifyDoubleClicked() {
|
||||
FOR_EACH_OBSERVER(TrayIconObserver, observers_, OnDoubleClicked());
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -31,12 +31,21 @@ class TrayIcon {
|
||||
// status icon (e.g. Ubuntu Unity).
|
||||
virtual void SetToolTip(const std::string& tool_tip) = 0;
|
||||
|
||||
// Sets the title displayed aside of the status icon in the status bar. This
|
||||
// only works on OS X.
|
||||
virtual void SetTitle(const std::string& title);
|
||||
|
||||
// Sets whether the status icon is highlighted when it is clicked. This only
|
||||
// works on OS X.
|
||||
virtual void SetHighlightMode(bool highlight);
|
||||
|
||||
// Set the context menu for this icon.
|
||||
virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) = 0;
|
||||
|
||||
void AddObserver(TrayIconObserver* obs) { observers_.AddObserver(obs); }
|
||||
void RemoveObserver(TrayIconObserver* obs) { observers_.RemoveObserver(obs); }
|
||||
void NotifyClicked();
|
||||
void NotifyDoubleClicked();
|
||||
|
||||
protected:
|
||||
TrayIcon();
|
||||
|
||||
@@ -25,6 +25,8 @@ class TrayIconCocoa : public TrayIcon {
|
||||
virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE;
|
||||
virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE;
|
||||
virtual void SetToolTip(const std::string& tool_tip) OVERRIDE;
|
||||
virtual void SetTitle(const std::string& title) OVERRIDE;
|
||||
virtual void SetHighlightMode(bool highlight) OVERRIDE;
|
||||
virtual void SetContextMenu(ui::SimpleMenuModel* menu_model) OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
}
|
||||
- (id)initWithIcon:(atom::TrayIconCocoa*)icon;
|
||||
- (void)handleClick:(id)sender;
|
||||
- (void)handleDoubleClick:(id)sender;
|
||||
|
||||
@end // @interface StatusItemController
|
||||
|
||||
@@ -24,10 +25,13 @@
|
||||
}
|
||||
|
||||
- (void)handleClick:(id)sender {
|
||||
DCHECK(trayIcon_);
|
||||
trayIcon_->NotifyClicked();
|
||||
}
|
||||
|
||||
- (void)handleDoubleClick:(id)sender {
|
||||
trayIcon_->NotifyDoubleClicked();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace atom {
|
||||
@@ -40,6 +44,7 @@ TrayIconCocoa::TrayIconCocoa() {
|
||||
[item_ setEnabled:YES];
|
||||
[item_ setTarget:controller_];
|
||||
[item_ setAction:@selector(handleClick:)];
|
||||
[item_ setDoubleAction:@selector(handleDoubleClick:)];
|
||||
[item_ setHighlightMode:YES];
|
||||
}
|
||||
|
||||
@@ -68,6 +73,14 @@ void TrayIconCocoa::SetToolTip(const std::string& tool_tip) {
|
||||
[item_ setToolTip:base::SysUTF8ToNSString(tool_tip)];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetTitle(const std::string& title) {
|
||||
[item_ setTitle:base::SysUTF8ToNSString(title)];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetHighlightMode(bool highlight) {
|
||||
[item_ setHighlightMode:highlight];
|
||||
}
|
||||
|
||||
void TrayIconCocoa::SetContextMenu(ui::SimpleMenuModel* menu_model) {
|
||||
menu_.reset([[AtomMenuController alloc] initWithModel:menu_model]);
|
||||
[item_ setMenu:[menu_ menu]];
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace atom {
|
||||
class TrayIconObserver {
|
||||
public:
|
||||
virtual void OnClicked() {}
|
||||
virtual void OnDoubleClicked() {}
|
||||
|
||||
protected:
|
||||
virtual ~TrayIconObserver() {}
|
||||
|
||||
@@ -35,14 +35,15 @@ void MenuDelegate::RunMenu(ui::MenuModel* model, views::MenuButton* button) {
|
||||
id_ = button->tag();
|
||||
views::MenuItemView* item = BuildMenu(model);
|
||||
|
||||
views::MenuRunner menu_runner(item);
|
||||
views::MenuRunner menu_runner(
|
||||
item,
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
button->GetWidget()->GetTopLevelWidget(),
|
||||
button,
|
||||
bounds,
|
||||
views::MENU_ANCHOR_TOPRIGHT,
|
||||
ui::MENU_SOURCE_MOUSE,
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
|
||||
ui::MENU_SOURCE_MOUSE));
|
||||
}
|
||||
|
||||
views::MenuItemView* MenuDelegate::BuildMenu(ui::MenuModel* model) {
|
||||
|
||||
@@ -60,14 +60,15 @@ void NotifyIcon::HandleClickEvent(const gfx::Point& cursor_pos,
|
||||
if (!SetForegroundWindow(window_))
|
||||
return;
|
||||
|
||||
views::MenuRunner menu_runner(menu_model_);
|
||||
views::MenuRunner menu_runner(
|
||||
menu_model_,
|
||||
views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS);
|
||||
ignore_result(menu_runner.RunMenuAt(
|
||||
NULL,
|
||||
NULL,
|
||||
gfx::Rect(cursor_pos, gfx::Size()),
|
||||
views::MENU_ANCHOR_TOPLEFT,
|
||||
ui::MENU_SOURCE_MOUSE,
|
||||
views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
|
||||
ui::MENU_SOURCE_MOUSE));
|
||||
}
|
||||
|
||||
void NotifyIcon::ResetIcon() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "base/values.h"
|
||||
#include "content/public/common/common_param_traits.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
#include "ui/gfx/ipc/gfx_param_traits.h"
|
||||
|
||||
// The message starter should be declared in ipc/ipc_message_start.h. Since
|
||||
// we don't want to patch Chromium, we just pretend to be Content Shell.
|
||||
|
||||
117
atom/common/api/atom_api_asar.cc
Normal file
117
atom/common/api/atom_api_asar.cc
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/asar/archive.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "native_mate/arguments.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
#include "native_mate/wrappable.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class Archive : public mate::Wrappable {
|
||||
public:
|
||||
static v8::Handle<v8::Value> Create(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
scoped_ptr<asar::Archive> archive(new asar::Archive(path));
|
||||
if (!archive->Init())
|
||||
return v8::False(isolate);
|
||||
return (new Archive(archive.Pass()))->GetWrapper(isolate);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit Archive(scoped_ptr<asar::Archive> archive)
|
||||
: archive_(archive.Pass()) {}
|
||||
|
||||
// Reads the offset and size of file.
|
||||
v8::Handle<v8::Value> GetFileInfo(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
asar::Archive::FileInfo info;
|
||||
if (!archive_ || !archive_->GetFileInfo(path, &info))
|
||||
return v8::False(isolate);
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
dict.Set("size", info.size);
|
||||
dict.Set("offset", info.offset);
|
||||
return dict.GetHandle();
|
||||
}
|
||||
|
||||
// Returns a fake result of fs.stat(path).
|
||||
v8::Handle<v8::Value> Stat(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
asar::Archive::Stats stats;
|
||||
if (!archive_ || !archive_->Stat(path, &stats))
|
||||
return v8::False(isolate);
|
||||
mate::Dictionary dict(isolate, v8::Object::New(isolate));
|
||||
dict.Set("size", stats.size);
|
||||
dict.Set("offset", stats.offset);
|
||||
dict.Set("isFile", stats.is_file);
|
||||
dict.Set("isDirectory", stats.is_directory);
|
||||
dict.Set("isLink", stats.is_link);
|
||||
return dict.GetHandle();
|
||||
}
|
||||
|
||||
// Returns all files under a directory.
|
||||
v8::Handle<v8::Value> Readdir(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
std::vector<base::FilePath> files;
|
||||
if (!archive_ || !archive_->Readdir(path, &files))
|
||||
return v8::False(isolate);
|
||||
return mate::ConvertToV8(isolate, files);
|
||||
}
|
||||
|
||||
// Returns the path of file with symbol link resolved.
|
||||
v8::Handle<v8::Value> Realpath(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
base::FilePath realpath;
|
||||
if (!archive_ || !archive_->Realpath(path, &realpath))
|
||||
return v8::False(isolate);
|
||||
return mate::ConvertToV8(isolate, realpath);
|
||||
}
|
||||
|
||||
// Copy the file out into a temporary file and returns the new path.
|
||||
v8::Handle<v8::Value> CopyFileOut(v8::Isolate* isolate,
|
||||
const base::FilePath& path) {
|
||||
base::FilePath new_path;
|
||||
if (!archive_ || !archive_->CopyFileOut(path, &new_path))
|
||||
return v8::False(isolate);
|
||||
return mate::ConvertToV8(isolate, new_path);
|
||||
}
|
||||
|
||||
// Free the resources used by archive.
|
||||
void Destroy() {
|
||||
archive_.reset();
|
||||
}
|
||||
|
||||
// mate::Wrappable:
|
||||
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetValue("path", archive_->path())
|
||||
.SetMethod("getFileInfo", &Archive::GetFileInfo)
|
||||
.SetMethod("stat", &Archive::Stat)
|
||||
.SetMethod("readdir", &Archive::Readdir)
|
||||
.SetMethod("realpath", &Archive::Realpath)
|
||||
.SetMethod("copyFileOut", &Archive::CopyFileOut)
|
||||
.SetMethod("destroy", &Archive::Destroy);
|
||||
}
|
||||
|
||||
private:
|
||||
scoped_ptr<asar::Archive> archive_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Archive);
|
||||
};
|
||||
|
||||
void Initialize(v8::Handle<v8::Object> exports, v8::Handle<v8::Value> unused,
|
||||
v8::Handle<v8::Context> context, void* priv) {
|
||||
mate::Dictionary dict(context->GetIsolate(), exports);
|
||||
dict.SetMethod("createArchive", &Archive::Create);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_common_asar, Initialize)
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/atom_version.h"
|
||||
#include "atom/common/chrome_version.h"
|
||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||
#include "base/logging.h"
|
||||
#include "native_mate/callback.h"
|
||||
@@ -78,9 +79,12 @@ void AtomBindings::BindTo(v8::Isolate* isolate,
|
||||
base::Bind(&AtomBindings::ActivateUVLoop, base::Unretained(this)));
|
||||
|
||||
v8::Handle<v8::Object> versions;
|
||||
if (dict.Get("versions", &versions))
|
||||
if (dict.Get("versions", &versions)) {
|
||||
versions->Set(mate::StringToV8(isolate, "atom-shell"),
|
||||
mate::StringToV8(isolate, ATOM_VERSION_STRING));
|
||||
versions->Set(mate::StringToV8(isolate, "chrome"),
|
||||
mate::StringToV8(isolate, CHROME_VERSION_STRING));
|
||||
}
|
||||
}
|
||||
|
||||
void AtomBindings::ActivateUVLoop(v8::Isolate* isolate) {
|
||||
|
||||
252
atom/common/asar/archive.cc
Normal file
252
atom/common/asar/archive.cc
Normal file
@@ -0,0 +1,252 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/common/asar/archive.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/common/asar/scoped_temporary_file.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/pickle.h"
|
||||
#include "base/json/json_string_value_serializer.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
const char kSeparators[] = "\\/";
|
||||
#else
|
||||
const char kSeparators[] = "/";
|
||||
#endif
|
||||
|
||||
bool GetNodeFromPath(std::string path,
|
||||
const base::DictionaryValue* root,
|
||||
const base::DictionaryValue** out);
|
||||
|
||||
// Gets the "files" from "dir".
|
||||
bool GetFilesNode(const base::DictionaryValue* root,
|
||||
const base::DictionaryValue* dir,
|
||||
const base::DictionaryValue** out) {
|
||||
// Test for symbol linked directory.
|
||||
std::string link;
|
||||
if (dir->GetStringWithoutPathExpansion("link", &link)) {
|
||||
const base::DictionaryValue* linked_node = NULL;
|
||||
if (!GetNodeFromPath(link, root, &linked_node))
|
||||
return false;
|
||||
dir = linked_node;
|
||||
}
|
||||
|
||||
return dir->GetDictionaryWithoutPathExpansion("files", out);
|
||||
}
|
||||
|
||||
// Gets sub-file "name" from "dir".
|
||||
bool GetChildNode(const base::DictionaryValue* root,
|
||||
const std::string& name,
|
||||
const base::DictionaryValue* dir,
|
||||
const base::DictionaryValue** out) {
|
||||
const base::DictionaryValue* files = NULL;
|
||||
return GetFilesNode(root, dir, &files) &&
|
||||
files->GetDictionaryWithoutPathExpansion(name, out);
|
||||
}
|
||||
|
||||
// Gets the node of "path" from "root".
|
||||
bool GetNodeFromPath(std::string path,
|
||||
const base::DictionaryValue* root,
|
||||
const base::DictionaryValue** out) {
|
||||
if (path == "") {
|
||||
*out = root;
|
||||
return true;
|
||||
}
|
||||
|
||||
const base::DictionaryValue* dir = root;
|
||||
for (size_t delimiter_position = path.find_first_of(kSeparators);
|
||||
delimiter_position != std::string::npos;
|
||||
delimiter_position = path.find_first_of(kSeparators)) {
|
||||
const base::DictionaryValue* child = NULL;
|
||||
if (!GetChildNode(root, path.substr(0, delimiter_position), dir, &child))
|
||||
return false;
|
||||
|
||||
dir = child;
|
||||
path.erase(0, delimiter_position + 1);
|
||||
}
|
||||
|
||||
return GetChildNode(root, path, dir, out);
|
||||
}
|
||||
|
||||
bool FillFileInfoWithNode(Archive::FileInfo* info,
|
||||
uint32 header_size,
|
||||
const base::DictionaryValue* node) {
|
||||
std::string offset;
|
||||
if (!node->GetString("offset", &offset))
|
||||
return false;
|
||||
if (!base::StringToUint64(offset, &info->offset))
|
||||
return false;
|
||||
|
||||
int size;
|
||||
if (!node->GetInteger("size", &size))
|
||||
return false;
|
||||
|
||||
info->offset += header_size;
|
||||
info->size = static_cast<uint32>(size);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Archive::Archive(const base::FilePath& path)
|
||||
: path_(path),
|
||||
header_size_(0) {
|
||||
}
|
||||
|
||||
Archive::~Archive() {
|
||||
}
|
||||
|
||||
bool Archive::Init() {
|
||||
base::File file(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
|
||||
if (!file.IsValid())
|
||||
return false;
|
||||
|
||||
std::vector<char> buf;
|
||||
int len;
|
||||
|
||||
buf.resize(8);
|
||||
len = file.ReadAtCurrentPos(buf.data(), buf.size());
|
||||
if (len != static_cast<int>(buf.size())) {
|
||||
PLOG(ERROR) << "Failed to read header size from " << path_.value();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 size;
|
||||
if (!PickleIterator(Pickle(buf.data(), buf.size())).ReadUInt32(&size)) {
|
||||
LOG(ERROR) << "Failed to parse header size from " << path_.value();
|
||||
return false;
|
||||
}
|
||||
|
||||
buf.resize(size);
|
||||
len = file.ReadAtCurrentPos(buf.data(), buf.size());
|
||||
if (len != static_cast<int>(buf.size())) {
|
||||
PLOG(ERROR) << "Failed to read header from " << path_.value();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string header;
|
||||
if (!PickleIterator(Pickle(buf.data(), buf.size())).ReadString(&header)) {
|
||||
LOG(ERROR) << "Failed to parse header from " << path_.value();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
JSONStringValueSerializer serializer(&header);
|
||||
base::Value* value = serializer.Deserialize(NULL, &error);
|
||||
if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
|
||||
LOG(ERROR) << "Failed to parse header: " << error;
|
||||
return false;
|
||||
}
|
||||
|
||||
header_size_ = 8 + size;
|
||||
header_.reset(static_cast<base::DictionaryValue*>(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) {
|
||||
if (!header_)
|
||||
return false;
|
||||
|
||||
const base::DictionaryValue* node;
|
||||
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
|
||||
return false;
|
||||
|
||||
std::string link;
|
||||
if (node->GetString("link", &link))
|
||||
return GetFileInfo(base::FilePath::FromUTF8Unsafe(link), info);
|
||||
|
||||
return FillFileInfoWithNode(info, header_size_, node);
|
||||
}
|
||||
|
||||
bool Archive::Stat(const base::FilePath& path, Stats* stats) {
|
||||
if (!header_)
|
||||
return false;
|
||||
|
||||
const base::DictionaryValue* node;
|
||||
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
|
||||
return false;
|
||||
|
||||
if (node->HasKey("link")) {
|
||||
stats->is_file = false;
|
||||
stats->is_link = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node->HasKey("files")) {
|
||||
stats->is_file = false;
|
||||
stats->is_directory = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return FillFileInfoWithNode(stats, header_size_, node);
|
||||
}
|
||||
|
||||
bool Archive::Readdir(const base::FilePath& path,
|
||||
std::vector<base::FilePath>* list) {
|
||||
if (!header_)
|
||||
return false;
|
||||
|
||||
const base::DictionaryValue* node;
|
||||
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
|
||||
return false;
|
||||
|
||||
const base::DictionaryValue* files;
|
||||
if (!GetFilesNode(header_.get(), node, &files))
|
||||
return false;
|
||||
|
||||
base::DictionaryValue::Iterator iter(*files);
|
||||
while (!iter.IsAtEnd()) {
|
||||
list->push_back(base::FilePath::FromUTF8Unsafe(iter.key()));
|
||||
iter.Advance();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Archive::Realpath(const base::FilePath& path, base::FilePath* realpath) {
|
||||
if (!header_)
|
||||
return false;
|
||||
|
||||
const base::DictionaryValue* node;
|
||||
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
|
||||
return false;
|
||||
|
||||
std::string link;
|
||||
if (node->GetString("link", &link)) {
|
||||
*realpath = base::FilePath::FromUTF8Unsafe(link);
|
||||
return true;
|
||||
}
|
||||
|
||||
*realpath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Archive::CopyFileOut(const base::FilePath& path, base::FilePath* out) {
|
||||
if (external_files_.contains(path)) {
|
||||
*out = external_files_.get(path)->path();
|
||||
return true;
|
||||
}
|
||||
|
||||
FileInfo info;
|
||||
if (!GetFileInfo(path, &info))
|
||||
return false;
|
||||
|
||||
scoped_ptr<ScopedTemporaryFile> temp_file(new ScopedTemporaryFile);
|
||||
if (!temp_file->InitFromFile(path_, info.offset, info.size))
|
||||
return false;
|
||||
|
||||
*out = temp_file->path();
|
||||
external_files_.set(path, temp_file.Pass());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace asar
|
||||
76
atom/common/asar/archive.h
Normal file
76
atom/common/asar/archive.h
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_ASAR_ARCHIVE_H_
|
||||
#define ATOM_COMMON_ASAR_ARCHIVE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/scoped_ptr_hash_map.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
}
|
||||
|
||||
namespace asar {
|
||||
|
||||
class ScopedTemporaryFile;
|
||||
|
||||
// This class represents an asar package, and provides methods to read
|
||||
// information from it.
|
||||
class Archive {
|
||||
public:
|
||||
struct FileInfo {
|
||||
FileInfo() : size(0), offset(0) {}
|
||||
uint32 size;
|
||||
uint64 offset;
|
||||
};
|
||||
|
||||
struct Stats : public FileInfo {
|
||||
Stats() : is_file(true), is_directory(false), is_link(false) {}
|
||||
bool is_file;
|
||||
bool is_directory;
|
||||
bool is_link;
|
||||
};
|
||||
|
||||
explicit Archive(const base::FilePath& path);
|
||||
virtual ~Archive();
|
||||
|
||||
// Read and parse the header.
|
||||
bool Init();
|
||||
|
||||
// Get the info of a file.
|
||||
bool GetFileInfo(const base::FilePath& path, FileInfo* info);
|
||||
|
||||
// Fs.stat(path).
|
||||
bool Stat(const base::FilePath& path, Stats* stats);
|
||||
|
||||
// Fs.readdir(path).
|
||||
bool Readdir(const base::FilePath& path, std::vector<base::FilePath>* files);
|
||||
|
||||
// Fs.realpath(path).
|
||||
bool Realpath(const base::FilePath& path, base::FilePath* realpath);
|
||||
|
||||
// Copy the file into a temporary file, and return the new path.
|
||||
bool CopyFileOut(const base::FilePath& path, base::FilePath* out);
|
||||
|
||||
base::FilePath path() const { return path_; }
|
||||
base::DictionaryValue* header() const { return header_.get(); }
|
||||
|
||||
private:
|
||||
base::FilePath path_;
|
||||
uint32 header_size_;
|
||||
scoped_ptr<base::DictionaryValue> header_;
|
||||
|
||||
// Cached external temporary files.
|
||||
base::ScopedPtrHashMap<base::FilePath, ScopedTemporaryFile> external_files_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Archive);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_COMMON_ASAR_ARCHIVE_H_
|
||||
54
atom/common/asar/scoped_temporary_file.cc
Normal file
54
atom/common/asar/scoped_temporary_file.cc
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/common/asar/scoped_temporary_file.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
ScopedTemporaryFile::ScopedTemporaryFile() {
|
||||
}
|
||||
|
||||
ScopedTemporaryFile::~ScopedTemporaryFile() {
|
||||
if (!path_.empty()) {
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
base::DeleteFile(path_, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool ScopedTemporaryFile::Init() {
|
||||
if (!path_.empty())
|
||||
return true;
|
||||
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
return base::CreateTemporaryFile(&path_);
|
||||
}
|
||||
|
||||
bool ScopedTemporaryFile::InitFromFile(const base::FilePath& path,
|
||||
uint64 offset, uint64 size) {
|
||||
if (!Init())
|
||||
return false;
|
||||
|
||||
base::File src(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
|
||||
if (!src.IsValid())
|
||||
return false;
|
||||
|
||||
std::vector<char> buf(size);
|
||||
int len = src.Read(offset, buf.data(), buf.size());
|
||||
if (len != static_cast<int>(size))
|
||||
return false;
|
||||
|
||||
base::File dest(path_, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
|
||||
if (!dest.IsValid())
|
||||
return false;
|
||||
|
||||
return dest.WriteAtCurrentPos(buf.data(), buf.size()) ==
|
||||
static_cast<int>(size);
|
||||
}
|
||||
|
||||
} // namespace asar
|
||||
37
atom/common/asar/scoped_temporary_file.h
Normal file
37
atom/common/asar/scoped_temporary_file.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
|
||||
#define ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
|
||||
namespace asar {
|
||||
|
||||
// An object representing a temporary file that should be cleaned up when this
|
||||
// object goes out of scope. Note that since deletion occurs during the
|
||||
// destructor, no further error handling is possible if the directory fails to
|
||||
// be deleted. As a result, deletion is not guaranteed by this class.
|
||||
class ScopedTemporaryFile {
|
||||
public:
|
||||
ScopedTemporaryFile();
|
||||
virtual ~ScopedTemporaryFile();
|
||||
|
||||
// Init an empty temporary file.
|
||||
bool Init();
|
||||
|
||||
// Init an temporary file and fill it with content of |path|.
|
||||
bool InitFromFile(const base::FilePath& path, uint64 offset, uint64 size);
|
||||
|
||||
base::FilePath path() const { return path_; }
|
||||
|
||||
private:
|
||||
base::FilePath path_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
|
||||
};
|
||||
|
||||
} // namespace asar
|
||||
|
||||
#endif // ATOM_COMMON_ASAR_SCOPED_TEMPORARY_FILE_H_
|
||||
@@ -6,8 +6,8 @@
|
||||
#define ATOM_VERSION_H
|
||||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 16
|
||||
#define ATOM_PATCH_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 18
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#ifndef ATOM_COMMON_CHROME_VERSION_H_
|
||||
#define ATOM_COMMON_CHROME_VERSION_H_
|
||||
|
||||
#define CHROME_VERSION_STRING "37.0.2062.102"
|
||||
#define CHROME_VERSION_STRING "38.0.2125.101"
|
||||
#define CHROME_VERSION "v" CHROME_VERSION_STRING
|
||||
|
||||
#endif // ATOM_COMMON_CHROME_VERSION_H_
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "chrome/common/tts_messages.h"
|
||||
|
||||
12
atom/common/google_api_key.h
Normal file
12
atom/common/google_api_key.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_GOOGLE_API_KEY_H_
|
||||
#define ATOM_COMMON_GOOGLE_API_KEY_H_
|
||||
|
||||
#ifndef GOOGLEAPIS_API_KEY
|
||||
#define GOOGLEAPIS_API_KEY "AIzaSyAQfxPJiounkhOjODEO5ZieffeBv6yft2Q"
|
||||
#endif
|
||||
|
||||
#endif // ATOM_COMMON_GOOGLE_API_KEY_H_
|
||||
301
atom/common/lib/asar.coffee
Normal file
301
atom/common/lib/asar.coffee
Normal file
@@ -0,0 +1,301 @@
|
||||
asar = process.atomBinding 'asar'
|
||||
child_process = require 'child_process'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
util = require 'util'
|
||||
|
||||
# Cache asar archive objects.
|
||||
cachedArchives = {}
|
||||
getOrCreateArchive = (p) ->
|
||||
archive = cachedArchives[p]
|
||||
return archive if archive?
|
||||
archive = asar.createArchive p
|
||||
return false unless archive
|
||||
cachedArchives[p] = archive
|
||||
|
||||
# Clean cache on quit.
|
||||
process.on 'exit', ->
|
||||
archive.destroy() for p, archive of cachedArchives
|
||||
|
||||
# Separate asar package's path from full path.
|
||||
splitPath = (p) ->
|
||||
return [false] if typeof p isnt 'string'
|
||||
return [true, p, ''] if p.substr(-5) is '.asar'
|
||||
index = p.lastIndexOf ".asar#{path.sep}"
|
||||
return [false] if index is -1
|
||||
[true, p.substr(0, index + 5), p.substr(index + 6)]
|
||||
|
||||
# Convert asar archive's Stats object to fs's Stats object.
|
||||
nextInode = 0
|
||||
uid = if process.getuid? then process.getuid() else 0
|
||||
gid = if process.getgid? then process.getgid() else 0
|
||||
fakeTime = new Date()
|
||||
asarStatsToFsStats = (stats) ->
|
||||
{
|
||||
dev: 1,
|
||||
ino: ++nextInode,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
rdev: 0,
|
||||
atime: stats.atime || fakeTime,
|
||||
birthtime: stats.birthtime || fakeTime,
|
||||
mtime: stats.mtime || fakeTime,
|
||||
ctime: stats.ctime || fakeTime,
|
||||
size: stats.size,
|
||||
isFile: -> stats.isFile
|
||||
isDirectory: -> stats.isDirectory
|
||||
isSymbolicLink: -> stats.isLink
|
||||
isBlockDevice: -> false
|
||||
isCharacterDevice: -> false
|
||||
isFIFO: -> false
|
||||
isSocket: -> false
|
||||
}
|
||||
|
||||
# Create a ENOENT error.
|
||||
createNotFoundError = (asarPath, filePath) ->
|
||||
error = new Error("ENOENT, #{filePath} not found in #{asarPath}")
|
||||
error.code = "ENOENT"
|
||||
error.errno = -2
|
||||
error
|
||||
|
||||
# Override fs APIs.
|
||||
lstatSync = fs.lstatSync
|
||||
fs.lstatSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return lstatSync p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
throw new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
stats = archive.stat filePath
|
||||
throw createNotFoundError(asarPath, filePath) unless stats
|
||||
|
||||
asarStatsToFsStats stats
|
||||
|
||||
lstat = fs.lstat
|
||||
fs.lstat = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return lstat p, callback unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return callback new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
stats = getOrCreateArchive(asarPath).stat filePath
|
||||
return callback createNotFoundError(asarPath, filePath) unless stats
|
||||
|
||||
process.nextTick -> callback null, asarStatsToFsStats stats
|
||||
|
||||
statSync = fs.statSync
|
||||
fs.statSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return statSync p unless isAsar
|
||||
|
||||
# Do not distinguish links for now.
|
||||
fs.lstatSync p
|
||||
|
||||
stat = fs.stat
|
||||
fs.stat = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return stat p, callback unless isAsar
|
||||
|
||||
# Do not distinguish links for now.
|
||||
process.nextTick -> fs.lstat p, callback
|
||||
|
||||
statSyncNoException = fs.statSyncNoException
|
||||
fs.statSyncNoException = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return statSyncNoException p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return false unless archive
|
||||
stats = archive.stat filePath
|
||||
return false unless stats
|
||||
asarStatsToFsStats stats
|
||||
|
||||
realpathSync = fs.realpathSync
|
||||
fs.realpathSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return realpathSync.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
throw new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
real = archive.realpath filePath
|
||||
throw createNotFoundError(asarPath, filePath) if real is false
|
||||
|
||||
path.join realpathSync(asarPath), real
|
||||
|
||||
realpath = fs.realpath
|
||||
fs.realpath = (p, cache, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return realpath.apply this, arguments unless isAsar
|
||||
|
||||
if typeof cache is 'function'
|
||||
callback = cache
|
||||
cache = undefined
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return callback new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
real = archive.realpath filePath
|
||||
return callback createNotFoundError(asarPath, filePath) if real is false
|
||||
|
||||
realpath asarPath, (err, p) ->
|
||||
return callback err if err
|
||||
callback null, path.join(p, real)
|
||||
|
||||
exists = fs.exists
|
||||
fs.exists = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return exists p, callback unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return callback new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
process.nextTick -> callback archive.stat(filePath) isnt false
|
||||
|
||||
existsSync = fs.existsSync
|
||||
fs.existsSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return existsSync p unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return false unless archive
|
||||
|
||||
archive.stat(filePath) isnt false
|
||||
|
||||
open = fs.open
|
||||
readFile = fs.readFile
|
||||
fs.readFile = (p, options, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readFile.apply this, arguments unless isAsar
|
||||
|
||||
if typeof options is 'function'
|
||||
callback = options
|
||||
options = undefined
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return callback new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
info = archive.getFileInfo filePath
|
||||
return callback createNotFoundError(asarPath, filePath) unless info
|
||||
|
||||
if not options
|
||||
options = encoding: null, flag: 'r'
|
||||
else if util.isString options
|
||||
options = encoding: options, flag: 'r'
|
||||
else if not util.isObject options
|
||||
throw new TypeError('Bad arguments')
|
||||
|
||||
flag = options.flag || 'r'
|
||||
encoding = options.encoding
|
||||
|
||||
buffer = new Buffer(info.size)
|
||||
open archive.path, flag, (error, fd) ->
|
||||
return callback error if error
|
||||
fs.read fd, buffer, 0, info.size, info.offset, (error) ->
|
||||
fs.close fd, ->
|
||||
callback error, if encoding then buffer.toString encoding else buffer
|
||||
|
||||
openSync = fs.openSync
|
||||
readFileSync = fs.readFileSync
|
||||
fs.readFileSync = (p, options) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readFileSync.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
throw new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
info = archive.getFileInfo filePath
|
||||
throw createNotFoundError(asarPath, filePath) unless info
|
||||
|
||||
if not options
|
||||
options = encoding: null, flag: 'r'
|
||||
else if util.isString options
|
||||
options = encoding: options, flag: 'r'
|
||||
else if not util.isObject options
|
||||
throw new TypeError('Bad arguments')
|
||||
|
||||
flag = options.flag || 'r'
|
||||
encoding = options.encoding
|
||||
|
||||
buffer = new Buffer(info.size)
|
||||
fd = openSync archive.path, flag
|
||||
try
|
||||
fs.readSync fd, buffer, 0, info.size, info.offset
|
||||
catch e
|
||||
throw e
|
||||
finally
|
||||
fs.closeSync fd
|
||||
if encoding then buffer.toString encoding else buffer
|
||||
|
||||
readdir = fs.readdir
|
||||
fs.readdir = (p, callback) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readdir.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return callback new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
files = archive.readdir filePath
|
||||
return callback createNotFoundError(asarPath, filePath) unless files
|
||||
|
||||
process.nextTick -> callback null, files
|
||||
|
||||
readdirSync = fs.readdirSync
|
||||
fs.readdirSync = (p) ->
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return readdirSync.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
throw new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
files = archive.readdir filePath
|
||||
throw createNotFoundError(asarPath, filePath) unless files
|
||||
|
||||
files
|
||||
|
||||
# Override APIs that rely on passing file path instead of content to C++.
|
||||
overrideAPISync = (module, name, arg = 0) ->
|
||||
old = module[name]
|
||||
module[name] = ->
|
||||
p = arguments[arg]
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return old.apply this, arguments unless isAsar
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
throw new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
newPath = archive.copyFileOut filePath
|
||||
throw createNotFoundError(asarPath, filePath) unless newPath
|
||||
|
||||
arguments[arg] = newPath
|
||||
old.apply this, arguments
|
||||
|
||||
overrideAPI = (module, name, arg = 0) ->
|
||||
old = module[name]
|
||||
module[name] = ->
|
||||
p = arguments[arg]
|
||||
[isAsar, asarPath, filePath] = splitPath p
|
||||
return old.apply this, arguments unless isAsar
|
||||
|
||||
callback = arguments[arguments.length - 1]
|
||||
return overrideAPISync module, name, arg unless typeof callback is 'function'
|
||||
|
||||
archive = getOrCreateArchive asarPath
|
||||
return callback new Error("Invalid package #{asarPath}") unless archive
|
||||
|
||||
newPath = archive.copyFileOut filePath
|
||||
return callback createNotFoundError(asarPath, filePath) unless newPath
|
||||
|
||||
arguments[arg] = newPath
|
||||
old.apply this, arguments
|
||||
|
||||
overrideAPI fs, 'open'
|
||||
overrideAPI child_process, 'execFile'
|
||||
overrideAPISync process, 'dlopen', 1
|
||||
overrideAPISync require('module')._extensions, '.node', 1
|
||||
overrideAPISync fs, 'openSync'
|
||||
overrideAPISync child_process, 'fork'
|
||||
@@ -6,7 +6,7 @@ process.atomBinding = (name) ->
|
||||
try
|
||||
process.binding "atom_#{process.type}_#{name}"
|
||||
catch e
|
||||
process.binding "atom_common_#{name}" if e.message is 'No such module'
|
||||
process.binding "atom_common_#{name}" if /No such module/.test e.message
|
||||
|
||||
# Add common/api/lib to module search paths.
|
||||
globalPaths = Module.globalPaths
|
||||
@@ -33,3 +33,6 @@ global.clearImmediate = timers.clearImmediate
|
||||
if process.type is 'browser'
|
||||
global.setTimeout = wrapWithActivateUvLoop timers.setTimeout
|
||||
global.setInterval = wrapWithActivateUvLoop timers.setInterval
|
||||
|
||||
# Add support for asar packages.
|
||||
require './asar'
|
||||
|
||||
@@ -69,6 +69,7 @@ REFERENCE_MODULE(atom_browser_protocol);
|
||||
REFERENCE_MODULE(atom_browser_global_shortcut);
|
||||
REFERENCE_MODULE(atom_browser_tray);
|
||||
REFERENCE_MODULE(atom_browser_window);
|
||||
REFERENCE_MODULE(atom_common_asar);
|
||||
REFERENCE_MODULE(atom_common_clipboard);
|
||||
REFERENCE_MODULE(atom_common_crash_reporter);
|
||||
REFERENCE_MODULE(atom_common_id_weak_map);
|
||||
|
||||
@@ -39,7 +39,7 @@ void NodeBindingsWin::PollEvents() {
|
||||
ULONG_PTR key;
|
||||
OVERLAPPED* overlapped;
|
||||
|
||||
timeout = uv_get_poll_timeout(uv_loop_);
|
||||
timeout = uv_backend_timeout(uv_loop_);
|
||||
GetQueuedCompletionStatus(uv_loop_->iocp,
|
||||
&bytes,
|
||||
&key,
|
||||
|
||||
@@ -57,6 +57,17 @@ const char kEnableLargerThanScreen[] = "enable-larger-than-screen";
|
||||
// Forces to use dark theme on Linux.
|
||||
const char kDarkTheme[] = "dark-theme";
|
||||
|
||||
// Enable DirectWrite on Windows.
|
||||
const char kDirectWrite[] = "direct-write";
|
||||
|
||||
// Web runtime features.
|
||||
const char kExperimentalFeatures[] = "experimental-features";
|
||||
const char kExperimentalCanvasFeatures[] = "experimental-canvas-features";
|
||||
const char kSubpixelFontScaling[] = "subpixel-font-scaling";
|
||||
const char kOverlayScrollbars[] = "overlay-scrollbars";
|
||||
const char kOverlayFullscreenVideo[] = "overlay-fullscreen-video";
|
||||
const char kSharedWorker[] = "shared-worker";
|
||||
|
||||
} // namespace switches
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -35,6 +35,14 @@ extern const char kZoomFactor[];
|
||||
extern const char kAutoHideMenuBar[];
|
||||
extern const char kEnableLargerThanScreen[];
|
||||
extern const char kDarkTheme[];
|
||||
extern const char kDirectWrite[];
|
||||
|
||||
extern const char kExperimentalFeatures[];
|
||||
extern const char kExperimentalCanvasFeatures[];
|
||||
extern const char kSubpixelFontScaling[];
|
||||
extern const char kOverlayScrollbars[];
|
||||
extern const char kOverlayFullscreenVideo[];
|
||||
extern const char kSharedWorker[];
|
||||
|
||||
} // namespace switches
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "atom/renderer/api/atom_renderer_bindings.h"
|
||||
#include "atom/renderer/atom_render_view_observer.h"
|
||||
#include "chrome/renderer/printing/print_web_view_helper.h"
|
||||
#include "chrome/renderer/tts_dispatcher.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "content/public/renderer/render_thread.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebKit.h"
|
||||
#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
@@ -34,6 +36,19 @@ const char* kSecurityManualEnableIframe = "manual-enable-iframe";
|
||||
const char* kSecurityDisable = "disable";
|
||||
const char* kSecurityEnableNodeIntegration = "enable-node-integration";
|
||||
|
||||
bool IsSwitchEnabled(base::CommandLine* command_line,
|
||||
const char* switch_string,
|
||||
bool* enabled) {
|
||||
std::string value = command_line->GetSwitchValueASCII(switch_string);
|
||||
if (value == "true")
|
||||
*enabled = true;
|
||||
else if (value == "false")
|
||||
*enabled = false;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper class to forward the WillReleaseScriptContext message to the client.
|
||||
class AtomRenderFrameObserver : public content::RenderFrameObserver {
|
||||
public:
|
||||
@@ -73,10 +88,6 @@ AtomRendererClient::AtomRendererClient()
|
||||
node_integration_ = ALL;
|
||||
|
||||
if (IsNodeBindingEnabled()) {
|
||||
// Always enable harmony when node binding is on.
|
||||
std::string flags("--harmony");
|
||||
v8::V8::SetFlagsFromString(flags.c_str(), static_cast<int>(flags.size()));
|
||||
|
||||
node_bindings_.reset(NodeBindings::Create(false));
|
||||
atom_bindings_.reset(new AtomRendererBindings);
|
||||
}
|
||||
@@ -86,6 +97,8 @@ AtomRendererClient::~AtomRendererClient() {
|
||||
}
|
||||
|
||||
void AtomRendererClient::WebKitInitialized() {
|
||||
EnableWebRuntimeFeatures();
|
||||
|
||||
if (!IsNodeBindingEnabled())
|
||||
return;
|
||||
|
||||
@@ -116,6 +129,11 @@ void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) {
|
||||
new AtomRenderViewObserver(render_view, this);
|
||||
}
|
||||
|
||||
blink::WebSpeechSynthesizer* AtomRendererClient::OverrideSpeechSynthesizer(
|
||||
blink::WebSpeechSynthesizerClient* client) {
|
||||
return new TtsDispatcher(client);
|
||||
}
|
||||
|
||||
void AtomRendererClient::DidCreateScriptContext(blink::WebFrame* frame,
|
||||
v8::Handle<v8::Context> context,
|
||||
int extension_group,
|
||||
@@ -220,4 +238,21 @@ bool AtomRendererClient::IsNodeBindingEnabled(blink::WebFrame* frame) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AtomRendererClient::EnableWebRuntimeFeatures() {
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
bool b;
|
||||
if (IsSwitchEnabled(command_line, switches::kExperimentalFeatures, &b))
|
||||
blink::WebRuntimeFeatures::enableExperimentalFeatures(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures, &b))
|
||||
blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kSubpixelFontScaling, &b))
|
||||
blink::WebRuntimeFeatures::enableSubpixelFontScaling(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars, &b))
|
||||
blink::WebRuntimeFeatures::enableOverlayScrollbars(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo, &b))
|
||||
blink::WebRuntimeFeatures::enableOverlayFullscreenVideo(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kSharedWorker, &b))
|
||||
blink::WebRuntimeFeatures::enableSharedWorker(b);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -50,6 +50,8 @@ class AtomRendererClient : public content::ContentRendererClient,
|
||||
virtual void RenderThreadStarted() OVERRIDE;
|
||||
virtual void RenderFrameCreated(content::RenderFrame* render_frame) OVERRIDE;
|
||||
virtual void RenderViewCreated(content::RenderView*) OVERRIDE;
|
||||
virtual blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer(
|
||||
blink::WebSpeechSynthesizerClient* client);
|
||||
virtual void DidCreateScriptContext(blink::WebFrame* frame,
|
||||
v8::Handle<v8::Context> context,
|
||||
int extension_group,
|
||||
@@ -61,6 +63,8 @@ class AtomRendererClient : public content::ContentRendererClient,
|
||||
bool is_server_redirect,
|
||||
bool* send_referrer) OVERRIDE;
|
||||
|
||||
void EnableWebRuntimeFeatures();
|
||||
|
||||
std::vector<node::Environment*> web_page_envs_;
|
||||
|
||||
scoped_ptr<NodeBindings> node_bindings_;
|
||||
|
||||
@@ -24,14 +24,18 @@ require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js')
|
||||
global.require = require
|
||||
global.module = module
|
||||
|
||||
# Set the __filename to the path of html file if it's file:// protocol.
|
||||
if window.location.protocol is 'file:'
|
||||
# Emit the 'exit' event when page is unloading.
|
||||
window.addEventListener 'unload', ->
|
||||
process.emit 'exit'
|
||||
|
||||
# Set the __filename to the path of html file if it's file: or asar: protocol.
|
||||
if window.location.protocol in ['file:', 'asar:']
|
||||
pathname =
|
||||
if process.platform is 'win32'
|
||||
if process.platform is 'win32' and window.location.pathname[0] is '/'
|
||||
window.location.pathname.substr 1
|
||||
else
|
||||
window.location.pathname
|
||||
global.__filename = decodeURIComponent pathname
|
||||
global.__filename = path.normalize decodeURIComponent(pathname)
|
||||
global.__dirname = path.dirname global.__filename
|
||||
|
||||
# Set module's filename so relative require can work as expected.
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace extensions {
|
||||
// forwards its output to the base class for processing.
|
||||
//
|
||||
// This class does two things:
|
||||
// 1. Intercepts media keys. Uses an event tap for intercepting media keys
|
||||
// (PlayPause, NextTrack, PreviousTrack).
|
||||
// 1. Intercepts media/volume keys. Uses an event tap for intercepting media keys
|
||||
// (PlayPause, NextTrack, PreviousTrack) and volume keys(VolumeUp, VolumeDown, VolumeMute).
|
||||
// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for
|
||||
// binding to non-media key global hot keys (eg. Command-Shift-1).
|
||||
class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||
@@ -38,7 +38,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||
|
||||
// Keyboard event callbacks.
|
||||
void OnHotKeyEvent(EventHotKeyID hot_key_id);
|
||||
bool OnMediaKeyEvent(int key_code);
|
||||
bool OnMediaOrVolumeKeyEvent(int key_code);
|
||||
|
||||
// GlobalShortcutListener implementation.
|
||||
virtual void StartListening() OVERRIDE;
|
||||
@@ -52,16 +52,16 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||
bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
|
||||
void UnregisterHotKey(const ui::Accelerator& accelerator);
|
||||
|
||||
// Enable and disable the media key event tap.
|
||||
void StartWatchingMediaKeys();
|
||||
void StopWatchingMediaKeys();
|
||||
// Enable and disable the media/volume key event tap.
|
||||
void StartWatchingMediaOrVolumeKeys();
|
||||
void StopWatchingMediaOrVolumeKeys();
|
||||
|
||||
// Enable and disable the hot key event handler.
|
||||
void StartWatchingHotKeys();
|
||||
void StopWatchingHotKeys();
|
||||
|
||||
// Whether or not any media keys are currently registered.
|
||||
bool IsAnyMediaKeyRegistered();
|
||||
// Whether or not any media/volume keys are currently registered.
|
||||
bool IsAnyMediaOrVolumeKeyRegistered();
|
||||
|
||||
// Whether or not any hot keys are currently registered.
|
||||
bool IsAnyHotKeyRegistered();
|
||||
@@ -80,7 +80,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||
// The hotkey identifier for the next global shortcut that is added.
|
||||
KeyId hot_key_id_;
|
||||
|
||||
// A map of all hotkeys (media keys and shortcuts) mapping to their
|
||||
// A map of all hotkeys (media/volume keys and shortcuts) mapping to their
|
||||
// corresponding hotkey IDs. For quickly finding if an accelerator is
|
||||
// registered.
|
||||
AcceleratorIdMap accelerator_ids_;
|
||||
@@ -91,7 +91,7 @@ class GlobalShortcutListenerMac : public GlobalShortcutListener {
|
||||
// Keyboard shortcut IDs to hotkeys map for unregistration.
|
||||
IdHotKeyRefMap id_hot_key_refs_;
|
||||
|
||||
// Event tap for intercepting mac media keys.
|
||||
// Event tap for intercepting mac media/volume keys.
|
||||
CFMachPortRef event_tap_;
|
||||
CFRunLoopSourceRef event_tap_source_;
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ using extensions::GlobalShortcutListenerMac;
|
||||
|
||||
namespace {
|
||||
|
||||
// The media keys subtype. No official docs found, but widely known.
|
||||
// The media/volume keys subtype. No official docs found, but widely known.
|
||||
// http://lists.apple.com/archives/cocoa-dev/2007/Aug/msg00499.html
|
||||
const int kSystemDefinedEventMediaKeysSubtype = 8;
|
||||
const int kSystemDefinedEventMediaAndVolumeKeysSubtype = 8;
|
||||
|
||||
ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
|
||||
ui::KeyboardCode MediaOrVolumeKeyCodeToKeyboardCode(int key_code) {
|
||||
switch (key_code) {
|
||||
case NX_KEYTYPE_PLAY:
|
||||
return ui::VKEY_MEDIA_PLAY_PAUSE;
|
||||
@@ -33,17 +33,26 @@ ui::KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) {
|
||||
case NX_KEYTYPE_NEXT:
|
||||
case NX_KEYTYPE_FAST:
|
||||
return ui::VKEY_MEDIA_NEXT_TRACK;
|
||||
case NX_KEYTYPE_SOUND_UP:
|
||||
return ui::VKEY_VOLUME_UP;
|
||||
case NX_KEYTYPE_SOUND_DOWN:
|
||||
return ui::VKEY_VOLUME_DOWN;
|
||||
case NX_KEYTYPE_MUTE:
|
||||
return ui::VKEY_VOLUME_MUTE;
|
||||
}
|
||||
return ui::VKEY_UNKNOWN;
|
||||
}
|
||||
|
||||
bool IsMediaKey(const ui::Accelerator& accelerator) {
|
||||
bool IsMediaOrVolumeKey(const ui::Accelerator& accelerator) {
|
||||
if (accelerator.modifiers() != 0)
|
||||
return false;
|
||||
return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK ||
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK ||
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE ||
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_STOP);
|
||||
accelerator.key_code() == ui::VKEY_MEDIA_STOP ||
|
||||
accelerator.key_code() == ui::VKEY_VOLUME_UP ||
|
||||
accelerator.key_code() == ui::VKEY_VOLUME_DOWN ||
|
||||
accelerator.key_code() == ui::VKEY_VOLUME_MUTE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -77,8 +86,8 @@ GlobalShortcutListenerMac::~GlobalShortcutListenerMac() {
|
||||
|
||||
// If keys are still registered, make sure we stop the tap. Again, this
|
||||
// should never happen.
|
||||
if (IsAnyMediaKeyRegistered())
|
||||
StopWatchingMediaKeys();
|
||||
if (IsAnyMediaOrVolumeKeyRegistered())
|
||||
StopWatchingMediaOrVolumeKeys();
|
||||
|
||||
if (IsAnyHotKeyRegistered())
|
||||
StopWatchingHotKeys();
|
||||
@@ -114,9 +123,11 @@ void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) {
|
||||
NotifyKeyPressed(accelerator);
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::OnMediaKeyEvent(int media_key_code) {
|
||||
bool GlobalShortcutListenerMac::OnMediaOrVolumeKeyEvent(
|
||||
int media_or_volume_key_code) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
ui::KeyboardCode key_code = MediaKeyCodeToKeyboardCode(media_key_code);
|
||||
ui::KeyboardCode key_code = MediaOrVolumeKeyCodeToKeyboardCode(
|
||||
media_or_volume_key_code);
|
||||
// Create an accelerator corresponding to the keyCode.
|
||||
ui::Accelerator accelerator(key_code, 0);
|
||||
// Look for a match with a bound hot_key.
|
||||
@@ -133,10 +144,10 @@ bool GlobalShortcutListenerMac::RegisterAcceleratorImpl(
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
DCHECK(accelerator_ids_.find(accelerator) == accelerator_ids_.end());
|
||||
|
||||
if (IsMediaKey(accelerator)) {
|
||||
if (!IsAnyMediaKeyRegistered()) {
|
||||
// If this is the first media key registered, start the event tap.
|
||||
StartWatchingMediaKeys();
|
||||
if (IsMediaOrVolumeKey(accelerator)) {
|
||||
if (!IsAnyMediaOrVolumeKeyRegistered()) {
|
||||
// If this is the first media/volume key registered, start the event tap.
|
||||
StartWatchingMediaOrVolumeKeys();
|
||||
}
|
||||
} else {
|
||||
// Register hot_key if they are non-media keyboard shortcuts.
|
||||
@@ -161,7 +172,7 @@ void GlobalShortcutListenerMac::UnregisterAcceleratorImpl(
|
||||
DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
|
||||
|
||||
// Unregister the hot_key if it's a keyboard shortcut.
|
||||
if (!IsMediaKey(accelerator))
|
||||
if (!IsMediaOrVolumeKey(accelerator))
|
||||
UnregisterHotKey(accelerator);
|
||||
|
||||
// Remove hot_key from the mappings.
|
||||
@@ -169,11 +180,11 @@ void GlobalShortcutListenerMac::UnregisterAcceleratorImpl(
|
||||
id_accelerators_.erase(key_id);
|
||||
accelerator_ids_.erase(accelerator);
|
||||
|
||||
if (IsMediaKey(accelerator)) {
|
||||
// If we unregistered a media key, and now no media keys are registered,
|
||||
// stop the media key tap.
|
||||
if (!IsAnyMediaKeyRegistered())
|
||||
StopWatchingMediaKeys();
|
||||
if (IsMediaOrVolumeKey(accelerator)) {
|
||||
// If we unregistered a media/volume key, and now no media/volume keys are registered,
|
||||
// stop the media/volume key tap.
|
||||
if (!IsAnyMediaOrVolumeKeyRegistered())
|
||||
StopWatchingMediaOrVolumeKeys();
|
||||
} else {
|
||||
// If we unregistered a hot key, and no more hot keys are registered, remove
|
||||
// the hot key handler.
|
||||
@@ -226,12 +237,12 @@ void GlobalShortcutListenerMac::UnregisterHotKey(
|
||||
id_hot_key_refs_.erase(key_id);
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StartWatchingMediaKeys() {
|
||||
void GlobalShortcutListenerMac::StartWatchingMediaOrVolumeKeys() {
|
||||
// Make sure there's no existing event tap.
|
||||
DCHECK(event_tap_ == NULL);
|
||||
DCHECK(event_tap_source_ == NULL);
|
||||
|
||||
// Add an event tap to intercept the system defined media key events.
|
||||
// Add an event tap to intercept the system defined media/volume key events.
|
||||
event_tap_ = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionDefault,
|
||||
@@ -254,7 +265,7 @@ void GlobalShortcutListenerMac::StartWatchingMediaKeys() {
|
||||
kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
void GlobalShortcutListenerMac::StopWatchingMediaKeys() {
|
||||
void GlobalShortcutListenerMac::StopWatchingMediaOrVolumeKeys() {
|
||||
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), event_tap_source_,
|
||||
kCFRunLoopCommonModes);
|
||||
// Ensure both event tap and source are initialized.
|
||||
@@ -287,11 +298,11 @@ void GlobalShortcutListenerMac::StopWatchingHotKeys() {
|
||||
event_handler_ = NULL;
|
||||
}
|
||||
|
||||
bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() {
|
||||
// Iterate through registered accelerators, looking for media keys.
|
||||
bool GlobalShortcutListenerMac::IsAnyMediaOrVolumeKeyRegistered() {
|
||||
// Iterate through registered accelerators, looking for media/volume keys.
|
||||
AcceleratorIdMap::iterator it;
|
||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
||||
if (IsMediaKey(it->first))
|
||||
if (IsMediaOrVolumeKey(it->first))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -300,7 +311,7 @@ bool GlobalShortcutListenerMac::IsAnyMediaKeyRegistered() {
|
||||
bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
|
||||
AcceleratorIdMap::iterator it;
|
||||
for (it = accelerator_ids_.begin(); it != accelerator_ids_.end(); ++it) {
|
||||
if (!IsMediaKey(it->first))
|
||||
if (!IsMediaOrVolumeKey(it->first))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -329,20 +340,24 @@ CGEventRef GlobalShortcutListenerMac::EventTapCallback(
|
||||
return event;
|
||||
}
|
||||
|
||||
// Ignore events that are not system defined media keys.
|
||||
// Ignore events that are not system defined media/volume keys.
|
||||
if (type != NX_SYSDEFINED ||
|
||||
[ns_event type] != NSSystemDefined ||
|
||||
[ns_event subtype] != kSystemDefinedEventMediaKeysSubtype) {
|
||||
[ns_event subtype] != kSystemDefinedEventMediaAndVolumeKeysSubtype) {
|
||||
return event;
|
||||
}
|
||||
|
||||
NSInteger data1 = [ns_event data1];
|
||||
// Ignore media keys that aren't previous, next and play/pause.
|
||||
// Ignore media keys that aren't previous, next and play/pause and
|
||||
// volume keys that aren't up, down and mute.
|
||||
// Magical constants are from http://weblog.rogueamoeba.com/2007/09/29/
|
||||
int key_code = (data1 & 0xFFFF0000) >> 16;
|
||||
if (key_code != NX_KEYTYPE_PLAY && key_code != NX_KEYTYPE_NEXT &&
|
||||
key_code != NX_KEYTYPE_PREVIOUS && key_code != NX_KEYTYPE_FAST &&
|
||||
key_code != NX_KEYTYPE_REWIND) {
|
||||
key_code != NX_KEYTYPE_REWIND &&
|
||||
key_code != NX_KEYTYPE_SOUND_UP &&
|
||||
key_code != NX_KEYTYPE_SOUND_DOWN &&
|
||||
key_code != NX_KEYTYPE_MUTE) {
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -353,8 +368,8 @@ CGEventRef GlobalShortcutListenerMac::EventTapCallback(
|
||||
if (!is_key_pressed)
|
||||
return event;
|
||||
|
||||
// Now we have a media key that we care about. Send it to the caller.
|
||||
bool was_handled = shortcut_listener->OnMediaKeyEvent(key_code);
|
||||
// Now we have a media/volume key that we care about. Send it to the caller.
|
||||
bool was_handled = shortcut_listener->OnMediaOrVolumeKeyEvent(key_code);
|
||||
|
||||
// Prevent event from proagating to other apps if handled by Chrome.
|
||||
if (was_handled)
|
||||
|
||||
343
chromium_src/chrome/browser/speech/tts_controller.h
Normal file
343
chromium_src/chrome/browser/speech/tts_controller.h
Normal file
@@ -0,0 +1,343 @@
|
||||
// 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 <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#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();
|
||||
|
||||
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.
|
||||
scoped_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_
|
||||
464
chromium_src/chrome/browser/speech/tts_controller_impl.cc
Normal file
464
chromium_src/chrome/browser/speech/tts_controller_impl.cc
Normal file
@@ -0,0 +1,464 @@
|
||||
// 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/float_util.h"
|
||||
#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() {}
|
||||
|
||||
|
||||
//
|
||||
// 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 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_;
|
||||
}
|
||||
104
chromium_src/chrome/browser/speech/tts_controller_impl.h
Normal file
104
chromium_src/chrome/browser/speech/tts_controller_impl.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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 <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#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
|
||||
virtual bool IsSpeaking() OVERRIDE;
|
||||
virtual void SpeakOrEnqueue(Utterance* utterance) OVERRIDE;
|
||||
virtual void Stop() OVERRIDE;
|
||||
virtual void Pause() OVERRIDE;
|
||||
virtual void Resume() OVERRIDE;
|
||||
virtual void OnTtsEvent(int utterance_id,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) OVERRIDE;
|
||||
virtual void GetVoices(content::BrowserContext* browser_context,
|
||||
std::vector<VoiceData>* out_voices) OVERRIDE;
|
||||
virtual void VoicesChanged() OVERRIDE;
|
||||
virtual void AddVoicesChangedDelegate(
|
||||
VoicesChangedDelegate* delegate) OVERRIDE;
|
||||
virtual void RemoveVoicesChangedDelegate(
|
||||
VoicesChangedDelegate* delegate) OVERRIDE;
|
||||
virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) OVERRIDE;
|
||||
virtual TtsEngineDelegate* GetTtsEngineDelegate() OVERRIDE;
|
||||
virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) OVERRIDE;
|
||||
virtual int QueueSize() OVERRIDE;
|
||||
|
||||
protected:
|
||||
TtsControllerImpl();
|
||||
virtual ~TtsControllerImpl();
|
||||
|
||||
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 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_
|
||||
347
chromium_src/chrome/browser/speech/tts_linux.cc
Normal file
347
chromium_src/chrome/browser/speech/tts_linux.cc
Normal file
@@ -0,0 +1,347 @@
|
||||
// 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 "base/command_line.h"
|
||||
#include "base/debug/leak_annotations.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
#include "content/public/browser/browser_thread.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:
|
||||
virtual bool PlatformImplAvailable() OVERRIDE;
|
||||
virtual bool Speak(
|
||||
int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) OVERRIDE;
|
||||
virtual bool StopSpeaking() OVERRIDE;
|
||||
virtual void Pause() OVERRIDE;
|
||||
virtual void Resume() OVERRIDE;
|
||||
virtual bool IsSpeaking() OVERRIDE;
|
||||
virtual void GetVoices(std::vector<VoiceData>* out_voices) OVERRIDE;
|
||||
|
||||
void OnSpeechEvent(SPDNotificationType type);
|
||||
|
||||
// Get the single instance of this class.
|
||||
static TtsPlatformImplLinux* GetInstance();
|
||||
|
||||
private:
|
||||
TtsPlatformImplLinux();
|
||||
virtual ~TtsPlatformImplLinux();
|
||||
|
||||
// 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.
|
||||
scoped_ptr<std::map<std::string, SPDChromeVoice> > all_native_voices_;
|
||||
|
||||
friend struct DefaultSingletonTraits<TtsPlatformImplLinux>;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
|
||||
};
|
||||
|
||||
// static
|
||||
SPDNotificationType TtsPlatformImplLinux::current_notification_ =
|
||||
SPD_EVENT_END;
|
||||
|
||||
TtsPlatformImplLinux::TtsPlatformImplLinux()
|
||||
: utterance_id_(0) {
|
||||
BrowserThread::PostTask(BrowserThread::FILE,
|
||||
FROM_HERE,
|
||||
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_SINGLE);
|
||||
}
|
||||
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_SINGLE);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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 Singleton<TtsPlatformImplLinux,
|
||||
LeakySingletonTraits<TtsPlatformImplLinux> >::get();
|
||||
}
|
||||
|
||||
// static
|
||||
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
|
||||
return TtsPlatformImplLinux::GetInstance();
|
||||
}
|
||||
352
chromium_src/chrome/browser/speech/tts_mac.mm
Normal file
352
chromium_src/chrome/browser/speech/tts_mac.mm
Normal file
@@ -0,0 +1,352 @@
|
||||
// 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:
|
||||
virtual bool PlatformImplAvailable() OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool Speak(
|
||||
int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) OVERRIDE;
|
||||
|
||||
virtual bool StopSpeaking() OVERRIDE;
|
||||
|
||||
virtual void Pause() OVERRIDE;
|
||||
|
||||
virtual void Resume() OVERRIDE;
|
||||
|
||||
virtual bool IsSpeaking() OVERRIDE;
|
||||
|
||||
virtual 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();
|
||||
virtual ~TtsPlatformImplMac();
|
||||
|
||||
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 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 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
|
||||
176
chromium_src/chrome/browser/speech/tts_message_filter.cc
Normal file
176
chromium_src/chrome/browser/speech/tts_message_filter.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
// 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));
|
||||
|
||||
scoped_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);
|
||||
}
|
||||
64
chromium_src/chrome/browser/speech/tts_message_filter.h
Normal file
64
chromium_src/chrome/browser/speech/tts_message_filter.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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.
|
||||
virtual void OverrideThreadForMessage(
|
||||
const IPC::Message& message,
|
||||
content::BrowserThread::ID* thread) OVERRIDE;
|
||||
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
||||
virtual void OnChannelClosing() OVERRIDE;
|
||||
virtual void OnDestruct() const OVERRIDE;
|
||||
|
||||
// UtteranceEventDelegate implementation.
|
||||
virtual void OnTtsEvent(Utterance* utterance,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) OVERRIDE;
|
||||
|
||||
// VoicesChangedDelegate implementation.
|
||||
virtual void OnVoicesChanged() OVERRIDE;
|
||||
|
||||
private:
|
||||
friend class content::BrowserThread;
|
||||
friend class base::DeleteHelper<TtsMessageFilter>;
|
||||
|
||||
virtual ~TtsMessageFilter();
|
||||
|
||||
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_
|
||||
28
chromium_src/chrome/browser/speech/tts_platform.cc
Normal file
28
chromium_src/chrome/browser/speech/tts_platform.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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) {
|
||||
}
|
||||
81
chromium_src/chrome/browser/speech/tts_platform.h
Normal file
81
chromium_src/chrome/browser/speech/tts_platform.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// 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_
|
||||
257
chromium_src/chrome/browser/speech/tts_win.cc
Normal file
257
chromium_src/chrome/browser/speech/tts_win.cc
Normal file
@@ -0,0 +1,257 @@
|
||||
// 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 <sapi.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_comptr.h"
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
|
||||
class TtsPlatformImplWin : public TtsPlatformImpl {
|
||||
public:
|
||||
virtual bool PlatformImplAvailable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool Speak(
|
||||
int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params);
|
||||
|
||||
virtual bool StopSpeaking();
|
||||
|
||||
virtual void Pause();
|
||||
|
||||
virtual void Resume();
|
||||
|
||||
virtual bool IsSpeaking();
|
||||
|
||||
virtual 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();
|
||||
virtual ~TtsPlatformImplWin() {}
|
||||
|
||||
void OnSpeechEvent();
|
||||
|
||||
base::win::ScopedComPtr<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_;
|
||||
|
||||
friend struct 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;
|
||||
|
||||
// TODO(dmazzoni): support languages other than the default: crbug.com/88059
|
||||
|
||||
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>(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>(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) {
|
||||
// TODO: get all voices, not just default voice.
|
||||
// http://crbug.com/88059
|
||||
out_voices->push_back(VoiceData());
|
||||
VoiceData& voice = out_voices->back();
|
||||
voice.native = true;
|
||||
voice.name = "native";
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TtsPlatformImplWin::TtsPlatformImplWin()
|
||||
: utterance_id_(0),
|
||||
prefix_len_(0),
|
||||
stream_number_(0),
|
||||
char_position_(0),
|
||||
paused_(false) {
|
||||
speech_synthesizer_.CreateInstance(CLSID_SpVoice);
|
||||
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 Singleton<TtsPlatformImplWin,
|
||||
LeakySingletonTraits<TtsPlatformImplWin> >::get();
|
||||
}
|
||||
|
||||
// static
|
||||
void TtsPlatformImplWin::SpeechEventCallback(
|
||||
WPARAM w_param, LPARAM l_param) {
|
||||
GetInstance()->OnSpeechEvent();
|
||||
}
|
||||
69
chromium_src/chrome/common/tts_messages.h
Normal file
69
chromium_src/chrome/common/tts_messages.h
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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 */)
|
||||
30
chromium_src/chrome/common/tts_utterance_request.cc
Normal file
30
chromium_src/chrome/common/tts_utterance_request.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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() {
|
||||
}
|
||||
|
||||
TtsUtteranceResponse::TtsUtteranceResponse()
|
||||
: id(0) {
|
||||
}
|
||||
|
||||
TtsUtteranceResponse::~TtsUtteranceResponse() {
|
||||
}
|
||||
44
chromium_src/chrome/common/tts_utterance_request.h
Normal file
44
chromium_src/chrome/common/tts_utterance_request.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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/basictypes.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();
|
||||
|
||||
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_
|
||||
@@ -17,10 +17,10 @@
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "content/public/common/web_preferences.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_thread.h"
|
||||
#include "content/public/renderer/render_view.h"
|
||||
#include "content/public/renderer/web_preferences.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "printing/metafile.h"
|
||||
#include "printing/metafile_impl.h"
|
||||
@@ -42,7 +42,8 @@
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
#include "third_party/WebKit/public/web/WebViewClient.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
#include "webkit/common/webpreferences.h"
|
||||
|
||||
using content::WebPreferences;
|
||||
|
||||
namespace printing {
|
||||
|
||||
@@ -547,7 +548,7 @@ void PrepareFrameAndViewForPrint::CopySelection(
|
||||
|
||||
blink::WebView* web_view = blink::WebView::create(this);
|
||||
owns_web_view_ = true;
|
||||
content::ApplyWebPreferences(prefs, web_view);
|
||||
content::RenderView::ApplyWebPreferences(prefs, web_view);
|
||||
web_view->setMainFrame(blink::WebLocalFrame::create(this));
|
||||
frame_.Reset(web_view->mainFrame()->toWebLocalFrame());
|
||||
node_to_print_.reset();
|
||||
|
||||
200
chromium_src/chrome/renderer/tts_dispatcher.cc
Normal file
200
chromium_src/chrome/renderer/tts_dispatcher.cc
Normal file
@@ -0,0 +1,200 @@
|
||||
// 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/basictypes.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/WebKit/public/platform/WebCString.h"
|
||||
#include "third_party/WebKit/public/platform/WebSpeechSynthesisUtterance.h"
|
||||
#include "third_party/WebKit/public/platform/WebSpeechSynthesisVoice.h"
|
||||
#include "third_party/WebKit/public/platform/WebString.h"
|
||||
#include "third_party/WebKit/public/platform/WebVector.h"
|
||||
|
||||
using content::RenderThread;
|
||||
using blink::WebSpeechSynthesizerClient;
|
||||
using blink::WebSpeechSynthesisUtterance;
|
||||
using blink::WebSpeechSynthesisVoice;
|
||||
using blink::WebString;
|
||||
using blink::WebVector;
|
||||
|
||||
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.text().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);
|
||||
}
|
||||
78
chromium_src/chrome/renderer/tts_dispatcher.h
Normal file
78
chromium_src/chrome/renderer/tts_dispatcher.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// 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/basictypes.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/containers/hash_tables.h"
|
||||
#include "content/public/renderer/render_process_observer.h"
|
||||
#include "third_party/WebKit/public/platform/WebSpeechSynthesizer.h"
|
||||
#include "third_party/WebKit/public/platform/WebSpeechSynthesizerClient.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::RenderProcessObserver {
|
||||
public:
|
||||
explicit TtsDispatcher(blink::WebSpeechSynthesizerClient* client);
|
||||
|
||||
private:
|
||||
virtual ~TtsDispatcher();
|
||||
|
||||
// RenderProcessObserver override.
|
||||
virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE;
|
||||
|
||||
// blink::WebSpeechSynthesizer implementation.
|
||||
virtual void updateVoiceList() OVERRIDE;
|
||||
virtual void speak(const blink::WebSpeechSynthesisUtterance& utterance)
|
||||
OVERRIDE;
|
||||
virtual void pause() OVERRIDE;
|
||||
virtual void resume() OVERRIDE;
|
||||
virtual 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_
|
||||
231
chromium_src/library_loaders/libspeechd_loader.cc
Normal file
231
chromium_src/library_loaders/libspeechd_loader.cc
Normal file
@@ -0,0 +1,231 @@
|
||||
// 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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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<typeof(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;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
'variables': {
|
||||
'clang': 0,
|
||||
'openssl_no_asm': 1,
|
||||
'conditions': [
|
||||
['OS=="mac" or OS=="linux"', {
|
||||
'clang': 1,
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
* [Quick start](tutorial/quick-start.md)
|
||||
* [Application distribution](tutorial/application-distribution.md)
|
||||
* [Use native node modules](tutorial/use-native-node-modules.md)
|
||||
* [Application packaging](tutorial/application-packaging.md)
|
||||
* [Using native node modules](tutorial/using-native-node-modules.md)
|
||||
* [Debugging browser process](tutorial/debugging-browser-process.md)
|
||||
* [Using Selenium and WebDriver](tutorial/using-selenium-and-webdriver.md)
|
||||
* [DevTools extension](tutorial/devtools-extension.md)
|
||||
|
||||
## API references
|
||||
|
||||
@@ -45,6 +45,10 @@ terminating the application.
|
||||
See description of `window-all-closed` for the differences between `will-quit`
|
||||
and it.
|
||||
|
||||
## Event: quit
|
||||
|
||||
Emitted when application is quitting.
|
||||
|
||||
## Event: open-file
|
||||
|
||||
* `event` Event
|
||||
|
||||
@@ -80,6 +80,14 @@ normal browsers, see [Web Security](web-security.md) for more.
|
||||
should use `__dirname` or `process.resourcesPath` to join the paths to
|
||||
make them absolute, using relative paths would make atom-shell search
|
||||
under current working directory.
|
||||
* `experimental-features` Boolean
|
||||
* `experimental-canvas-features` Boolean
|
||||
* `subpixel-font-scaling` Boolean
|
||||
* `overlay-scrollbars` Boolean
|
||||
* `overlay-fullscreen-video` Boolean
|
||||
* `shared-worker` Boolean
|
||||
* `direct-write` Boolean - Whether the DirectWrite font rendering system on
|
||||
Windows is enabled
|
||||
|
||||
Creates a new `BrowserWindow` with native properties set by the `options`.
|
||||
Usually you only need to set the `width` and `height`, other properties will
|
||||
@@ -480,6 +488,19 @@ Sets the `menu` as the window top menu.
|
||||
|
||||
__Note:__ This API is not available on OS X.
|
||||
|
||||
### BrowserWindow.setProgressBar(progress)
|
||||
|
||||
* `progress` Double
|
||||
|
||||
Sets progress value in progress bar. Valid range is [0, 1.0].
|
||||
|
||||
Remove progress bar when progress < 0;
|
||||
Change to indeterminate mode when progress > 1.
|
||||
|
||||
On Linux platform, only supports Unity desktop environment, you need to specify
|
||||
the `*.desktop` file name to `desktopName` field in `package.json`. By default,
|
||||
it will assume `app.getName().desktop`.
|
||||
|
||||
## Class: WebContents
|
||||
|
||||
A `WebContents` is responsible for rendering and controlling a web page.
|
||||
|
||||
@@ -5,4 +5,5 @@ upstream node:
|
||||
|
||||
* `process.type` String - Process's type, can be `browser` or `renderer`.
|
||||
* `process.versions['atom-shell']` String - Version of atom-shell.
|
||||
* `process.versions['chrome']` String - Version of Chromium.
|
||||
* `process.resourcesPath` String - Path to JavaScript source codes.
|
||||
|
||||
@@ -48,6 +48,12 @@ Creates a new tray icon associated with the `image`.
|
||||
|
||||
Emitted when the tray icon is clicked.
|
||||
|
||||
### Event: 'double-clicked'
|
||||
|
||||
Emitted when the tray icon is double clicked.
|
||||
|
||||
This is only implmented on OS X.
|
||||
|
||||
### Tray.setImage(image)
|
||||
|
||||
* `image` [Image](image.md)
|
||||
@@ -64,8 +70,28 @@ Sets the `image` associated with this tray icon when pressed.
|
||||
|
||||
* `toolTip` String
|
||||
|
||||
Sets the hover text for this tray icon.
|
||||
|
||||
### Tray.setTitle(title)
|
||||
|
||||
* `title` String
|
||||
|
||||
Sets the title displayed aside of the tray icon in the status bar.
|
||||
|
||||
This is only implmented on OS X.
|
||||
|
||||
### Tray.setHighlightMode(highlight)
|
||||
|
||||
* `highlight` Boolean
|
||||
|
||||
Sets whether the tray icon is highlighted when it is clicked.
|
||||
|
||||
This is only implmented on OS X.
|
||||
|
||||
### Tray.setContextMenu(menu)
|
||||
|
||||
* `menu` Menu
|
||||
|
||||
Set the context menu for this icon.
|
||||
|
||||
[event-emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user