mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
266 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3260b42b18 | ||
|
|
f9c5d0796c | ||
|
|
2f113286aa | ||
|
|
686493e8ec | ||
|
|
1b3e6bdb92 | ||
|
|
0fe65a688e | ||
|
|
377666503c | ||
|
|
0411c2d2b6 | ||
|
|
839a751de5 | ||
|
|
a74d3d210d | ||
|
|
df86c9dac1 | ||
|
|
e3d67a304f | ||
|
|
77253abbd7 | ||
|
|
013a7717b4 | ||
|
|
aae4abfa86 | ||
|
|
abc6e1e289 | ||
|
|
dc154f3907 | ||
|
|
9f86fa1893 | ||
|
|
01b42c9e59 | ||
|
|
6bd0b82b2e | ||
|
|
391468ece0 | ||
|
|
5e05a3045c | ||
|
|
c2996d4fd1 | ||
|
|
d37bf06b5a | ||
|
|
1c07235121 | ||
|
|
59a2430f1d | ||
|
|
87221f8f55 | ||
|
|
e3215a1f21 | ||
|
|
2522bc2298 | ||
|
|
f3103e9b00 | ||
|
|
e897377879 | ||
|
|
6833865ff3 | ||
|
|
88dde07bf1 | ||
|
|
51f0090555 | ||
|
|
b8e75df8f0 | ||
|
|
de21a164ea | ||
|
|
95e1ea6599 | ||
|
|
2d2fbaaa4a | ||
|
|
7cfff62037 | ||
|
|
d61daf6f05 | ||
|
|
eca911a1b1 | ||
|
|
0a5a020466 | ||
|
|
79802c2919 | ||
|
|
3f1d540077 | ||
|
|
ff9027fa95 | ||
|
|
753a1e7aff | ||
|
|
4c8e483bc1 | ||
|
|
8404389a31 | ||
|
|
f8d6fab52b | ||
|
|
199e975a11 | ||
|
|
32f83d8e4e | ||
|
|
6ae3402aec | ||
|
|
21ccf80ba5 | ||
|
|
706c56bb30 | ||
|
|
c98e16b18f | ||
|
|
b386ec40be | ||
|
|
14cec9e20e | ||
|
|
120094a81e | ||
|
|
637b50044d | ||
|
|
acef33aa2c | ||
|
|
6134b9ed38 | ||
|
|
ce6f9f20bf | ||
|
|
b1f9c4dfc5 | ||
|
|
7660816468 | ||
|
|
1a43ec3557 | ||
|
|
4a3341e31e | ||
|
|
2f088c5caa | ||
|
|
38810c5518 | ||
|
|
876bfc69ac | ||
|
|
2ce1d3a784 | ||
|
|
a81ef7847d | ||
|
|
2117d06274 | ||
|
|
a5ec8a9110 | ||
|
|
30b3657c0e | ||
|
|
06a4f83bb7 | ||
|
|
7a8e43c65e | ||
|
|
90cc1a7062 | ||
|
|
7d93b4a48f | ||
|
|
a2ecb554cc | ||
|
|
f65f95e95c | ||
|
|
eaedac2536 | ||
|
|
8b9d35d84e | ||
|
|
99c0de6a1a | ||
|
|
f2bef6c26d | ||
|
|
1f57994e2a | ||
|
|
618040efc1 | ||
|
|
ed34aa6fb3 | ||
|
|
57639133a9 | ||
|
|
1b7c308475 | ||
|
|
2b2a55d870 | ||
|
|
c26a9b23a7 | ||
|
|
6c36f7e5c9 | ||
|
|
eb9673a152 | ||
|
|
fbe963c7f3 | ||
|
|
18f8af7822 | ||
|
|
3576c6d2ff | ||
|
|
b4ee01d43d | ||
|
|
679aa43113 | ||
|
|
2b82eafff4 | ||
|
|
94b3de557e | ||
|
|
c56480fd89 | ||
|
|
e9879b150e | ||
|
|
ea8d349b1b | ||
|
|
87b78a89fb | ||
|
|
27cd6688c1 | ||
|
|
c340cac02c | ||
|
|
0a9c371ca2 | ||
|
|
4a000a35c4 | ||
|
|
d89fb15daf | ||
|
|
d698ecf017 | ||
|
|
2b7b4a16f5 | ||
|
|
526aaecc52 | ||
|
|
e3d5b62000 | ||
|
|
6bd56f2a52 | ||
|
|
426e7645bc | ||
|
|
521fb7d54c | ||
|
|
4051d2ebdb | ||
|
|
b4fa3cd925 | ||
|
|
984d60f935 | ||
|
|
65f258160e | ||
|
|
a76183c188 | ||
|
|
fde4c544b8 | ||
|
|
274c9d04b1 | ||
|
|
07fc2b41af | ||
|
|
a9efe77ceb | ||
|
|
b932461b45 | ||
|
|
6b3ff63358 | ||
|
|
dd4e36a42e | ||
|
|
93687efee9 | ||
|
|
9ba7db8815 | ||
|
|
f4a2c12d75 | ||
|
|
74a83e3d7c | ||
|
|
283aa79c17 | ||
|
|
dc69c7b694 | ||
|
|
a78807b309 | ||
|
|
e7248b486f | ||
|
|
c2afa3521e | ||
|
|
6ae38d86df | ||
|
|
9f0d9e0fad | ||
|
|
45b626e0cc | ||
|
|
c13724b342 | ||
|
|
5da1e9ed10 | ||
|
|
a8913f5ea0 | ||
|
|
a59756485e | ||
|
|
6949af5427 | ||
|
|
da602a7c01 | ||
|
|
50eb5454d4 | ||
|
|
8ee1e1d208 | ||
|
|
73ec7783af | ||
|
|
a8f172752a | ||
|
|
b7e120b68c | ||
|
|
a85075103f | ||
|
|
fe30880ec0 | ||
|
|
c81128b675 | ||
|
|
2634328720 | ||
|
|
0df59e2714 | ||
|
|
18fdbb6432 | ||
|
|
bd51a4c8cf | ||
|
|
6478244fbf | ||
|
|
e0f660301c | ||
|
|
192014cc3f | ||
|
|
61b69a4e8a | ||
|
|
ec00da416f | ||
|
|
d4929de33c | ||
|
|
a0b15661ed | ||
|
|
9a58706e1f | ||
|
|
ce586e0835 | ||
|
|
795c31918a | ||
|
|
2592487c3f | ||
|
|
f05daa8bdc | ||
|
|
75ec34884d | ||
|
|
96cb4fd6d7 | ||
|
|
d63de1ae15 | ||
|
|
5250871e69 | ||
|
|
426bc97194 | ||
|
|
76098255f0 | ||
|
|
e8594b492e | ||
|
|
d4e362ec89 | ||
|
|
df399f7c8c | ||
|
|
ef8e5505c8 | ||
|
|
71fd147c47 | ||
|
|
c271d89677 | ||
|
|
7cf040e7ca | ||
|
|
a6c92f20c8 | ||
|
|
400e963b8b | ||
|
|
2a7244a366 | ||
|
|
5741bad918 | ||
|
|
1d7445c5aa | ||
|
|
4a1f8cf1c9 | ||
|
|
a2bf1b3907 | ||
|
|
b2d4d76bcf | ||
|
|
9ac555c302 | ||
|
|
43711083f7 | ||
|
|
9b07c2b894 | ||
|
|
2c28725722 | ||
|
|
53a4f34433 | ||
|
|
406f0b7bc7 | ||
|
|
6912a0513a | ||
|
|
0398577e93 | ||
|
|
42dc9c1ec6 | ||
|
|
2f798c5116 | ||
|
|
363f1da77e | ||
|
|
8655dec00e | ||
|
|
fbc68ecce5 | ||
|
|
8d4ac15834 | ||
|
|
249366c90a | ||
|
|
931182b677 | ||
|
|
2a129f892f | ||
|
|
57a27876ec | ||
|
|
fc2e069efe | ||
|
|
58d4ec5587 | ||
|
|
b0c23294fe | ||
|
|
290dd36bb0 | ||
|
|
84529de10d | ||
|
|
bde4a732c4 | ||
|
|
535b936c87 | ||
|
|
dc880b16f1 | ||
|
|
5eb1f52421 | ||
|
|
3747048218 | ||
|
|
a1a091d805 | ||
|
|
e536d0e7a8 | ||
|
|
e050494e5d | ||
|
|
bbc01e704c | ||
|
|
fff743053a | ||
|
|
e7b7efeb0a | ||
|
|
968fc71b78 | ||
|
|
d0494024a9 | ||
|
|
a12754f980 | ||
|
|
c76f510cea | ||
|
|
968f75529a | ||
|
|
93d5a2e195 | ||
|
|
367285f3f3 | ||
|
|
95b2003193 | ||
|
|
bcdc44c1c6 | ||
|
|
4ae2738ce5 | ||
|
|
e09a2ff811 | ||
|
|
7253a35455 | ||
|
|
1b1cf87115 | ||
|
|
3ec755e978 | ||
|
|
cf4616de7f | ||
|
|
2755fbaadf | ||
|
|
6f222651d7 | ||
|
|
fe4841a1cd | ||
|
|
fe8a2b9ee0 | ||
|
|
b939232670 | ||
|
|
a4253e3899 | ||
|
|
52b5f769f0 | ||
|
|
b73a114f8f | ||
|
|
7ca152070a | ||
|
|
f24ccd3841 | ||
|
|
66ac11ca5f | ||
|
|
7afef0fcdb | ||
|
|
627f487b36 | ||
|
|
c64a793364 | ||
|
|
63852a8c82 | ||
|
|
c97afdbdb3 | ||
|
|
6c5ea4ea32 | ||
|
|
cac3973731 | ||
|
|
19a35b58f0 | ||
|
|
5e51ad30d7 | ||
|
|
e1e5cb5ba4 | ||
|
|
8d124c7157 | ||
|
|
67ed72294b | ||
|
|
1bb5481071 | ||
|
|
876063b1ba | ||
|
|
7d193033f5 |
@@ -20,9 +20,12 @@
|
||||
#include "common/crash_reporter/win/crash_service_main.h"
|
||||
#include "content/public/app/startup_helper_win.h"
|
||||
#include "sandbox/win/src/sandbox_types.h"
|
||||
#else // defined(OS_WIN)
|
||||
#elif defined(OS_LINUX) // defined(OS_WIN)
|
||||
#include "app/atom_main_delegate.h" // NOLINT
|
||||
#include "content/public/app/content_main.h"
|
||||
#else // defined(OS_LINUX)
|
||||
#include "app/atom_library_main.h"
|
||||
#endif // defined(OS_MACOSX) || defined(OS_LINUX)
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
// Declaration of node::Start.
|
||||
namespace node {
|
||||
@@ -98,7 +101,18 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
|
||||
return content::ContentMain(instance, &sandbox_info, &delegate);
|
||||
}
|
||||
|
||||
#else // defined(OS_WIN)
|
||||
#elif defined(OS_LINUX) // defined(OS_WIN)
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE");
|
||||
if (node_indicator != NULL && strcmp(node_indicator, "1") == 0)
|
||||
return node::Start(argc, const_cast<char**>(argv));
|
||||
|
||||
atom::AtomMainDelegate delegate;
|
||||
return content::ContentMain(argc, argv, &delegate);
|
||||
}
|
||||
|
||||
#else // defined(OS_LINUX)
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
char* node_indicator = getenv("ATOM_SHELL_INTERNAL_RUN_AS_NODE");
|
||||
@@ -108,4 +122,4 @@ int main(int argc, const char* argv[]) {
|
||||
return AtomMain(argc, argv);
|
||||
}
|
||||
|
||||
#endif // defined(OS_MACOSX) || defined(OS_LINUX)
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "app/atom_main_delegate.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "base/logging.h"
|
||||
#include "browser/atom_browser_client.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
@@ -40,6 +41,11 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
|
||||
// Logging with pid and timestamp.
|
||||
logging::SetLogItems(true, false, true, false);
|
||||
|
||||
// Enable convient stack printing.
|
||||
#if defined(DEBUG) && defined(OS_LINUX)
|
||||
base::debug::EnableInProcessStackDumping();
|
||||
#endif
|
||||
|
||||
return brightray::MainDelegate::BasicStartupComplete(exit_code);
|
||||
}
|
||||
|
||||
@@ -51,16 +57,21 @@ void AtomMainDelegate::PreSandboxStartup() {
|
||||
InitializeResourceBundle();
|
||||
|
||||
CommandLine* command_line = CommandLine::ForCurrentProcess();
|
||||
std::string process_type = command_line->GetSwitchValueASCII(
|
||||
switches::kProcessType);
|
||||
|
||||
// Only append arguments for browser process.
|
||||
if (!process_type.empty())
|
||||
return;
|
||||
|
||||
// Add a flag to mark the start of switches added by atom-shell.
|
||||
command_line->AppendSwitch("atom-shell-switches-start");
|
||||
|
||||
// Disable renderer sandbox for most of node's functions.
|
||||
command_line->AppendSwitch(switches::kNoSandbox);
|
||||
|
||||
// Disable accelerated compositing since it caused a lot of troubles (black
|
||||
// devtools, screen flashes) and needed lots of effort to make it right.
|
||||
command_line->AppendSwitch(switches::kDisableAcceleratedCompositing);
|
||||
|
||||
// Add a flag to mark the end of switches added by atom-shell.
|
||||
command_line->AppendSwitch("no-more-switches");
|
||||
command_line->AppendSwitch("atom-shell-switches-end");
|
||||
}
|
||||
|
||||
void AtomMainDelegate::InitializeResourceBundle() {
|
||||
|
||||
196
atom.gyp
196
atom.gyp
@@ -8,10 +8,11 @@
|
||||
'app/atom_main.h',
|
||||
],
|
||||
'bundle_sources': [
|
||||
'browser/mac/atom.icns',
|
||||
'browser/resources/mac/atom.icns',
|
||||
],
|
||||
'coffee_sources': [
|
||||
'browser/api/lib/app.coffee',
|
||||
'browser/api/lib/atom-delegate.coffee',
|
||||
'browser/api/lib/auto-updater.coffee',
|
||||
'browser/api/lib/browser-window.coffee',
|
||||
'browser/api/lib/dialog.coffee',
|
||||
@@ -27,8 +28,11 @@
|
||||
'common/api/lib/clipboard.coffee',
|
||||
'common/api/lib/crash-reporter.coffee',
|
||||
'common/api/lib/id-weak-map.coffee',
|
||||
'common/api/lib/screen.coffee',
|
||||
'common/api/lib/shell.coffee',
|
||||
'common/lib/init.coffee',
|
||||
'renderer/lib/init.coffee',
|
||||
'renderer/lib/override.coffee',
|
||||
'renderer/api/lib/ipc.coffee',
|
||||
'renderer/api/lib/remote.coffee',
|
||||
],
|
||||
@@ -46,10 +50,10 @@
|
||||
'browser/api/atom_api_dialog.h',
|
||||
'browser/api/atom_api_event.cc',
|
||||
'browser/api/atom_api_event.h',
|
||||
'browser/api/atom_api_event_emitter.cc',
|
||||
'browser/api/atom_api_event_emitter.h',
|
||||
'browser/api/atom_api_menu.cc',
|
||||
'browser/api/atom_api_menu.h',
|
||||
'browser/api/atom_api_menu_gtk.cc',
|
||||
'browser/api/atom_api_menu_gtk.h',
|
||||
'browser/api/atom_api_menu_mac.h',
|
||||
'browser/api/atom_api_menu_mac.mm',
|
||||
'browser/api/atom_api_menu_win.cc',
|
||||
@@ -64,8 +68,8 @@
|
||||
'browser/api/atom_browser_bindings.h',
|
||||
'browser/auto_updater.cc',
|
||||
'browser/auto_updater.h',
|
||||
'browser/auto_updater_delegate.cc',
|
||||
'browser/auto_updater_delegate.h',
|
||||
'browser/auto_updater_linux.cc',
|
||||
'browser/auto_updater_mac.mm',
|
||||
'browser/auto_updater_win.cc',
|
||||
'browser/atom_application_mac.h',
|
||||
@@ -83,11 +87,16 @@
|
||||
'browser/atom_javascript_dialog_manager.h',
|
||||
'browser/browser.cc',
|
||||
'browser/browser.h',
|
||||
'browser/browser_linux.cc',
|
||||
'browser/browser_mac.mm',
|
||||
'browser/browser_win.cc',
|
||||
'browser/browser_observer.h',
|
||||
'browser/devtools_delegate.cc',
|
||||
'browser/devtools_delegate.h',
|
||||
'browser/native_window.cc',
|
||||
'browser/native_window.h',
|
||||
'browser/native_window_gtk.cc',
|
||||
'browser/native_window_gtk.h',
|
||||
'browser/native_window_mac.h',
|
||||
'browser/native_window_mac.mm',
|
||||
'browser/native_window_win.cc',
|
||||
@@ -103,20 +112,27 @@
|
||||
'browser/net/url_request_string_job.h',
|
||||
'browser/ui/accelerator_util.cc',
|
||||
'browser/ui/accelerator_util.h',
|
||||
'browser/ui/accelerator_util_gtk.cc',
|
||||
'browser/ui/accelerator_util_mac.mm',
|
||||
'browser/ui/accelerator_util_win.cc',
|
||||
'browser/ui/atom_event_processing_window.h',
|
||||
'browser/ui/atom_event_processing_window.mm',
|
||||
'browser/ui/atom_menu_controller_mac.h',
|
||||
'browser/ui/atom_menu_controller_mac.mm',
|
||||
'browser/ui/cocoa/atom_menu_controller.h',
|
||||
'browser/ui/cocoa/atom_menu_controller.mm',
|
||||
'browser/ui/cocoa/event_processing_window.h',
|
||||
'browser/ui/cocoa/event_processing_window.mm',
|
||||
'browser/ui/file_dialog.h',
|
||||
'browser/ui/file_dialog_gtk.cc',
|
||||
'browser/ui/file_dialog_mac.mm',
|
||||
'browser/ui/file_dialog_win.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu.h',
|
||||
'browser/ui/gtk/gtk_custom_menu_item.cc',
|
||||
'browser/ui/gtk/gtk_custom_menu_item.h',
|
||||
'browser/ui/gtk/gtk_window_util.cc',
|
||||
'browser/ui/gtk/gtk_window_util.h',
|
||||
'browser/ui/message_box.h',
|
||||
'browser/ui/message_box_gtk.cc',
|
||||
'browser/ui/message_box_mac.mm',
|
||||
'browser/ui/message_box_win.cc',
|
||||
'browser/ui/nsalert_synchronous_sheet_mac.h',
|
||||
'browser/ui/nsalert_synchronous_sheet_mac.mm',
|
||||
'browser/ui/win/menu_2.cc',
|
||||
'browser/ui/win/menu_2.h',
|
||||
'browser/ui/win/native_menu_win.cc',
|
||||
@@ -130,8 +146,12 @@
|
||||
'common/api/atom_api_clipboard.h',
|
||||
'common/api/atom_api_crash_reporter.cc',
|
||||
'common/api/atom_api_crash_reporter.h',
|
||||
'common/api/atom_api_event_emitter.cc',
|
||||
'common/api/atom_api_event_emitter.h',
|
||||
'common/api/atom_api_id_weak_map.cc',
|
||||
'common/api/atom_api_id_weak_map.h',
|
||||
'common/api/atom_api_screen.cc',
|
||||
'common/api/atom_api_screen.h',
|
||||
'common/api/atom_api_shell.cc',
|
||||
'common/api/atom_api_shell.h',
|
||||
'common/api/atom_api_v8_util.cc',
|
||||
@@ -143,18 +163,25 @@
|
||||
'common/api/object_life_monitor.h',
|
||||
'common/crash_reporter/crash_reporter.cc',
|
||||
'common/crash_reporter/crash_reporter.h',
|
||||
'common/crash_reporter/crash_reporter_linux.cc',
|
||||
'common/crash_reporter/crash_reporter_linux.h',
|
||||
'common/crash_reporter/crash_reporter_mac.h',
|
||||
'common/crash_reporter/crash_reporter_mac.mm',
|
||||
'common/crash_reporter/crash_reporter_win.cc',
|
||||
'common/crash_reporter/crash_reporter_win.h',
|
||||
'common/crash_reporter/linux/crash_dump_handler.cc',
|
||||
'common/crash_reporter/linux/crash_dump_handler.h',
|
||||
'common/crash_reporter/win/crash_service.cc',
|
||||
'common/crash_reporter/win/crash_service.h',
|
||||
'common/crash_reporter/win/crash_service_main.cc',
|
||||
'common/crash_reporter/win/crash_service_main.h',
|
||||
'common/draggable_region.cc',
|
||||
'common/draggable_region.h',
|
||||
'common/linux/application_info.cc',
|
||||
'common/node_bindings.cc',
|
||||
'common/node_bindings.h',
|
||||
'common/node_bindings_linux.cc',
|
||||
'common/node_bindings_linux.h',
|
||||
'common/node_bindings_mac.cc',
|
||||
'common/node_bindings_mac.h',
|
||||
'common/node_bindings_win.cc',
|
||||
@@ -162,6 +189,7 @@
|
||||
'common/options_switches.cc',
|
||||
'common/options_switches.h',
|
||||
'common/platform_util.h',
|
||||
'common/platform_util_linux.cc',
|
||||
'common/platform_util_mac.mm',
|
||||
'common/platform_util_win.cc',
|
||||
'common/swap_or_assign.h',
|
||||
@@ -186,20 +214,13 @@
|
||||
'conditions': [
|
||||
['OS=="win"', {
|
||||
'app_sources': [
|
||||
'app/win/resource.h',
|
||||
'app/win/atom.ico',
|
||||
'app/win/atom.rc',
|
||||
'browser/resources/win/resource.h',
|
||||
'browser/resources/win/atom.ico',
|
||||
'browser/resources/win/atom.rc',
|
||||
'<(libchromiumcontent_src_dir)/content/app/startup_helper_win.cc',
|
||||
],
|
||||
}], # OS=="win"
|
||||
],
|
||||
'fix_framework_link_command': [
|
||||
'install_name_tool',
|
||||
'-change',
|
||||
'@loader_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle',
|
||||
'@rpath/Sparkle.framework/Versions/A/Sparkle',
|
||||
'${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}'
|
||||
],
|
||||
'atom_source_root': '<!(python tools/atom_source_root.py)',
|
||||
},
|
||||
'target_defaults': {
|
||||
@@ -212,9 +233,8 @@
|
||||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'defines': [
|
||||
'DEBUG',
|
||||
],
|
||||
'defines': [ 'DEBUG' ],
|
||||
'cflags': [ '-g', '-O0' ],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -244,7 +264,7 @@
|
||||
'<(project_name)_helper',
|
||||
],
|
||||
'xcode_settings': {
|
||||
'INFOPLIST_FILE': 'browser/mac/Info.plist',
|
||||
'INFOPLIST_FILE': 'browser/resources/mac/Info.plist',
|
||||
'LD_RUNPATH_SEARCH_PATHS': [
|
||||
'@executable_path/../Frameworks',
|
||||
],
|
||||
@@ -258,7 +278,9 @@
|
||||
'files': [
|
||||
'<(PRODUCT_DIR)/<(product_name) Helper.app',
|
||||
'<(PRODUCT_DIR)/<(framework_name).framework',
|
||||
'frameworks/Sparkle.framework',
|
||||
'frameworks/Squirrel.framework',
|
||||
'frameworks/ReactiveCocoa.framework',
|
||||
'frameworks/Mantle.framework',
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -269,12 +291,6 @@
|
||||
},
|
||||
],
|
||||
'postbuilds': [
|
||||
{
|
||||
'postbuild_name': 'Fix Framework Link',
|
||||
'action': [
|
||||
'<@(fix_framework_link_command)',
|
||||
],
|
||||
},
|
||||
{
|
||||
# This postbuid step is responsible for creating the following
|
||||
# helpers:
|
||||
@@ -313,6 +329,24 @@
|
||||
},
|
||||
],
|
||||
}], # OS=="win"
|
||||
['OS=="linux"', {
|
||||
'copies': [
|
||||
{
|
||||
'destination': '<(PRODUCT_DIR)',
|
||||
'files': [
|
||||
'<(libchromiumcontent_library_dir)/libchromiumcontent.so',
|
||||
'<(libchromiumcontent_library_dir)/libffmpegsumo.so',
|
||||
'<(libchromiumcontent_resources_dir)/content_shell.pak',
|
||||
],
|
||||
},
|
||||
{
|
||||
'destination': '<(PRODUCT_DIR)/resources/browser',
|
||||
'files': [
|
||||
'browser/default_app',
|
||||
]
|
||||
},
|
||||
],
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}, # target <(project_name)
|
||||
{
|
||||
@@ -328,6 +362,7 @@
|
||||
'include_dirs': [
|
||||
'.',
|
||||
'vendor',
|
||||
'vendor/brightray',
|
||||
# Include directories for uv and node.
|
||||
'vendor/node/src',
|
||||
'vendor/node/deps/http_parser',
|
||||
@@ -360,12 +395,34 @@
|
||||
'vendor/breakpad/breakpad.gyp:breakpad_handler',
|
||||
'vendor/breakpad/breakpad.gyp:breakpad_sender',
|
||||
],
|
||||
}],
|
||||
}], # OS=="win"
|
||||
['OS=="mac"', {
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:breakpad',
|
||||
],
|
||||
}],
|
||||
}], # OS=="mac"
|
||||
['OS=="linux"', {
|
||||
'link_settings': {
|
||||
'ldflags': [
|
||||
# Make binary search for libraries under current directory, so we
|
||||
# don't have to manually set $LD_LIBRARY_PATH:
|
||||
# http://serverfault.com/questions/279068/cant-find-so-in-the-same-directory-as-the-executable
|
||||
'-rpath \$$ORIGIN',
|
||||
# Make native module dynamic loading work.
|
||||
'-rdynamic',
|
||||
],
|
||||
},
|
||||
# Required settings of using breakpad.
|
||||
'include_dirs': [
|
||||
'vendor/breakpad/src',
|
||||
],
|
||||
'cflags': [
|
||||
'-Wno-empty-body',
|
||||
],
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:breakpad_client',
|
||||
],
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}, # target <(product_name)_lib
|
||||
{
|
||||
@@ -392,8 +449,7 @@
|
||||
'<(RULE_INPUT_PATH)',
|
||||
'<(PRODUCT_DIR)/<(product_name).app/Contents/Resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
}], # OS=="mac"
|
||||
['OS=="win"', {
|
||||
},{ # OS=="mac"
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
@@ -403,7 +459,7 @@
|
||||
'<(RULE_INPUT_PATH)',
|
||||
'<(PRODUCT_DIR)/resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
}], # OS=="win"
|
||||
}], # OS=="win" or OS=="linux"
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -430,7 +486,7 @@
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/mac/generate_breakpad_symbols.py',
|
||||
'tools/posix/generate_breakpad_symbols.py',
|
||||
'--build-dir=<(PRODUCT_DIR)',
|
||||
'--binary=<(PRODUCT_DIR)/<(product_name).app/Contents/MacOS/<(product_name)',
|
||||
'--symbols-dir=<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
@@ -462,6 +518,49 @@
|
||||
},
|
||||
],
|
||||
}], # OS=="win"
|
||||
['OS=="linux"', {
|
||||
'dependencies': [
|
||||
'vendor/breakpad/breakpad.gyp:dump_syms',
|
||||
],
|
||||
'actions': [
|
||||
{
|
||||
'action_name': 'Dump Symbols',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/<(project_name)',
|
||||
],
|
||||
'outputs': [
|
||||
'<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
],
|
||||
'action': [
|
||||
'python',
|
||||
'tools/posix/generate_breakpad_symbols.py',
|
||||
'--build-dir=<(PRODUCT_DIR)',
|
||||
'--binary=<(PRODUCT_DIR)/<(project_name)',
|
||||
'--symbols-dir=<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
'--libchromiumcontent-dir=<(libchromiumcontent_library_dir)',
|
||||
'--clear',
|
||||
'--jobs=16',
|
||||
],
|
||||
},
|
||||
{
|
||||
'action_name': 'Strip Binary',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/libchromiumcontent.so',
|
||||
'<(PRODUCT_DIR)/libffmpegsumo.so',
|
||||
'<(PRODUCT_DIR)/<(project_name)',
|
||||
# Add the syms folder as input would force this action to run
|
||||
# after the 'Dump Symbols' action. And since it is a folder,
|
||||
# it would be ignored by the 'strip' command.
|
||||
'<(PRODUCT_DIR)/Atom-Shell.breakpad.syms',
|
||||
],
|
||||
'outputs': [
|
||||
# Gyp action requires a output file, add a fake one here.
|
||||
'<(PRODUCT_DIR)/dummy_file',
|
||||
],
|
||||
'action': [ 'strip', '<@(_inputs)' ],
|
||||
},
|
||||
],
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}, # target <(project_name>_dump_symbols
|
||||
],
|
||||
@@ -489,15 +588,18 @@
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
|
||||
'frameworks/Sparkle.framework',
|
||||
'frameworks/Squirrel.framework',
|
||||
'frameworks/ReactiveCocoa.framework',
|
||||
'frameworks/Mantle.framework',
|
||||
],
|
||||
},
|
||||
'mac_bundle': 1,
|
||||
'mac_bundle_resources': [
|
||||
'browser/mac/MainMenu.xib',
|
||||
'common/resources/mac/MainMenu.xib',
|
||||
'<(libchromiumcontent_resources_dir)/content_shell.pak',
|
||||
],
|
||||
'xcode_settings': {
|
||||
'INFOPLIST_FILE': 'common/resources/mac/Info.plist',
|
||||
'LIBRARY_SEARCH_PATHS': [
|
||||
'<(libchromiumcontent_library_dir)',
|
||||
],
|
||||
@@ -526,12 +628,6 @@
|
||||
},
|
||||
],
|
||||
'postbuilds': [
|
||||
{
|
||||
'postbuild_name': 'Fix Framework Link',
|
||||
'action': [
|
||||
'<@(fix_framework_link_command)',
|
||||
],
|
||||
},
|
||||
{
|
||||
'postbuild_name': 'Add symlinks for framework subdirectories',
|
||||
'action': [
|
||||
@@ -558,19 +654,11 @@
|
||||
],
|
||||
'mac_bundle': 1,
|
||||
'xcode_settings': {
|
||||
'INFOPLIST_FILE': 'renderer/mac/Info.plist',
|
||||
'INFOPLIST_FILE': 'renderer/resources/mac/Info.plist',
|
||||
'LD_RUNPATH_SEARCH_PATHS': [
|
||||
'@executable_path/../../..',
|
||||
],
|
||||
},
|
||||
'postbuilds': [
|
||||
{
|
||||
'postbuild_name': 'Fix Framework Link',
|
||||
'action': [
|
||||
'<@(fix_framework_link_command)',
|
||||
],
|
||||
},
|
||||
],
|
||||
}, # target helper
|
||||
],
|
||||
}], # OS==Mac
|
||||
|
||||
@@ -44,6 +44,10 @@ void App::OnOpenURL(const std::string& url) {
|
||||
Emit("open-url", &args);
|
||||
}
|
||||
|
||||
void App::OnActivateWithNoOpenWindows() {
|
||||
Emit("activate-with-no-open-windows");
|
||||
}
|
||||
|
||||
void App::OnWillFinishLaunching() {
|
||||
Emit("will-finish-launching");
|
||||
}
|
||||
@@ -174,8 +178,6 @@ void App::DockGetBadgeText(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// static
|
||||
void App::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("Application"));
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#define ATOM_BROWSER_API_ATOM_API_APP_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "browser/api/atom_api_event_emitter.h"
|
||||
#include "browser/browser_observer.h"
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -29,6 +29,7 @@ class App : public EventEmitter,
|
||||
virtual void OnOpenFile(bool* prevent_default,
|
||||
const std::string& file_path) OVERRIDE;
|
||||
virtual void OnOpenURL(const std::string& url) OVERRIDE;
|
||||
virtual void OnActivateWithNoOpenWindows() OVERRIDE;
|
||||
virtual void OnWillFinishLaunching() OVERRIDE;
|
||||
virtual void OnFinishLaunching() OVERRIDE;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "browser/api/atom_api_auto_updater.h"
|
||||
|
||||
#include "base/time/time.h"
|
||||
#include "base/values.h"
|
||||
#include "browser/auto_updater.h"
|
||||
#include "common/v8/native_type_conversions.h"
|
||||
@@ -17,38 +18,47 @@ namespace api {
|
||||
AutoUpdater::AutoUpdater(v8::Handle<v8::Object> wrapper)
|
||||
: EventEmitter(wrapper) {
|
||||
auto_updater::AutoUpdater::SetDelegate(this);
|
||||
auto_updater::AutoUpdater::Init();
|
||||
}
|
||||
|
||||
AutoUpdater::~AutoUpdater() {
|
||||
auto_updater::AutoUpdater::SetDelegate(NULL);
|
||||
}
|
||||
|
||||
void AutoUpdater::WillInstallUpdate(const std::string& version,
|
||||
const base::Closure& install) {
|
||||
continue_update_ = install;
|
||||
|
||||
void AutoUpdater::OnError(const std::string& error) {
|
||||
base::ListValue args;
|
||||
args.AppendString(version);
|
||||
bool prevent_default = Emit("will-install-update-raw", &args);
|
||||
|
||||
if (!prevent_default)
|
||||
install.Run();
|
||||
args.AppendString(error);
|
||||
Emit("error", &args);
|
||||
}
|
||||
|
||||
void AutoUpdater::ReadyForUpdateOnQuit(const std::string& version,
|
||||
const base::Closure& quit_and_install) {
|
||||
void AutoUpdater::OnCheckingForUpdate() {
|
||||
Emit("checking-for-update");
|
||||
}
|
||||
|
||||
void AutoUpdater::OnUpdateAvailable() {
|
||||
Emit("update-available");
|
||||
}
|
||||
|
||||
void AutoUpdater::OnUpdateNotAvailable() {
|
||||
Emit("update-not-available");
|
||||
}
|
||||
|
||||
void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url,
|
||||
const base::Closure& quit_and_install) {
|
||||
quit_and_install_ = quit_and_install;
|
||||
|
||||
base::ListValue args;
|
||||
args.AppendString(version);
|
||||
Emit("ready-for-update-on-quit-raw", &args);
|
||||
args.AppendString(release_notes);
|
||||
args.AppendString(release_name);
|
||||
args.AppendDouble(release_date.ToJsTime());
|
||||
args.AppendString(update_url);
|
||||
Emit("update-downloaded-raw", &args);
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
if (!args.IsConstructCall())
|
||||
return node::ThrowError("Require constructor call");
|
||||
|
||||
@@ -60,68 +70,30 @@ void AutoUpdater::SetFeedURL(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
auto_updater::AutoUpdater::SetFeedURL(FromV8Value(args[0]));
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetAutomaticallyChecksForUpdates(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
auto_updater::AutoUpdater::SetAutomaticallyChecksForUpdates(
|
||||
FromV8Value(args[0]));
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetAutomaticallyDownloadsUpdates(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
auto_updater::AutoUpdater::SetAutomaticallyDownloadsUpdates(
|
||||
FromV8Value(args[0]));
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
auto_updater::AutoUpdater::CheckForUpdates();
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdatesInBackground(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
auto_updater::AutoUpdater::CheckForUpdatesInBackground();
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::ContinueUpdate(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
AutoUpdater* self = AutoUpdater::Unwrap<AutoUpdater>(args.This());
|
||||
self->continue_update_.Run();
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::QuitAndInstall(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
AutoUpdater* self = AutoUpdater::Unwrap<AutoUpdater>(args.This());
|
||||
self->quit_and_install_.Run();
|
||||
|
||||
if (!self->quit_and_install_.is_null())
|
||||
self->quit_and_install_.Run();
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t(
|
||||
v8::FunctionTemplate::New(AutoUpdater::New));
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("AutoUpdater"));
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "setFeedUrl", SetFeedURL);
|
||||
NODE_SET_PROTOTYPE_METHOD(t,
|
||||
"setAutomaticallyChecksForUpdates",
|
||||
SetAutomaticallyChecksForUpdates);
|
||||
NODE_SET_PROTOTYPE_METHOD(t,
|
||||
"setAutomaticallyDownloadsUpdates",
|
||||
SetAutomaticallyDownloadsUpdates);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "checkForUpdates", CheckForUpdates);
|
||||
NODE_SET_PROTOTYPE_METHOD(t,
|
||||
"checkForUpdatesInBackground",
|
||||
CheckForUpdatesInBackground);
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "continueUpdate", ContinueUpdate);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "quitAndInstall", QuitAndInstall);
|
||||
|
||||
target->Set(v8::String::NewSymbol("AutoUpdater"), t->GetFunction());
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "browser/api/atom_api_event_emitter.h"
|
||||
#include "browser/auto_updater_delegate.h"
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
@@ -24,28 +24,27 @@ class AutoUpdater : public EventEmitter,
|
||||
protected:
|
||||
explicit AutoUpdater(v8::Handle<v8::Object> wrapper);
|
||||
|
||||
virtual void WillInstallUpdate(const std::string& version,
|
||||
const base::Closure& install) OVERRIDE;
|
||||
virtual void ReadyForUpdateOnQuit(
|
||||
const std::string& version,
|
||||
// AutoUpdaterDelegate implementations.
|
||||
virtual void OnError(const std::string& error) OVERRIDE;
|
||||
virtual void OnCheckingForUpdate() OVERRIDE;
|
||||
virtual void OnUpdateAvailable() OVERRIDE;
|
||||
virtual void OnUpdateNotAvailable() OVERRIDE;
|
||||
virtual void OnUpdateDownloaded(
|
||||
const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url,
|
||||
const base::Closure& quit_and_install) OVERRIDE;
|
||||
|
||||
private:
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void SetFeedURL(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetAutomaticallyChecksForUpdates(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void SetAutomaticallyDownloadsUpdates(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void CheckForUpdates(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void CheckForUpdatesInBackground(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void ContinueUpdate(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void QuitAndInstall(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
base::Closure continue_update_;
|
||||
base::Closure quit_and_install_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoUpdater);
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace {
|
||||
|
||||
template<typename T>
|
||||
void CallV8Function(const RefCountedV8Function& callback, T arg) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Handle<v8::Value> value = ToV8Value(arg);
|
||||
callback->NewHandle(node_isolate)->Call(
|
||||
v8::Context::GetCurrent()->Global(), 1, &value);
|
||||
@@ -33,8 +36,6 @@ void CallV8Function2(const RefCountedV8Function& callback, bool result, T arg) {
|
||||
}
|
||||
|
||||
void Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
NODE_SET_METHOD(target, "showMessageBox", ShowMessageBox);
|
||||
NODE_SET_METHOD(target, "showOpenDialog", ShowOpenDialog);
|
||||
NODE_SET_METHOD(target, "showSaveDialog", ShowSaveDialog);
|
||||
|
||||
@@ -24,6 +24,7 @@ v8::Handle<v8::Value> CallDelegate(v8::Handle<v8::Value> default_value,
|
||||
v8::Handle<v8::Object> menu,
|
||||
const char* method,
|
||||
int command_id) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Handle<v8::Value> delegate = menu->Get(v8::String::New("delegate"));
|
||||
@@ -52,6 +53,7 @@ Menu::~Menu() {
|
||||
}
|
||||
|
||||
bool Menu::IsCommandIdChecked(int command_id) const {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
return CallDelegate(v8::False(),
|
||||
const_cast<Menu*>(this)->handle(),
|
||||
@@ -60,6 +62,7 @@ bool Menu::IsCommandIdChecked(int command_id) const {
|
||||
}
|
||||
|
||||
bool Menu::IsCommandIdEnabled(int command_id) const {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
return CallDelegate(v8::True(),
|
||||
const_cast<Menu*>(this)->handle(),
|
||||
@@ -68,6 +71,7 @@ bool Menu::IsCommandIdEnabled(int command_id) const {
|
||||
}
|
||||
|
||||
bool Menu::IsCommandIdVisible(int command_id) const {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
return CallDelegate(v8::True(),
|
||||
const_cast<Menu*>(this)->handle(),
|
||||
@@ -77,6 +81,7 @@ bool Menu::IsCommandIdVisible(int command_id) const {
|
||||
|
||||
bool Menu::GetAcceleratorForCommandId(int command_id,
|
||||
ui::Accelerator* accelerator) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
v8::Handle<v8::Value> shortcut = CallDelegate(v8::Undefined(),
|
||||
handle(),
|
||||
@@ -91,6 +96,7 @@ bool Menu::GetAcceleratorForCommandId(int command_id,
|
||||
}
|
||||
|
||||
bool Menu::IsItemForCommandIdDynamic(int command_id) const {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
return CallDelegate(v8::False(),
|
||||
const_cast<Menu*>(this)->handle(),
|
||||
@@ -99,6 +105,7 @@ bool Menu::IsItemForCommandIdDynamic(int command_id) const {
|
||||
}
|
||||
|
||||
string16 Menu::GetLabelForCommandId(int command_id) const {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
return FromV8Value(CallDelegate(v8::False(),
|
||||
const_cast<Menu*>(this)->handle(),
|
||||
@@ -107,6 +114,7 @@ string16 Menu::GetLabelForCommandId(int command_id) const {
|
||||
}
|
||||
|
||||
string16 Menu::GetSublabelForCommandId(int command_id) const {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
return FromV8Value(CallDelegate(v8::False(),
|
||||
const_cast<Menu*>(this)->handle(),
|
||||
@@ -115,14 +123,13 @@ string16 Menu::GetSublabelForCommandId(int command_id) const {
|
||||
}
|
||||
|
||||
void Menu::ExecuteCommand(int command_id, int event_flags) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
CallDelegate(v8::False(), handle(), "executeCommand", command_id);
|
||||
}
|
||||
|
||||
// static
|
||||
void Menu::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
if (!args.IsConstructCall())
|
||||
return node::ThrowError("Require constructor call");
|
||||
|
||||
@@ -307,8 +314,6 @@ void Menu::Popup(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// static
|
||||
void Menu::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t(v8::FunctionTemplate::New(Menu::New));
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("Menu"));
|
||||
@@ -335,7 +340,7 @@ void Menu::Initialize(v8::Handle<v8::Object> target) {
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "popup", Popup);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#if defined(OS_WIN) || defined(TOOLKIT_GTK)
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "attachToWindow", AttachToWindow);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define ATOM_BROWSER_API_ATOM_API_MENU_H_
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "browser/api/atom_api_event_emitter.h"
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
#include "ui/base/models/simple_menu_model.h"
|
||||
|
||||
namespace atom {
|
||||
@@ -69,7 +69,7 @@ class Menu : public EventEmitter,
|
||||
|
||||
static void Popup(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#if defined(OS_WIN) || defined(TOOLKIT_GTK)
|
||||
static void AttachToWindow(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
#elif defined(OS_MACOSX)
|
||||
static void SetApplicationMenu(
|
||||
|
||||
32
browser/api/atom_api_menu_gtk.cc
Normal file
32
browser/api/atom_api_menu_gtk.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/api/atom_api_menu_gtk.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
MenuGtk::MenuGtk(v8::Handle<v8::Object> wrapper)
|
||||
: Menu(wrapper) {
|
||||
}
|
||||
|
||||
MenuGtk::~MenuGtk() {
|
||||
}
|
||||
|
||||
void MenuGtk::Popup(NativeWindow* native_window) {
|
||||
}
|
||||
|
||||
// static
|
||||
void Menu::AttachToWindow(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
}
|
||||
|
||||
// static
|
||||
Menu* Menu::Create(v8::Handle<v8::Object> wrapper) {
|
||||
return new MenuGtk(wrapper);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
30
browser/api/atom_api_menu_gtk.h
Normal file
30
browser/api/atom_api_menu_gtk.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_MENU_GTK_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_MENU_GTK_H_
|
||||
|
||||
#include "browser/api/atom_api_menu.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class MenuGtk : public Menu {
|
||||
public:
|
||||
explicit MenuGtk(v8::Handle<v8::Object> wrapper);
|
||||
virtual ~MenuGtk();
|
||||
|
||||
protected:
|
||||
virtual void Popup(NativeWindow* window) OVERRIDE;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MenuGtk);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_MENU_GTK_H_
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "browser/api/atom_api_menu.h"
|
||||
|
||||
#import "browser/ui/atom_menu_controller_mac.h"
|
||||
#import "browser/ui/cocoa/atom_menu_controller.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
|
||||
@@ -74,8 +74,6 @@ void MenuMac::SendActionToFirstResponder(const std::string& action) {
|
||||
|
||||
// static
|
||||
void Menu::SetApplicationMenu(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
if (!args[0]->IsObject())
|
||||
return node::ThrowTypeError("Bad argument");
|
||||
|
||||
@@ -94,8 +92,6 @@ void Menu::SetApplicationMenu(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
// static
|
||||
void Menu::SendActionToFirstResponder(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
std::string action;
|
||||
if (!FromV8Arguments(args, &action))
|
||||
return node::ThrowTypeError("Bad argument");
|
||||
|
||||
@@ -31,8 +31,6 @@ void MenuWin::Popup(NativeWindow* native_window) {
|
||||
|
||||
// static
|
||||
void Menu::AttachToWindow(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
Menu* self = ObjectWrap::Unwrap<Menu>(args.This());
|
||||
if (self == NULL)
|
||||
return node::ThrowError("Menu is already destroyed");
|
||||
|
||||
@@ -39,8 +39,6 @@ void PowerMonitor::OnResume() {
|
||||
|
||||
// static
|
||||
void PowerMonitor::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
if (!args.IsConstructCall())
|
||||
return node::ThrowError("Require constructor call");
|
||||
|
||||
@@ -49,8 +47,6 @@ void PowerMonitor::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// static
|
||||
void PowerMonitor::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
base::PowerMonitorDeviceSource::AllocateSystemIOPorts();
|
||||
#endif
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_POWER_MONITOR_H_
|
||||
|
||||
#include "browser/api/atom_api_event_emitter.h"
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/power_monitor/power_observer.h"
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ static const char* kEarlyUseProtocolError = "This method can only be used"
|
||||
|
||||
// Emit an event for the protocol module.
|
||||
void EmitEventInUI(const std::string& event, const std::string& parameter) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Handle<v8::Value> argv[] = {
|
||||
@@ -72,6 +73,7 @@ class CustomProtocolRequestJob : public AdapterRequestJob {
|
||||
virtual void GetJobTypeInUI() OVERRIDE {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
// Call the JS handler.
|
||||
@@ -366,6 +368,10 @@ void Protocol::Initialize(v8::Handle<v8::Object> target) {
|
||||
// Remember the protocol object, used for emitting event later.
|
||||
g_protocol_object.reset(target);
|
||||
|
||||
// Make sure the job factory has been created.
|
||||
AtomBrowserContext::Get()->url_request_context_getter()->
|
||||
GetURLRequestContext();
|
||||
|
||||
NODE_SET_METHOD(target, "registerProtocol", RegisterProtocol);
|
||||
NODE_SET_METHOD(target, "unregisterProtocol", UnregisterProtocol);
|
||||
NODE_SET_METHOD(target, "isHandledProtocol", IsHandledProtocol);
|
||||
|
||||
@@ -92,6 +92,7 @@ void Window::OnRendererCrashed() {
|
||||
|
||||
void Window::OnCapturePageDone(const RefCountedV8Function& callback,
|
||||
const std::vector<unsigned char>& data) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Local<v8::Value> buffer = node::Buffer::New(
|
||||
@@ -103,8 +104,6 @@ void Window::OnCapturePageDone(const RefCountedV8Function& callback,
|
||||
|
||||
// static
|
||||
void Window::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
if (!args.IsConstructCall())
|
||||
return node::ThrowError("Require constructor call");
|
||||
|
||||
@@ -132,7 +131,7 @@ void Window::Destroy(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
// renderer process became a zombie.
|
||||
base::MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::Bind(base::IgnoreResult(base::KillProcess), 0, false, handle),
|
||||
base::Bind(base::IgnoreResult(base::KillProcess), handle, 0, false),
|
||||
base::TimeDelta::FromMilliseconds(5000));
|
||||
}
|
||||
|
||||
@@ -415,6 +414,13 @@ void Window::InspectElement(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
self->window_->InspectElement(x, y);
|
||||
}
|
||||
|
||||
// static
|
||||
void Window::DebugDevTools(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
UNWRAP_WINDOW_AND_CHECK;
|
||||
if (self->window_->IsDevToolsOpened())
|
||||
NativeWindow::Debug(self->window_->GetDevToolsWebContents());
|
||||
}
|
||||
|
||||
// static
|
||||
void Window::FocusOnWebView(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
UNWRAP_WINDOW_AND_CHECK;
|
||||
@@ -625,8 +631,6 @@ void Window::ReloadIgnoringCache(
|
||||
|
||||
// static
|
||||
void Window::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(Window::New);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("BrowserWindow"));
|
||||
@@ -666,6 +670,7 @@ void Window::Initialize(v8::Handle<v8::Object> target) {
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "closeDevTools", CloseDevTools);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "isDevToolsOpened", IsDevToolsOpened);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "inspectElement", InspectElement);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "debugDevTools", DebugDevTools);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "focusOnWebView", FocusOnWebView);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "blurWebView", BlurWebView);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "isWebViewFocused", IsWebViewFocused);
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "browser/api/atom_api_event_emitter.h"
|
||||
#include "browser/native_window_observer.h"
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
#include "common/v8/scoped_persistent.h"
|
||||
|
||||
namespace base {
|
||||
@@ -86,6 +86,7 @@ class Window : public EventEmitter,
|
||||
static void CloseDevTools(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void IsDevToolsOpened(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void InspectElement(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void DebugDevTools(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void FocusOnWebView(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void BlurWebView(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void IsWebViewFocused(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
@@ -25,6 +25,7 @@ void AtomBrowserBindings::OnRendererMessage(int process_id,
|
||||
int routing_id,
|
||||
const string16& channel,
|
||||
const base::ListValue& args) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
scoped_ptr<V8ValueConverter> converter(new V8ValueConverter);
|
||||
@@ -58,6 +59,7 @@ void AtomBrowserBindings::OnRendererMessageSync(
|
||||
const base::ListValue& args,
|
||||
NativeWindow* sender,
|
||||
IPC::Message* message) {
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
scoped_ptr<V8ValueConverter> converter(new V8ValueConverter);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
BrowserWindow = require 'browser-window'
|
||||
EventEmitter = require('events').EventEmitter
|
||||
|
||||
bindings = process.atomBinding 'app'
|
||||
|
||||
6
browser/api/lib/atom-delegate.coffee
Normal file
6
browser/api/lib/atom-delegate.coffee
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports =
|
||||
browserMainParts:
|
||||
preMainMessageLoopRun: ->
|
||||
|
||||
setImmediate ->
|
||||
module.exports.browserMainParts.preMainMessageLoopRun()
|
||||
@@ -4,9 +4,22 @@ EventEmitter = require('events').EventEmitter
|
||||
AutoUpdater::__proto__ = EventEmitter.prototype
|
||||
|
||||
autoUpdater = new AutoUpdater
|
||||
autoUpdater.on 'will-install-update-raw', (event, version) ->
|
||||
@emit 'will-install-update', event, version, => @continueUpdate()
|
||||
autoUpdater.on 'ready-for-update-on-quit-raw', (event, version) ->
|
||||
@emit 'ready-for-update-on-quit', event, version, => @quitAndInstall()
|
||||
autoUpdater.on 'update-downloaded-raw', (args...) ->
|
||||
args[3] = new Date(args[3]) # releaseDate
|
||||
@emit 'update-downloaded', args..., => @quitAndInstall()
|
||||
|
||||
autoUpdater.quitAndInstall = ->
|
||||
# If we don't have any window then quitAndInstall immediately.
|
||||
BrowserWindow = require 'browser-window'
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
if windows.length is 0
|
||||
AutoUpdater::quitAndInstall.call this
|
||||
return
|
||||
|
||||
# Do the restart after all windows have been closed.
|
||||
app = require 'app'
|
||||
app.removeAllListeners 'window-all-closed'
|
||||
app.once 'window-all-closed', AutoUpdater::quitAndInstall.bind(this)
|
||||
win.close() for win in windows
|
||||
|
||||
module.exports = autoUpdater
|
||||
|
||||
@@ -34,7 +34,8 @@ BrowserWindow::restart = ->
|
||||
@loadUrl(@getUrl())
|
||||
|
||||
BrowserWindow::setMenu = (menu) ->
|
||||
throw new Error('BrowserWindow.setMenu is only available on Windows') unless process.platform is 'win32'
|
||||
if process.platform is 'darwin'
|
||||
throw new Error('BrowserWindow.setMenu is not available on OS X')
|
||||
|
||||
throw new TypeError('Invalid menu') unless menu?.constructor?.name is 'Menu'
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ sendWrap = (channel, processId, routingId, args...) ->
|
||||
BrowserWindow = require 'browser-window'
|
||||
if processId?.constructor is BrowserWindow
|
||||
window = processId
|
||||
args = [routingId, args...]
|
||||
processId = window.getProcessId()
|
||||
routingId = window.getRoutingId()
|
||||
|
||||
|
||||
@@ -35,4 +35,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
|
||||
hasVisibleWindows:(BOOL)flag {
|
||||
atom::Browser* browser = atom::Browser::Get();
|
||||
if (flag) {
|
||||
return YES;
|
||||
} else {
|
||||
browser->ActivateWithNoOpenWindows();
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,12 +6,36 @@
|
||||
|
||||
#include "browser/atom_browser_context.h"
|
||||
#include "browser/atom_browser_main_parts.h"
|
||||
#include "browser/native_window.h"
|
||||
#include "browser/net/atom_url_request_context_getter.h"
|
||||
#include "browser/window_list.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/site_instance.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "webkit/common/webpreferences.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AtomBrowserClient::AtomBrowserClient() {
|
||||
namespace {
|
||||
|
||||
struct FindByProcessId {
|
||||
explicit FindByProcessId(int child_process_id)
|
||||
: child_process_id_(child_process_id) {
|
||||
}
|
||||
|
||||
bool operator() (NativeWindow* const window) {
|
||||
int id = window->GetWebContents()->GetRenderProcessHost()->GetID();
|
||||
return id == child_process_id_;
|
||||
}
|
||||
|
||||
int child_process_id_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomBrowserClient::AtomBrowserClient()
|
||||
: dying_render_process_(NULL) {
|
||||
}
|
||||
|
||||
AtomBrowserClient::~AtomBrowserClient() {
|
||||
@@ -29,7 +53,7 @@ void AtomBrowserClient::OverrideWebkitPrefs(
|
||||
const GURL& url,
|
||||
WebPreferences* prefs) {
|
||||
prefs->javascript_enabled = true;
|
||||
prefs->web_security_enabled = false;
|
||||
prefs->web_security_enabled = true;
|
||||
prefs->javascript_can_open_windows_automatically = true;
|
||||
prefs->plugins_enabled = false;
|
||||
prefs->dom_paste_enabled = true;
|
||||
@@ -44,14 +68,52 @@ void AtomBrowserClient::OverrideWebkitPrefs(
|
||||
prefs->experimental_webgl_enabled = false;
|
||||
prefs->allow_displaying_insecure_content = true;
|
||||
prefs->allow_running_insecure_content = true;
|
||||
|
||||
NativeWindow* window = NativeWindow::FromRenderView(
|
||||
render_view_host->GetProcess()->GetID(),
|
||||
render_view_host->GetRoutingID());
|
||||
if (window)
|
||||
window->OverrideWebkitPrefs(url, prefs);
|
||||
}
|
||||
|
||||
bool AtomBrowserClient::ShouldSwapProcessesForNavigation(
|
||||
content::SiteInstance* site_instance,
|
||||
const GURL& current_url,
|
||||
const GURL& new_url) {
|
||||
// Restart renderer process if navigating to the same url.
|
||||
return current_url == new_url;
|
||||
if (site_instance->HasProcess())
|
||||
dying_render_process_ = site_instance->GetProcess();
|
||||
|
||||
// Restart renderer process for all navigations.
|
||||
return true;
|
||||
}
|
||||
|
||||
void AtomBrowserClient::AppendExtraCommandLineSwitches(
|
||||
CommandLine* command_line,
|
||||
int child_process_id) {
|
||||
WindowList* list = WindowList::GetInstance();
|
||||
NativeWindow* window = NULL;
|
||||
|
||||
// Find the owner of this child process.
|
||||
WindowList::const_iterator iter = std::find_if(
|
||||
list->begin(), list->end(), FindByProcessId(child_process_id));
|
||||
if (iter != list->end())
|
||||
window = *iter;
|
||||
|
||||
// If the render process is a newly started one, which means the window still
|
||||
// uses the old going-to-be-swapped render process, then we try to find the
|
||||
// window from the swapped render process.
|
||||
if (window == NULL && dying_render_process_ != NULL) {
|
||||
child_process_id = dying_render_process_->GetID();
|
||||
WindowList::const_iterator iter = std::find_if(
|
||||
list->begin(), list->end(), FindByProcessId(child_process_id));
|
||||
if (iter != list->end())
|
||||
window = *iter;
|
||||
}
|
||||
|
||||
if (window != NULL)
|
||||
window->AppendExtraCommandLineSwitches(command_line, child_process_id);
|
||||
|
||||
dying_render_process_ = NULL;
|
||||
}
|
||||
|
||||
brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts(
|
||||
|
||||
@@ -25,11 +25,16 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
||||
content::SiteInstance* site_instance,
|
||||
const GURL& current_url,
|
||||
const GURL& new_url) OVERRIDE;
|
||||
virtual void AppendExtraCommandLineSwitches(CommandLine* command_line,
|
||||
int child_process_id) OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
|
||||
const content::MainFunctionParams&) OVERRIDE;
|
||||
|
||||
// The render process which would be swapped out soon.
|
||||
content::RenderProcessHost* dying_render_process_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient);
|
||||
};
|
||||
|
||||
|
||||
@@ -61,7 +61,8 @@ AtomURLRequestContextGetter* AtomBrowserContext::CreateRequestContext(
|
||||
GetPath(),
|
||||
BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::IO),
|
||||
BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::FILE),
|
||||
CreateNetworkDelegate().Pass(),
|
||||
base::Bind(&AtomBrowserContext::CreateNetworkDelegate,
|
||||
base::Unretained(this)),
|
||||
protocol_handlers);
|
||||
|
||||
resource_context_->set_url_request_context_getter(url_request_getter_.get());
|
||||
|
||||
@@ -51,6 +51,7 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||
v8::V8::Initialize();
|
||||
|
||||
// Create context.
|
||||
v8::Locker locker(node_isolate);
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(node_isolate);
|
||||
|
||||
|
||||
@@ -8,6 +8,18 @@
|
||||
|
||||
namespace atom {
|
||||
|
||||
void AtomJavaScriptDialogManager::RunJavaScriptDialog(
|
||||
content::WebContents* web_contents,
|
||||
const GURL& origin_url,
|
||||
const std::string& accept_lang,
|
||||
content::JavaScriptMessageType javascript_message_type,
|
||||
const string16& message_text,
|
||||
const string16& default_prompt_text,
|
||||
const DialogClosedCallback& callback,
|
||||
bool* did_suppress_message) {
|
||||
callback.Run(false, string16());
|
||||
}
|
||||
|
||||
void AtomJavaScriptDialogManager::RunBeforeUnloadDialog(
|
||||
content::WebContents* web_contents,
|
||||
const string16& message_text,
|
||||
|
||||
@@ -20,7 +20,7 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager {
|
||||
const string16& message_text,
|
||||
const string16& default_prompt_text,
|
||||
const DialogClosedCallback& callback,
|
||||
bool* did_suppress_message) OVERRIDE {}
|
||||
bool* did_suppress_message) OVERRIDE;
|
||||
virtual void RunBeforeUnloadDialog(
|
||||
content::WebContents* web_contents,
|
||||
const string16& message_text,
|
||||
|
||||
@@ -19,13 +19,8 @@ class AutoUpdater {
|
||||
static AutoUpdaterDelegate* GetDelegate();
|
||||
static void SetDelegate(AutoUpdaterDelegate* delegate);
|
||||
|
||||
static void Init();
|
||||
|
||||
static void SetFeedURL(const std::string& url);
|
||||
static void SetAutomaticallyChecksForUpdates(bool yes);
|
||||
static void SetAutomaticallyDownloadsUpdates(bool yes);
|
||||
static void CheckForUpdates();
|
||||
static void CheckForUpdatesInBackground();
|
||||
|
||||
private:
|
||||
static AutoUpdaterDelegate* delegate_;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/auto_updater_delegate.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
void AutoUpdaterDelegate::WillInstallUpdate(const std::string& version,
|
||||
const base::Closure& install) {
|
||||
install.Run();
|
||||
}
|
||||
|
||||
void AutoUpdaterDelegate::ReadyForUpdateOnQuit(
|
||||
const std::string& version,
|
||||
const base::Closure& quit_and_install) {
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
||||
@@ -9,17 +9,32 @@
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
|
||||
namespace base {
|
||||
class Time;
|
||||
}
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
class AutoUpdaterDelegate {
|
||||
public:
|
||||
// The application is going to relaunch to install update.
|
||||
virtual void WillInstallUpdate(const std::string& version,
|
||||
const base::Closure& install);
|
||||
// An error happened.
|
||||
virtual void OnError(const std::string& error) {}
|
||||
|
||||
// User has chosen to update on quit.
|
||||
virtual void ReadyForUpdateOnQuit(const std::string& version,
|
||||
const base::Closure& quit_and_install);
|
||||
// Checking to see if there is an update
|
||||
virtual void OnCheckingForUpdate() {}
|
||||
|
||||
// There is an update available and it is being downloaded
|
||||
virtual void OnUpdateAvailable() {}
|
||||
|
||||
// There is no available update.
|
||||
virtual void OnUpdateNotAvailable() {}
|
||||
|
||||
// There is a new update which has been downloaded.
|
||||
virtual void OnUpdateDownloaded(const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url,
|
||||
const base::Closure& quit_and_install) {}
|
||||
|
||||
protected:
|
||||
virtual ~AutoUpdaterDelegate() {}
|
||||
|
||||
17
browser/auto_updater_linux.cc
Normal file
17
browser/auto_updater_linux.cc
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/auto_updater.h"
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
||||
@@ -4,106 +4,89 @@
|
||||
|
||||
#include "browser/auto_updater.h"
|
||||
|
||||
// Sparkle's headers are throwing compilation warnings, supress them.
|
||||
#pragma GCC diagnostic ignored "-Wmissing-method-return-type"
|
||||
#import <Sparkle/Sparkle.h>
|
||||
#import <ReactiveCocoa/RACCommand.h>
|
||||
#import <ReactiveCocoa/RACSignal.h>
|
||||
#import <ReactiveCocoa/NSObject+RACPropertySubscribing.h>
|
||||
#import <Squirrel/Squirrel.h>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "browser/auto_updater_delegate.h"
|
||||
|
||||
using auto_updater::AutoUpdaterDelegate;
|
||||
#include <iostream>
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
namespace {
|
||||
|
||||
struct NSInvocationDeleter {
|
||||
inline void operator()(NSInvocation* invocation) const {
|
||||
[invocation release];
|
||||
}
|
||||
};
|
||||
// The gloal SQRLUpdater object.
|
||||
SQRLUpdater* g_updater = nil;
|
||||
|
||||
typedef scoped_ptr<NSInvocation, NSInvocationDeleter> ScopedNSInvocation;
|
||||
|
||||
// We are passing the NSInvocation as scoped_ptr, because we want to make sure
|
||||
// whether or not the callback is called, the NSInvocation should alwasy be
|
||||
// released, the only way to ensure it is to use scoped_ptr.
|
||||
void CallNSInvocation(ScopedNSInvocation invocation) {
|
||||
[invocation.get() invoke];
|
||||
void RelaunchToInstallUpdate() {
|
||||
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
|
||||
AutoUpdaterDelegate* delegate = AutoUpdater::GetDelegate();
|
||||
if (delegate)
|
||||
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
|
||||
}];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface SUUpdaterDelegate : NSObject {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SUUpdaterDelegate
|
||||
|
||||
- (BOOL)updater:(SUUpdater*)updater
|
||||
shouldPostponeRelaunchForUpdate:(SUAppcastItem*)update
|
||||
untilInvoking:(NSInvocation*)invocation {
|
||||
AutoUpdaterDelegate* delegate = auto_updater::AutoUpdater::GetDelegate();
|
||||
if (!delegate)
|
||||
return NO;
|
||||
|
||||
std::string version(base::SysNSStringToUTF8([update versionString]));
|
||||
ScopedNSInvocation invocation_ptr([invocation retain]);
|
||||
delegate->WillInstallUpdate(
|
||||
version,
|
||||
base::Bind(&CallNSInvocation, base::Passed(invocation_ptr.Pass())));
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)updater:(SUUpdater*)updater
|
||||
willInstallUpdateOnQuit:(SUAppcastItem*)update
|
||||
immediateInstallationInvocation:(NSInvocation*)invocation {
|
||||
AutoUpdaterDelegate* delegate = auto_updater::AutoUpdater::GetDelegate();
|
||||
if (!delegate)
|
||||
return;
|
||||
|
||||
std::string version(base::SysNSStringToUTF8([update versionString]));
|
||||
ScopedNSInvocation invocation_ptr([invocation retain]);
|
||||
delegate->ReadyForUpdateOnQuit(
|
||||
version,
|
||||
base::Bind(&CallNSInvocation, base::Passed(invocation_ptr.Pass())));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
// static
|
||||
void AutoUpdater::Init() {
|
||||
SUUpdaterDelegate* delegate = [[SUUpdaterDelegate alloc] init];
|
||||
[[SUUpdater sharedUpdater] setDelegate:delegate];
|
||||
}
|
||||
void AutoUpdater::SetFeedURL(const std::string& feed) {
|
||||
if (g_updater == nil) {
|
||||
// Initialize the SQRLUpdater.
|
||||
NSURL* url = [NSURL URLWithString:base::SysUTF8ToNSString(feed)];
|
||||
NSURLRequest* urlRequest = [NSURLRequest requestWithURL:url];
|
||||
g_updater = [[SQRLUpdater alloc] initWithUpdateRequest:urlRequest];
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
NSString* url_str(base::SysUTF8ToNSString(url));
|
||||
[[SUUpdater sharedUpdater] setFeedURL:[NSURL URLWithString:url_str]];
|
||||
}
|
||||
AutoUpdaterDelegate* delegate = GetDelegate();
|
||||
if (!delegate)
|
||||
return;
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetAutomaticallyChecksForUpdates(bool yes) {
|
||||
[[SUUpdater sharedUpdater] setAutomaticallyChecksForUpdates:yes];
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetAutomaticallyDownloadsUpdates(bool yes) {
|
||||
[[SUUpdater sharedUpdater] setAutomaticallyDownloadsUpdates:yes];
|
||||
[[g_updater rac_valuesForKeyPath:@"state" observer:g_updater]
|
||||
subscribeNext:^(NSNumber *stateNumber) {
|
||||
int state = [stateNumber integerValue];
|
||||
if (state == SQRLUpdaterStateCheckingForUpdate) {
|
||||
delegate->OnCheckingForUpdate();
|
||||
} else if (state == SQRLUpdaterStateDownloadingUpdate) {
|
||||
delegate->OnUpdateAvailable();
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
[[SUUpdater sharedUpdater] checkForUpdates:nil];
|
||||
}
|
||||
AutoUpdaterDelegate* delegate = GetDelegate();
|
||||
if (!delegate)
|
||||
return;
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdatesInBackground() {
|
||||
[[SUUpdater sharedUpdater] checkForUpdatesInBackground];
|
||||
[[[[g_updater.checkForUpdatesCommand
|
||||
execute:nil]
|
||||
// Send a `nil` after everything...
|
||||
concat:[RACSignal return:nil]]
|
||||
// But only take the first value. If an update is sent, we'll get that.
|
||||
// Otherwise, we'll get our inserted `nil` value.
|
||||
take:1]
|
||||
subscribeNext:^(SQRLDownloadedUpdate *downloadedUpdate) {
|
||||
if (downloadedUpdate) {
|
||||
SQRLUpdate* update = downloadedUpdate.update;
|
||||
// There is a new update that has been downloaded.
|
||||
delegate->OnUpdateDownloaded(
|
||||
base::SysNSStringToUTF8(update.releaseNotes),
|
||||
base::SysNSStringToUTF8(update.releaseName),
|
||||
base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970),
|
||||
base::SysNSStringToUTF8(update.updateURL.absoluteString),
|
||||
base::Bind(RelaunchToInstallUpdate));
|
||||
} else {
|
||||
// When the completed event is sent with no update, then we know there
|
||||
// is no update available.
|
||||
delegate->OnUpdateNotAvailable();
|
||||
}
|
||||
} error:^(NSError *error) {
|
||||
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
|
||||
}];
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
||||
|
||||
@@ -6,28 +6,12 @@
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
// static
|
||||
void AutoUpdater::Init() {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetAutomaticallyChecksForUpdates(bool yes) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetAutomaticallyDownloadsUpdates(bool yes) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdatesInBackground() {
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
||||
|
||||
@@ -74,6 +74,10 @@ void Browser::OpenURL(const std::string& url) {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnOpenURL(url));
|
||||
}
|
||||
|
||||
void Browser::ActivateWithNoOpenWindows() {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnActivateWithNoOpenWindows());
|
||||
}
|
||||
|
||||
void Browser::WillFinishLaunching() {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnWillFinishLaunching());
|
||||
}
|
||||
|
||||
@@ -62,6 +62,9 @@ class Browser : public WindowListObserver {
|
||||
// Tell the application to open a url.
|
||||
void OpenURL(const std::string& url);
|
||||
|
||||
// Tell the application that application is activated with no open windows.
|
||||
void ActivateWithNoOpenWindows();
|
||||
|
||||
// Tell the application the loading has been done.
|
||||
void WillFinishLaunching();
|
||||
void DidFinishLaunching();
|
||||
|
||||
44
browser/browser_linux.cc
Normal file
44
browser/browser_linux.cc
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/browser.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "browser/native_window.h"
|
||||
#include "browser/window_list.h"
|
||||
#include "common/atom_version.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
void Browser::Terminate() {
|
||||
is_quiting_ = true;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void Browser::Focus() {
|
||||
// Focus on the first visible window.
|
||||
WindowList* list = WindowList::GetInstance();
|
||||
for (WindowList::iterator iter = list->begin(); iter != list->end(); ++iter) {
|
||||
NativeWindow* window = *iter;
|
||||
if (window->IsVisible()) {
|
||||
window->Focus(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Browser::GetExecutableFileVersion() const {
|
||||
return ATOM_VERSION_STRING;
|
||||
}
|
||||
|
||||
std::string Browser::GetExecutableFileProductName() const {
|
||||
return "Atom-Shell";
|
||||
}
|
||||
|
||||
void Browser::CancelQuit() {
|
||||
// No way to cancel quit on Linux.
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
@@ -26,6 +26,10 @@ class BrowserObserver {
|
||||
// Browser is used to open a url.
|
||||
virtual void OnOpenURL(const std::string& url) {}
|
||||
|
||||
// The browser is activated with no open windows (usually by clicking on the
|
||||
// dock icon).
|
||||
virtual void OnActivateWithNoOpenWindows() {}
|
||||
|
||||
// The browser has finished loading.
|
||||
virtual void OnWillFinishLaunching() {}
|
||||
virtual void OnFinishLaunching() {}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
var app = require('app');
|
||||
var dialog = require('dialog');
|
||||
var path = require('path');
|
||||
var optimist = require('optimist');
|
||||
|
||||
@@ -9,7 +8,7 @@ app.on('window-all-closed', function() {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
var argv = optimist(process.argv.slice(1)).argv;
|
||||
var argv = optimist(process.argv.slice(1)).boolean('ci').argv;
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
|
||||
122
browser/devtools_delegate.cc
Normal file
122
browser/devtools_delegate.cc
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/devtools_delegate.h"
|
||||
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/values.h"
|
||||
#include "browser/native_window.h"
|
||||
#include "content/public/browser/devtools_agent_host.h"
|
||||
#include "content/public/browser/devtools_client_host.h"
|
||||
#include "content/public/browser/devtools_http_handler.h"
|
||||
#include "content/public/browser/devtools_manager.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "ui/gfx/point.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
DevToolsDelegate::DevToolsDelegate(NativeWindow* window,
|
||||
content::WebContents* target_web_contents)
|
||||
: content::WebContentsObserver(window->GetWebContents()),
|
||||
owner_window_(window),
|
||||
delegate_(NULL),
|
||||
embedder_message_dispatcher_(
|
||||
new DevToolsEmbedderMessageDispatcher(this)) {
|
||||
content::WebContents* web_contents = window->GetWebContents();
|
||||
|
||||
// Setup devtools.
|
||||
devtools_agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(
|
||||
target_web_contents->GetRenderViewHost());
|
||||
devtools_client_host_.reset(
|
||||
content::DevToolsClientHost::CreateDevToolsFrontendHost(web_contents,
|
||||
this));
|
||||
content::DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor(
|
||||
devtools_agent_host_.get(), devtools_client_host_.get());
|
||||
|
||||
// Go!
|
||||
base::DictionaryValue options;
|
||||
options.SetString("title", "DevTools Debugger");
|
||||
window->InitFromOptions(&options);
|
||||
window->AddObserver(this);
|
||||
web_contents->GetController().LoadURL(
|
||||
GURL("chrome-devtools://devtools/devtools.html?dockSide=undocked"),
|
||||
content::Referrer(),
|
||||
content::PAGE_TRANSITION_AUTO_TOPLEVEL,
|
||||
std::string());
|
||||
}
|
||||
|
||||
DevToolsDelegate::~DevToolsDelegate() {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::DispatchOnEmbedder(const std::string& message) {
|
||||
embedder_message_dispatcher_->Dispatch(message);
|
||||
}
|
||||
|
||||
void DevToolsDelegate::InspectedContentsClosing() {
|
||||
owner_window_->Close();
|
||||
}
|
||||
|
||||
void DevToolsDelegate::AboutToNavigateRenderView(
|
||||
content::RenderViewHost* render_view_host) {
|
||||
content::DevToolsClientHost::SetupDevToolsFrontendClient(
|
||||
owner_window_->GetWebContents()->GetRenderViewHost());
|
||||
}
|
||||
|
||||
void DevToolsDelegate::OnWindowClosed() {
|
||||
base::MessageLoop::current()->DeleteSoon(FROM_HERE, owner_window_);
|
||||
}
|
||||
|
||||
void DevToolsDelegate::ActivateWindow() {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::CloseWindow() {
|
||||
owner_window_->Close();
|
||||
}
|
||||
|
||||
void DevToolsDelegate::MoveWindow(int x, int y) {
|
||||
owner_window_->SetPosition(gfx::Point(x, y));
|
||||
}
|
||||
|
||||
void DevToolsDelegate::SetDockSide(const std::string& dock_side) {
|
||||
bool succeed = true;
|
||||
if (delegate_ &&
|
||||
delegate_->DevToolsSetDockSide("attach-back", &succeed) &&
|
||||
succeed)
|
||||
owner_window_->Close();
|
||||
}
|
||||
|
||||
void DevToolsDelegate::OpenInNewTab(const std::string& url) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::SaveToFile(
|
||||
const std::string& url, const std::string& content, bool save_as) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::AppendToFile(
|
||||
const std::string& url, const std::string& content) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::RequestFileSystems() {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::AddFileSystem() {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::RemoveFileSystem(const std::string& file_system_path) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::IndexPath(
|
||||
int request_id, const std::string& file_system_path) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::StopIndexing(int request_id) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::SearchInPath(
|
||||
int request_id,
|
||||
const std::string& file_system_path,
|
||||
const std::string& query) {
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
85
browser/devtools_delegate.h
Normal file
85
browser/devtools_delegate.h
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_DEVTOOLS_DELEGATE_H_
|
||||
#define ATOM_BROWSER_DEVTOOLS_DELEGATE_H_
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "browser/native_window_observer.h"
|
||||
#include "content/public/browser/devtools_frontend_host_delegate.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "vendor/brightray/browser/devtools_embedder_message_dispatcher.h"
|
||||
#include "vendor/brightray/browser/inspectable_web_contents_delegate.h"
|
||||
|
||||
namespace content {
|
||||
class DevToolsAgentHost;
|
||||
class DevToolsClientHost;
|
||||
}
|
||||
|
||||
using brightray::DevToolsEmbedderMessageDispatcher;
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindow;
|
||||
|
||||
class DevToolsDelegate : public content::DevToolsFrontendHostDelegate,
|
||||
public content::WebContentsObserver,
|
||||
public NativeWindowObserver,
|
||||
public DevToolsEmbedderMessageDispatcher::Delegate {
|
||||
public:
|
||||
DevToolsDelegate(NativeWindow* window,
|
||||
content::WebContents* target_web_contents);
|
||||
virtual ~DevToolsDelegate();
|
||||
|
||||
void SetDelegate(brightray::InspectableWebContentsDelegate* delegate) {
|
||||
delegate_ = delegate;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Implementations of content::DevToolsFrontendHostDelegate.
|
||||
virtual void DispatchOnEmbedder(const std::string& message) OVERRIDE;
|
||||
virtual void InspectedContentsClosing() OVERRIDE;
|
||||
|
||||
// Implementations of content::WebContentsObserver.
|
||||
virtual void AboutToNavigateRenderView(
|
||||
content::RenderViewHost* render_view_host) OVERRIDE;
|
||||
|
||||
// Implementations of NativeWindowObserver.
|
||||
virtual void OnWindowClosed() OVERRIDE;
|
||||
|
||||
// Implementations of DevToolsEmbedderMessageDispatcher::Delegate.
|
||||
virtual void ActivateWindow() OVERRIDE;
|
||||
virtual void CloseWindow() OVERRIDE;
|
||||
virtual void MoveWindow(int x, int y) OVERRIDE;
|
||||
virtual void SetDockSide(const std::string& dock_side) OVERRIDE;
|
||||
virtual void OpenInNewTab(const std::string& url) OVERRIDE;
|
||||
virtual void SaveToFile(const std::string& url,
|
||||
const std::string& content,
|
||||
bool save_as) OVERRIDE;
|
||||
virtual void AppendToFile(const std::string& url,
|
||||
const std::string& content) OVERRIDE;
|
||||
virtual void RequestFileSystems() OVERRIDE;
|
||||
virtual void AddFileSystem() OVERRIDE;
|
||||
virtual void RemoveFileSystem(const std::string& file_system_path) OVERRIDE;
|
||||
virtual void IndexPath(int request_id,
|
||||
const std::string& file_system_path) OVERRIDE;
|
||||
virtual void StopIndexing(int request_id) OVERRIDE;
|
||||
virtual void SearchInPath(int request_id,
|
||||
const std::string& file_system_path,
|
||||
const std::string& query) OVERRIDE;
|
||||
|
||||
private:
|
||||
NativeWindow* owner_window_;
|
||||
brightray::InspectableWebContentsDelegate* delegate_;
|
||||
|
||||
scoped_refptr<content::DevToolsAgentHost> devtools_agent_host_;
|
||||
scoped_ptr<content::DevToolsClientHost> devtools_client_host_;
|
||||
scoped_ptr<DevToolsEmbedderMessageDispatcher> embedder_message_dispatcher_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DevToolsDelegate);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_DEVTOOLS_DELEGATE_H_
|
||||
@@ -1,5 +1,6 @@
|
||||
fs = require 'fs'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
util = require 'util'
|
||||
|
||||
# Expose information of current process.
|
||||
process.__atom_type = 'browser'
|
||||
@@ -9,29 +10,36 @@ process.resourcesPath = path.resolve process.argv[1], '..', '..', '..'
|
||||
# we need to restore it here.
|
||||
process.argv.splice 1, 1
|
||||
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle output when running as GUI program.
|
||||
console.log = console.error = console.warn = process.log
|
||||
process.stdout.write = process.stderr.write = process.log
|
||||
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
# Pick out switches appended by atom-shell.
|
||||
startMark = process.argv.indexOf '--atom-shell-switches-start'
|
||||
endMark = process.argv.indexOf '--atom-shell-switches-end'
|
||||
process.execArgv = process.argv.splice startMark, endMark - startMark + 1
|
||||
|
||||
# Add browser/api/lib to require's search paths,
|
||||
# which contains javascript part of Atom's built-in libraries.
|
||||
globalPaths = require('module').globalPaths
|
||||
globalPaths.push path.join process.resourcesPath, 'browser', 'api', 'lib'
|
||||
|
||||
# And also common/api/lib
|
||||
globalPaths.push path.join process.resourcesPath, 'common', 'api', 'lib'
|
||||
|
||||
# Do loading in next tick since we still need some initialize work before
|
||||
# native bindings can work.
|
||||
setImmediate ->
|
||||
# Import common settings.
|
||||
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init.js')
|
||||
|
||||
if process.platform is 'win32'
|
||||
# Redirect node's console to use our own implementations, since node can not
|
||||
# handle console output when running as GUI program.
|
||||
print = (args...) ->
|
||||
process.log util.format(args...)
|
||||
console.log = console.error = console.warn = print
|
||||
process.stdout.write = process.stderr.write = print
|
||||
|
||||
# Always returns EOF for stdin stream.
|
||||
Readable = require('stream').Readable
|
||||
stdin = new Readable
|
||||
stdin.push null
|
||||
process.__defineGetter__ 'stdin', -> stdin
|
||||
|
||||
# Don't quit on fatal error.
|
||||
process.on 'uncaughtException', (error) ->
|
||||
# Show error in GUI.
|
||||
|
||||
@@ -6,18 +6,19 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/prefs/pref_service.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "brightray/browser/inspectable_web_contents.h"
|
||||
#include "brightray/browser/inspectable_web_contents_view.h"
|
||||
#include "browser/api/atom_browser_bindings.h"
|
||||
#include "browser/atom_browser_context.h"
|
||||
#include "browser/atom_browser_main_parts.h"
|
||||
#include "browser/atom_javascript_dialog_manager.h"
|
||||
#include "browser/browser.h"
|
||||
#include "browser/devtools_delegate.h"
|
||||
#include "browser/window_list.h"
|
||||
#include "content/public/browser/devtools_agent_host.h"
|
||||
#include "content/public/browser/invalidate_type.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#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/browser/web_contents_view.h"
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "common/api/api_messages.h"
|
||||
#include "common/atom_version.h"
|
||||
@@ -37,29 +39,49 @@
|
||||
#include "ui/gfx/point.h"
|
||||
#include "ui/gfx/rect.h"
|
||||
#include "ui/gfx/size.h"
|
||||
#include "vendor/brightray/browser/inspectable_web_contents.h"
|
||||
#include "vendor/brightray/browser/inspectable_web_contents_view.h"
|
||||
#include "webkit/common/user_agent/user_agent_util.h"
|
||||
#include "webkit/common/webpreferences.h"
|
||||
|
||||
using content::NavigationEntry;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kDockSidePref[] = "brightray.devtools.dockside";
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindow::NativeWindow(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options)
|
||||
: content::WebContentsObserver(web_contents),
|
||||
has_frame_(true),
|
||||
is_closed_(false),
|
||||
node_integration_("except-iframe"),
|
||||
has_dialog_attached_(false),
|
||||
weak_factory_(this),
|
||||
inspectable_web_contents_(
|
||||
brightray::InspectableWebContents::Create(web_contents)) {
|
||||
options->GetBoolean(switches::kFrame, &has_frame_);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// Temporary fix for flashing devtools, try removing this after upgraded to
|
||||
// Chrome 32.
|
||||
web_contents->GetView()->SetAllowOverlappingViews(false);
|
||||
#endif
|
||||
|
||||
// Read icon before window is created.
|
||||
std::string icon;
|
||||
if (options->GetString(switches::kIcon, &icon)) {
|
||||
if (!SetIcon(icon))
|
||||
LOG(ERROR) << "Failed to set icon to " << icon;
|
||||
}
|
||||
if (options->GetString(switches::kIcon, &icon) && !SetIcon(icon))
|
||||
LOG(ERROR) << "Failed to set icon to " << icon;
|
||||
|
||||
// Read iframe security before any navigation.
|
||||
options->GetString(switches::kNodeIntegration, &node_integration_);
|
||||
|
||||
web_contents->SetDelegate(this);
|
||||
inspectable_web_contents()->SetDelegate(this);
|
||||
|
||||
WindowList::AddWindow(this);
|
||||
|
||||
@@ -89,6 +111,14 @@ NativeWindow* NativeWindow::Create(base::DictionaryValue* options) {
|
||||
return Create(content::WebContents::Create(create_params), options);
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::Debug(content::WebContents* web_contents) {
|
||||
base::DictionaryValue options;
|
||||
NativeWindow* window = NativeWindow::Create(&options);
|
||||
window->devtools_delegate_.reset(new DevToolsDelegate(window, web_contents));
|
||||
return window;
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::FromRenderView(int process_id, int routing_id) {
|
||||
// Stupid iterating.
|
||||
@@ -155,16 +185,33 @@ void NativeWindow::InitFromOptions(base::DictionaryValue* options) {
|
||||
Show();
|
||||
}
|
||||
|
||||
bool NativeWindow::HasModalDialog() {
|
||||
return has_dialog_attached_;
|
||||
}
|
||||
|
||||
void NativeWindow::OpenDevTools() {
|
||||
inspectable_web_contents()->ShowDevTools();
|
||||
if (devtools_window_) {
|
||||
devtools_window_->Focus(true);
|
||||
} else {
|
||||
inspectable_web_contents()->ShowDevTools();
|
||||
#if defined(OS_MACOSX)
|
||||
// Temporary fix for flashing devtools, try removing this after upgraded to
|
||||
// Chrome 32.
|
||||
GetDevToolsWebContents()->GetView()->SetAllowOverlappingViews(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindow::CloseDevTools() {
|
||||
inspectable_web_contents()->GetView()->CloseDevTools();
|
||||
if (devtools_window_)
|
||||
devtools_window_->Close();
|
||||
else
|
||||
inspectable_web_contents()->GetView()->CloseDevTools();
|
||||
}
|
||||
|
||||
bool NativeWindow::IsDevToolsOpened() {
|
||||
return inspectable_web_contents()->IsDevToolsViewShowing();
|
||||
return (devtools_window_ && devtools_window_->IsFocused()) ||
|
||||
inspectable_web_contents()->IsDevToolsViewShowing();
|
||||
}
|
||||
|
||||
void NativeWindow::InspectElement(int x, int y) {
|
||||
@@ -213,8 +260,11 @@ base::ProcessHandle NativeWindow::GetRenderProcessHandle() {
|
||||
|
||||
void NativeWindow::CapturePage(const gfx::Rect& rect,
|
||||
const CapturePageCallback& callback) {
|
||||
gfx::Rect flipped_y_rect = rect;
|
||||
flipped_y_rect.set_y(-rect.y());
|
||||
|
||||
GetWebContents()->GetRenderViewHost()->CopyFromBackingStore(
|
||||
rect,
|
||||
flipped_y_rect,
|
||||
gfx::Size(),
|
||||
base::Bind(&NativeWindow::OnCapturePageDone,
|
||||
weak_factory_.GetWeakPtr(),
|
||||
@@ -237,7 +287,8 @@ void NativeWindow::CloseWebContents() {
|
||||
// not closed in 500ms, in this way we can quickly show the unresponsive
|
||||
// dialog when the window is busy executing some script withouth waiting for
|
||||
// the unresponsive timeout.
|
||||
if (window_unresposive_closure_.IsCancelled()) {
|
||||
if (!Browser::Get()->is_quiting() &&
|
||||
window_unresposive_closure_.IsCancelled()) {
|
||||
window_unresposive_closure_.Reset(
|
||||
base::Bind(&NativeWindow::RendererUnresponsive,
|
||||
weak_factory_.GetWeakPtr(),
|
||||
@@ -258,6 +309,23 @@ content::WebContents* NativeWindow::GetWebContents() const {
|
||||
return inspectable_web_contents_->GetWebContents();
|
||||
}
|
||||
|
||||
content::WebContents* NativeWindow::GetDevToolsWebContents() const {
|
||||
return inspectable_web_contents()->devtools_web_contents();
|
||||
}
|
||||
|
||||
void NativeWindow::AppendExtraCommandLineSwitches(CommandLine* command_line,
|
||||
int child_process_id) {
|
||||
// Append --node-integration to renderer process.
|
||||
command_line->AppendSwitchASCII(switches::kNodeIntegration,
|
||||
node_integration_);
|
||||
}
|
||||
|
||||
void NativeWindow::OverrideWebkitPrefs(const GURL& url, WebPreferences* prefs) {
|
||||
// FIXME Disable accelerated composition in frameless window.
|
||||
if (!has_frame_)
|
||||
prefs->accelerated_compositing_enabled = false;
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowClosed() {
|
||||
if (is_closed_)
|
||||
return;
|
||||
@@ -280,6 +348,30 @@ void NativeWindow::NotifyWindowBlur() {
|
||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowBlur());
|
||||
}
|
||||
|
||||
// In atom-shell all reloads and navigations started by renderer process would
|
||||
// be redirected to this method, so we can have precise control of how we
|
||||
// would open the url (in our case, is to restart the renderer process). See
|
||||
// AtomRendererClient::ShouldFork for how this is done.
|
||||
content::WebContents* NativeWindow::OpenURLFromTab(
|
||||
content::WebContents* source,
|
||||
const content::OpenURLParams& params) {
|
||||
if (params.disposition != CURRENT_TAB)
|
||||
return NULL;
|
||||
|
||||
content::NavigationController::LoadURLParams load_url_params(params.url);
|
||||
load_url_params.referrer = params.referrer;
|
||||
load_url_params.transition_type = params.transition;
|
||||
load_url_params.extra_headers = params.extra_headers;
|
||||
load_url_params.should_replace_current_entry =
|
||||
params.should_replace_current_entry;
|
||||
load_url_params.is_renderer_initiated = params.is_renderer_initiated;
|
||||
load_url_params.transferred_global_request_id =
|
||||
params.transferred_global_request_id;
|
||||
|
||||
source->GetController().LoadURLWithParams(load_url_params);
|
||||
return source;
|
||||
}
|
||||
|
||||
content::JavaScriptDialogManager* NativeWindow::GetJavaScriptDialogManager() {
|
||||
if (!dialog_manager_)
|
||||
dialog_manager_.reset(new AtomJavaScriptDialogManager);
|
||||
@@ -412,6 +504,22 @@ void NativeWindow::Observe(int type,
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeWindow::DevToolsSetDockSide(const std::string& dock_side,
|
||||
bool* succeed) {
|
||||
if (dock_side == "undocked") {
|
||||
*succeed = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeWindow::DevToolsShow(std::string* dock_side) {
|
||||
if (*dock_side == "undocked")
|
||||
*dock_side = "bottom";
|
||||
return false;
|
||||
}
|
||||
|
||||
void NativeWindow::OnCapturePageDone(const CapturePageCallback& callback,
|
||||
bool succeed,
|
||||
const SkBitmap& bitmap) {
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
#include "browser/native_window_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
#include "vendor/brightray/browser/default_web_contents_delegate.h"
|
||||
#include "vendor/brightray/browser/inspectable_web_contents_delegate.h"
|
||||
#include "vendor/brightray/browser/inspectable_web_contents_impl.h"
|
||||
|
||||
class CommandLine;
|
||||
struct WebPreferences;
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class ListValue;
|
||||
}
|
||||
|
||||
namespace brightray {
|
||||
class InspectableWebContents;
|
||||
}
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
class WebContents;
|
||||
@@ -45,24 +45,51 @@ class Message;
|
||||
namespace atom {
|
||||
|
||||
class AtomJavaScriptDialogManager;
|
||||
class DevToolsDelegate;
|
||||
struct DraggableRegion;
|
||||
|
||||
class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
public brightray::InspectableWebContentsDelegate,
|
||||
public content::WebContentsObserver,
|
||||
public content::NotificationObserver {
|
||||
public:
|
||||
typedef base::Callback<void(const std::vector<unsigned char>& buffer)>
|
||||
CapturePageCallback;
|
||||
|
||||
class DialogScope {
|
||||
public:
|
||||
explicit DialogScope(NativeWindow* window)
|
||||
: window_(window) {
|
||||
if (window_ != NULL)
|
||||
window_->set_has_dialog_attached(true);
|
||||
}
|
||||
|
||||
~DialogScope() {
|
||||
if (window_ != NULL)
|
||||
window_->set_has_dialog_attached(false);
|
||||
}
|
||||
|
||||
private:
|
||||
NativeWindow* window_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DialogScope);
|
||||
};
|
||||
|
||||
virtual ~NativeWindow();
|
||||
|
||||
// Create window with existing WebContents.
|
||||
// Create window with existing WebContents, the caller is responsible for
|
||||
// managing the window's live.
|
||||
static NativeWindow* Create(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options);
|
||||
|
||||
// Create window with new WebContents.
|
||||
// Create window with new WebContents, the caller is responsible for
|
||||
// managing the window's live.
|
||||
static NativeWindow* Create(base::DictionaryValue* options);
|
||||
|
||||
// Creates a devtools window to debug the WebContents, the returned window
|
||||
// will manage its own life.
|
||||
static NativeWindow* Debug(content::WebContents* web_contents);
|
||||
|
||||
// Find a window from its process id and routing id.
|
||||
static NativeWindow* FromRenderView(int process_id, int routing_id);
|
||||
|
||||
@@ -100,7 +127,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
virtual void FlashFrame(bool flash) = 0;
|
||||
virtual void SetKiosk(bool kiosk) = 0;
|
||||
virtual bool IsKiosk() = 0;
|
||||
virtual bool HasModalDialog() = 0;
|
||||
virtual bool HasModalDialog();
|
||||
virtual gfx::NativeWindow GetNativeWindow() = 0;
|
||||
|
||||
virtual bool IsClosed() const { return is_closed_; }
|
||||
@@ -129,7 +156,17 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
// Should be called by platform code when user want to close the window.
|
||||
virtual void CloseWebContents();
|
||||
|
||||
base::WeakPtr<NativeWindow> GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
content::WebContents* GetWebContents() const;
|
||||
content::WebContents* GetDevToolsWebContents() const;
|
||||
|
||||
// Called when renderer process is going to be started.
|
||||
void AppendExtraCommandLineSwitches(CommandLine* command_line,
|
||||
int child_process_id);
|
||||
void OverrideWebkitPrefs(const GURL& url, WebPreferences* prefs);
|
||||
|
||||
void AddObserver(NativeWindowObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
@@ -141,12 +178,17 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
|
||||
bool has_frame() const { return has_frame_; }
|
||||
|
||||
void set_has_dialog_attached(bool has_dialog_attached) {
|
||||
has_dialog_attached_ = has_dialog_attached;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit NativeWindow(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options);
|
||||
|
||||
brightray::InspectableWebContents* inspectable_web_contents() const {
|
||||
return inspectable_web_contents_.get();
|
||||
brightray::InspectableWebContentsImpl* inspectable_web_contents() const {
|
||||
return static_cast<brightray::InspectableWebContentsImpl*>(
|
||||
inspectable_web_contents_.get());
|
||||
}
|
||||
|
||||
void NotifyWindowClosed();
|
||||
@@ -157,6 +199,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
const std::vector<DraggableRegion>& regions) = 0;
|
||||
|
||||
// Implementations of content::WebContentsDelegate.
|
||||
virtual content::WebContents* OpenURLFromTab(
|
||||
content::WebContents* source,
|
||||
const content::OpenURLParams& params) OVERRIDE;
|
||||
virtual content::JavaScriptDialogManager*
|
||||
GetJavaScriptDialogManager() OVERRIDE;
|
||||
virtual void BeforeUnloadFired(content::WebContents* tab,
|
||||
@@ -183,11 +228,16 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) OVERRIDE;
|
||||
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
|
||||
|
||||
// Implementations of content::NotificationObserver
|
||||
// Implementations of content::NotificationObserver.
|
||||
virtual void Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) OVERRIDE;
|
||||
|
||||
// Implementations of brightray::InspectableWebContentsDelegate.
|
||||
virtual bool DevToolsSetDockSide(const std::string& dock_side,
|
||||
bool* succeed) OVERRIDE;
|
||||
virtual bool DevToolsShow(std::string* dock_side) OVERRIDE;
|
||||
|
||||
// Whether window has standard frame.
|
||||
bool has_frame_;
|
||||
|
||||
@@ -216,12 +266,21 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
// The windows has been closed.
|
||||
bool is_closed_;
|
||||
|
||||
// The security token of iframe.
|
||||
std::string node_integration_;
|
||||
|
||||
// There is a dialog that has been attached to window.
|
||||
bool has_dialog_attached_;
|
||||
|
||||
// Closure that would be called when window is unresponsive when closing,
|
||||
// it should be cancelled when we can prove that the window is responsive.
|
||||
base::CancelableClosure window_unresposive_closure_;
|
||||
|
||||
base::WeakPtrFactory<NativeWindow> weak_factory_;
|
||||
|
||||
base::WeakPtr<NativeWindow> devtools_window_;
|
||||
|
||||
scoped_ptr<DevToolsDelegate> devtools_delegate_;
|
||||
scoped_ptr<AtomJavaScriptDialogManager> dialog_manager_;
|
||||
scoped_ptr<brightray::InspectableWebContents> inspectable_web_contents_;
|
||||
|
||||
|
||||
429
browser/native_window_gtk.cc
Normal file
429
browser/native_window_gtk.cc
Normal file
@@ -0,0 +1,429 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/native_window_gtk.h"
|
||||
|
||||
#include "base/values.h"
|
||||
#include "browser/ui/gtk/gtk_window_util.h"
|
||||
#include "common/draggable_region.h"
|
||||
#include "common/options_switches.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
#include "content/public/common/renderer_preferences.h"
|
||||
#include "ui/base/x/x11_util.h"
|
||||
#include "ui/gfx/gtk_util.h"
|
||||
#include "ui/gfx/rect.h"
|
||||
#include "ui/gfx/skia_utils_gtk.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Dividing GTK's cursor blink cycle time (in milliseconds) by this value yields
|
||||
// an appropriate value for content::RendererPreferences::caret_blink_interval.
|
||||
// This matches the logic in the WebKit GTK port.
|
||||
const double kGtkCursorBlinkCycleFactor = 2000.0;
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindowGtk::NativeWindowGtk(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options)
|
||||
: NativeWindow(web_contents, options),
|
||||
window_(GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL))),
|
||||
state_(GDK_WINDOW_STATE_WITHDRAWN),
|
||||
is_always_on_top_(false) {
|
||||
gtk_container_add(GTK_CONTAINER(window_),
|
||||
GetWebContents()->GetView()->GetNativeView());
|
||||
|
||||
int width = 800, height = 600;
|
||||
options->GetInteger(switches::kWidth, &width);
|
||||
options->GetInteger(switches::kHeight, &height);
|
||||
gtk_window_set_default_size(window_, width, height);
|
||||
|
||||
if (!icon_.IsEmpty())
|
||||
gtk_window_set_icon(window_, icon_.ToGdkPixbuf());
|
||||
|
||||
// In some (older) versions of compiz, raising top-level windows when they
|
||||
// are partially off-screen causes them to get snapped back on screen, not
|
||||
// always even on the current virtual desktop. If we are running under
|
||||
// compiz, suppress such raises, as they are not necessary in compiz anyway.
|
||||
if (ui::GuessWindowManager() == ui::WM_COMPIZ)
|
||||
suppress_window_raise_ = true;
|
||||
|
||||
g_signal_connect(window_, "delete-event",
|
||||
G_CALLBACK(OnWindowDeleteEventThunk), this);
|
||||
g_signal_connect(window_, "focus-out-event",
|
||||
G_CALLBACK(OnFocusOutThunk), this);
|
||||
|
||||
if (!has_frame_) {
|
||||
gtk_window_set_decorated(window_, false);
|
||||
|
||||
g_signal_connect(window_, "motion-notify-event",
|
||||
G_CALLBACK(OnMouseMoveEventThunk), this);
|
||||
g_signal_connect(window_, "button-press-event",
|
||||
G_CALLBACK(OnButtonPressThunk), this);
|
||||
}
|
||||
|
||||
SetWebKitColorStyle();
|
||||
}
|
||||
|
||||
NativeWindowGtk::~NativeWindowGtk() {
|
||||
if (window_)
|
||||
gtk_widget_destroy(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Close() {
|
||||
CloseWebContents();
|
||||
}
|
||||
|
||||
void NativeWindowGtk::CloseImmediately() {
|
||||
gtk_widget_destroy(GTK_WIDGET(window_));
|
||||
window_ = NULL;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Move(const gfx::Rect& pos) {
|
||||
gtk_window_move(window_, pos.x(), pos.y());
|
||||
gtk_window_resize(window_, pos.width(), pos.height());
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Focus(bool focus) {
|
||||
if (!IsVisible())
|
||||
return;
|
||||
|
||||
if (focus)
|
||||
gtk_window_present(window_);
|
||||
else
|
||||
gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsFocused() {
|
||||
return gtk_window_is_active(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Show() {
|
||||
gtk_widget_show_all(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Hide() {
|
||||
gtk_widget_hide(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsVisible() {
|
||||
return gtk_widget_get_visible(GTK_WIDGET(window_));
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Maximize() {
|
||||
gtk_window_maximize(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Unmaximize() {
|
||||
gtk_window_unmaximize(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Minimize() {
|
||||
gtk_window_iconify(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Restore() {
|
||||
gtk_window_present(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetFullscreen(bool fullscreen) {
|
||||
if (fullscreen)
|
||||
gtk_window_fullscreen(window_);
|
||||
else
|
||||
gtk_window_unfullscreen(window_);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsFullscreen() {
|
||||
return state_ & GDK_WINDOW_STATE_FULLSCREEN;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetSize(const gfx::Size& size) {
|
||||
gtk_window_resize(window_, size.width(), size.height());
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowGtk::GetSize() {
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
|
||||
GdkRectangle frame_extents;
|
||||
gdk_window_get_frame_extents(gdk_window, &frame_extents);
|
||||
|
||||
return gfx::Size(frame_extents.width, frame_extents.height);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetMinimumSize(const gfx::Size& size) {
|
||||
minimum_size_ = size;
|
||||
|
||||
GdkGeometry geometry = { 0 };
|
||||
geometry.min_width = size.width();
|
||||
geometry.min_height = size.height();
|
||||
int hints = GDK_HINT_POS | GDK_HINT_MIN_SIZE;
|
||||
gtk_window_set_geometry_hints(
|
||||
window_, GTK_WIDGET(window_), &geometry, (GdkWindowHints)hints);
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowGtk::GetMinimumSize() {
|
||||
return minimum_size_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetMaximumSize(const gfx::Size& size) {
|
||||
maximum_size_ = size;
|
||||
|
||||
GdkGeometry geometry = { 0 };
|
||||
geometry.max_width = size.width();
|
||||
geometry.max_height = size.height();
|
||||
int hints = GDK_HINT_POS | GDK_HINT_MAX_SIZE;
|
||||
gtk_window_set_geometry_hints(
|
||||
window_, GTK_WIDGET(window_), &geometry, (GdkWindowHints)hints);
|
||||
}
|
||||
|
||||
gfx::Size NativeWindowGtk::GetMaximumSize() {
|
||||
return maximum_size_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetResizable(bool resizable) {
|
||||
// Should request widget size after setting unresizable, otherwise the
|
||||
// window will shrink to a very small size.
|
||||
if (!IsResizable()) {
|
||||
gint width, height;
|
||||
gtk_window_get_size(window_, &width, &height);
|
||||
gtk_widget_set_size_request(GTK_WIDGET(window_), width, height);
|
||||
}
|
||||
|
||||
gtk_window_set_resizable(window_, resizable);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsResizable() {
|
||||
return gtk_window_get_resizable(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetAlwaysOnTop(bool top) {
|
||||
is_always_on_top_ = top;
|
||||
gtk_window_set_keep_above(window_, top ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsAlwaysOnTop() {
|
||||
return is_always_on_top_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::Center() {
|
||||
gtk_window_set_position(window_, GTK_WIN_POS_CENTER);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetPosition(const gfx::Point& position) {
|
||||
gtk_window_move(window_, position.x(), position.y());
|
||||
}
|
||||
|
||||
gfx::Point NativeWindowGtk::GetPosition() {
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
|
||||
GdkRectangle frame_extents;
|
||||
gdk_window_get_frame_extents(gdk_window, &frame_extents);
|
||||
|
||||
return gfx::Point(frame_extents.x, frame_extents.y);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetTitle(const std::string& title) {
|
||||
gtk_window_set_title(window_, title.c_str());
|
||||
}
|
||||
|
||||
std::string NativeWindowGtk::GetTitle() {
|
||||
return gtk_window_get_title(window_);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::FlashFrame(bool flash) {
|
||||
gtk_window_set_urgency_hint(window_, flash);
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetKiosk(bool kiosk) {
|
||||
SetFullscreen(kiosk);
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsKiosk() {
|
||||
return IsFullscreen();
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::HasModalDialog() {
|
||||
// FIXME(zcbenz): Implement me.
|
||||
return false;
|
||||
}
|
||||
|
||||
gfx::NativeWindow NativeWindowGtk::GetNativeWindow() {
|
||||
return window_;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
// Draggable region is not supported for non-frameless window.
|
||||
if (has_frame_)
|
||||
return;
|
||||
|
||||
SkRegion draggable_region;
|
||||
|
||||
// By default, the whole window is non-draggable. We need to explicitly
|
||||
// include those draggable regions.
|
||||
for (std::vector<DraggableRegion>::const_iterator iter =
|
||||
regions.begin();
|
||||
iter != regions.end(); ++iter) {
|
||||
const DraggableRegion& region = *iter;
|
||||
draggable_region.op(
|
||||
region.bounds.x(),
|
||||
region.bounds.y(),
|
||||
region.bounds.right(),
|
||||
region.bounds.bottom(),
|
||||
region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
|
||||
}
|
||||
|
||||
draggable_region_ = draggable_region;
|
||||
}
|
||||
|
||||
void NativeWindowGtk::SetWebKitColorStyle() {
|
||||
content::RendererPreferences* prefs =
|
||||
GetWebContents()->GetMutableRendererPrefs();
|
||||
GtkStyle* frame_style = gtk_rc_get_style(GTK_WIDGET(window_));
|
||||
prefs->focus_ring_color =
|
||||
gfx::GdkColorToSkColor(frame_style->bg[GTK_STATE_SELECTED]);
|
||||
prefs->thumb_active_color = SkColorSetRGB(244, 244, 244);
|
||||
prefs->thumb_inactive_color = SkColorSetRGB(234, 234, 234);
|
||||
prefs->track_color = SkColorSetRGB(211, 211, 211);
|
||||
|
||||
GtkWidget* url_entry = gtk_entry_new();
|
||||
GtkStyle* entry_style = gtk_rc_get_style(url_entry);
|
||||
prefs->active_selection_bg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_SELECTED]);
|
||||
prefs->active_selection_fg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_SELECTED]);
|
||||
prefs->inactive_selection_bg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->base[GTK_STATE_ACTIVE]);
|
||||
prefs->inactive_selection_fg_color =
|
||||
gfx::GdkColorToSkColor(entry_style->text[GTK_STATE_ACTIVE]);
|
||||
gtk_widget_destroy(url_entry);
|
||||
|
||||
const base::TimeDelta cursor_blink_time = gfx::GetCursorBlinkCycle();
|
||||
prefs->caret_blink_interval =
|
||||
cursor_blink_time.InMilliseconds() ?
|
||||
cursor_blink_time.InMilliseconds() / kGtkCursorBlinkCycleFactor :
|
||||
0;
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::IsMaximized() const {
|
||||
return state_ & GDK_WINDOW_STATE_MAXIMIZED;
|
||||
}
|
||||
|
||||
bool NativeWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
|
||||
if (has_frame_)
|
||||
return false;
|
||||
|
||||
if (IsMaximized() || IsFullscreen())
|
||||
return false;
|
||||
|
||||
return gtk_window_util::GetWindowEdge(GetSize(), 0, x, y, edge);
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnWindowDeleteEvent(GtkWidget* widget,
|
||||
GdkEvent* event) {
|
||||
Close();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnFocusOut(GtkWidget* window, GdkEventFocus*) {
|
||||
NotifyWindowBlur();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnWindowState(GtkWidget* window,
|
||||
GdkEventWindowState* event) {
|
||||
state_ = event->new_window_state;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
|
||||
GdkEventMotion* event) {
|
||||
if (!IsResizable())
|
||||
return FALSE;
|
||||
|
||||
int win_x, win_y;
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
gdk_window_get_origin(gdk_window, &win_x, &win_y);
|
||||
gfx::Point point(static_cast<int>(event->x_root - win_x),
|
||||
static_cast<int>(event->y_root - win_y));
|
||||
|
||||
// Update the cursor if we're on the custom frame border.
|
||||
GdkWindowEdge edge;
|
||||
bool has_hit_edge = GetWindowEdge(point.x(), point.y(), &edge);
|
||||
GdkCursorType new_cursor = GDK_LAST_CURSOR;
|
||||
if (has_hit_edge)
|
||||
new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
|
||||
|
||||
GdkCursorType last_cursor = GDK_LAST_CURSOR;
|
||||
if (frame_cursor_)
|
||||
last_cursor = frame_cursor_->type;
|
||||
|
||||
if (last_cursor != new_cursor) {
|
||||
frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
|
||||
gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
|
||||
frame_cursor_);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean NativeWindowGtk::OnButtonPress(GtkWidget* widget,
|
||||
GdkEventButton* event) {
|
||||
// Make the button press coordinate relative to the browser window.
|
||||
int win_x, win_y;
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
|
||||
gdk_window_get_origin(gdk_window, &win_x, &win_y);
|
||||
|
||||
bool resizable = IsResizable();
|
||||
GdkWindowEdge edge;
|
||||
gfx::Point point(static_cast<int>(event->x_root - win_x),
|
||||
static_cast<int>(event->y_root - win_y));
|
||||
bool has_hit_edge = resizable && GetWindowEdge(point.x(), point.y(), &edge);
|
||||
bool has_hit_titlebar = !draggable_region_.isEmpty() &&
|
||||
draggable_region_.contains(event->x, event->y);
|
||||
|
||||
if (event->button == 1) {
|
||||
if (GDK_BUTTON_PRESS == event->type) {
|
||||
// Raise the window after a click on either the titlebar or the border to
|
||||
// match the behavior of most window managers, unless that behavior has
|
||||
// been suppressed.
|
||||
if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
|
||||
gdk_window_raise(GTK_WIDGET(widget)->window);
|
||||
|
||||
if (has_hit_edge) {
|
||||
gtk_window_begin_resize_drag(window_, edge, event->button,
|
||||
static_cast<gint>(event->x_root),
|
||||
static_cast<gint>(event->y_root),
|
||||
event->time);
|
||||
return TRUE;
|
||||
} else if (has_hit_titlebar) {
|
||||
return gtk_window_util::HandleTitleBarLeftMousePress(
|
||||
window_, gfx::Rect(GetPosition(), GetSize()), event);
|
||||
}
|
||||
} else if (GDK_2BUTTON_PRESS == event->type) {
|
||||
if (has_hit_titlebar && resizable) {
|
||||
// Maximize/restore on double click.
|
||||
if (IsMaximized())
|
||||
gtk_window_unmaximize(window_);
|
||||
else
|
||||
gtk_window_maximize(window_);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
} else if (event->button == 2) {
|
||||
if (has_hit_titlebar || has_hit_edge)
|
||||
gdk_window_lower(gdk_window);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// static
|
||||
NativeWindow* NativeWindow::Create(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options) {
|
||||
return new NativeWindowGtk(web_contents, options);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
109
browser/native_window_gtk.h
Normal file
109
browser/native_window_gtk.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NATIVE_WINDOW_GTK_H_
|
||||
#define ATOM_BROWSER_NATIVE_WINDOW_GTK_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "browser/native_window.h"
|
||||
#include "third_party/skia/include/core/SkRegion.h"
|
||||
#include "ui/base/gtk/gtk_signal.h"
|
||||
#include "ui/gfx/size.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindowGtk : public NativeWindow {
|
||||
public:
|
||||
explicit NativeWindowGtk(content::WebContents* web_contents,
|
||||
base::DictionaryValue* options);
|
||||
virtual ~NativeWindowGtk();
|
||||
|
||||
// NativeWindow implementation.
|
||||
virtual void Close() OVERRIDE;
|
||||
virtual void CloseImmediately() OVERRIDE;
|
||||
virtual void Move(const gfx::Rect& pos) OVERRIDE;
|
||||
virtual void Focus(bool focus) OVERRIDE;
|
||||
virtual bool IsFocused() OVERRIDE;
|
||||
virtual void Show() OVERRIDE;
|
||||
virtual void Hide() OVERRIDE;
|
||||
virtual bool IsVisible() OVERRIDE;
|
||||
virtual void Maximize() OVERRIDE;
|
||||
virtual void Unmaximize() OVERRIDE;
|
||||
virtual void Minimize() OVERRIDE;
|
||||
virtual void Restore() OVERRIDE;
|
||||
virtual void SetFullscreen(bool fullscreen) OVERRIDE;
|
||||
virtual bool IsFullscreen() OVERRIDE;
|
||||
virtual void SetSize(const gfx::Size& size) OVERRIDE;
|
||||
virtual gfx::Size GetSize() OVERRIDE;
|
||||
virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE;
|
||||
virtual gfx::Size GetMinimumSize() OVERRIDE;
|
||||
virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE;
|
||||
virtual gfx::Size GetMaximumSize() OVERRIDE;
|
||||
virtual void SetResizable(bool resizable) OVERRIDE;
|
||||
virtual bool IsResizable() OVERRIDE;
|
||||
virtual void SetAlwaysOnTop(bool top) OVERRIDE;
|
||||
virtual bool IsAlwaysOnTop() OVERRIDE;
|
||||
virtual void Center() OVERRIDE;
|
||||
virtual void SetPosition(const gfx::Point& position) OVERRIDE;
|
||||
virtual gfx::Point GetPosition() OVERRIDE;
|
||||
virtual void SetTitle(const std::string& title) OVERRIDE;
|
||||
virtual std::string GetTitle() OVERRIDE;
|
||||
virtual void FlashFrame(bool flash) OVERRIDE;
|
||||
virtual void SetKiosk(bool kiosk) OVERRIDE;
|
||||
virtual bool IsKiosk() OVERRIDE;
|
||||
virtual bool HasModalDialog() OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual void UpdateDraggableRegions(
|
||||
const std::vector<DraggableRegion>& regions) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Set WebKit's style from current theme.
|
||||
void SetWebKitColorStyle();
|
||||
|
||||
// Whether window is maximized.
|
||||
bool IsMaximized() const;
|
||||
|
||||
// If the point (|x|, |y|) is within the resize border area of the window,
|
||||
// returns true and sets |edge| to the appropriate GdkWindowEdge value.
|
||||
// Otherwise, returns false.
|
||||
bool GetWindowEdge(int x, int y, GdkWindowEdge* edge);
|
||||
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnWindowDeleteEvent,
|
||||
GdkEvent*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnFocusOut, GdkEventFocus*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnWindowState,
|
||||
GdkEventWindowState*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnMouseMoveEvent,
|
||||
GdkEventMotion*);
|
||||
CHROMEGTK_CALLBACK_1(NativeWindowGtk, gboolean, OnButtonPress,
|
||||
GdkEventButton*);
|
||||
|
||||
GtkWindow* window_;
|
||||
|
||||
GdkWindowState state_;
|
||||
bool is_always_on_top_;
|
||||
gfx::Size minimum_size_;
|
||||
gfx::Size maximum_size_;
|
||||
|
||||
// The region is treated as title bar, can be dragged to move
|
||||
// and double clicked to maximize.
|
||||
SkRegion draggable_region_;
|
||||
|
||||
// If true, don't call gdk_window_raise() when we get a click in the title
|
||||
// bar or window border. This is to work around a compiz bug.
|
||||
bool suppress_window_raise_;
|
||||
|
||||
// The current window cursor. We set it to a resize cursor when over the
|
||||
// custom frame border. We set it to NULL if we want the default cursor.
|
||||
GdkCursor* frame_cursor_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowGtk);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NATIVE_WINDOW_GTK_H_
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#import "browser/ui/atom_event_processing_window.h"
|
||||
#import "browser/ui/cocoa/event_processing_window.h"
|
||||
#include "brightray/browser/inspectable_web_contents.h"
|
||||
#include "brightray/browser/inspectable_web_contents_view.h"
|
||||
#include "common/draggable_region.h"
|
||||
@@ -76,7 +76,7 @@ static const CGFloat kAtomWindowCornerRadius = 4.0;
|
||||
|
||||
@end
|
||||
|
||||
@interface AtomNSWindow : AtomEventProcessingWindow {
|
||||
@interface AtomNSWindow : EventProcessingWindow {
|
||||
@protected
|
||||
atom::NativeWindowMac* shell_;
|
||||
}
|
||||
@@ -142,7 +142,7 @@ NativeWindowMac::NativeWindowMac(content::WebContents* web_contents,
|
||||
: NativeWindow(web_contents, options),
|
||||
is_kiosk_(false),
|
||||
attention_request_id_(0) {
|
||||
int width, height;
|
||||
int width = 800, height = 600;
|
||||
options->GetInteger(switches::kWidth, &width);
|
||||
options->GetInteger(switches::kHeight, &height);
|
||||
|
||||
@@ -440,9 +440,9 @@ void NativeWindowMac::HandleKeyboardEvent(
|
||||
event.type == content::NativeWebKeyboardEvent::Char)
|
||||
return;
|
||||
|
||||
AtomEventProcessingWindow* event_window =
|
||||
static_cast<AtomEventProcessingWindow*>(window());
|
||||
DCHECK([event_window isKindOfClass:[AtomEventProcessingWindow class]]);
|
||||
EventProcessingWindow* event_window =
|
||||
static_cast<EventProcessingWindow*>(window());
|
||||
DCHECK([event_window isKindOfClass:[EventProcessingWindow class]]);
|
||||
[event_window redispatchKeyEvent:event.os_event];
|
||||
}
|
||||
|
||||
|
||||
@@ -194,17 +194,6 @@ class NativeWindowFramelessView : public views::NonClientFrameView {
|
||||
DISALLOW_COPY_AND_ASSIGN(NativeWindowFramelessView);
|
||||
};
|
||||
|
||||
bool WindowHasModalDialog(HWND parent, HWND except, HWND after = NULL) {
|
||||
HWND hwnd = ::FindWindowEx(parent, after, NULL, NULL);
|
||||
if (hwnd != except &&
|
||||
(::GetWindowLong(hwnd, GWL_STYLE) & (WS_VISIBLE | WS_POPUP)))
|
||||
return true;
|
||||
else if (hwnd == NULL)
|
||||
return false;
|
||||
else
|
||||
return WindowHasModalDialog(parent, except, hwnd);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeWindowWin::NativeWindowWin(content::WebContents* web_contents,
|
||||
@@ -370,11 +359,6 @@ bool NativeWindowWin::IsKiosk() {
|
||||
return IsFullscreen();
|
||||
}
|
||||
|
||||
bool NativeWindowWin::HasModalDialog() {
|
||||
return WindowHasModalDialog(GetNativeWindow(),
|
||||
GetWebContents()->GetView()->GetNativeView());
|
||||
}
|
||||
|
||||
gfx::NativeWindow NativeWindowWin::GetNativeWindow() {
|
||||
return window_->GetNativeView();
|
||||
}
|
||||
@@ -417,7 +401,7 @@ void NativeWindowWin::UpdateDraggableRegions(
|
||||
void NativeWindowWin::HandleKeyboardEvent(
|
||||
content::WebContents*,
|
||||
const content::NativeWebKeyboardEvent& event) {
|
||||
if (event.type == WebKit::WebInputEvent::KeyUp) {
|
||||
if (event.type == WebKit::WebInputEvent::RawKeyDown) {
|
||||
ui::Accelerator accelerator(
|
||||
static_cast<ui::KeyboardCode>(event.windowsKeyCode),
|
||||
content::GetModifiersFromNativeWebKeyboardEvent(event));
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
#ifndef ATOM_BROWSER_NATIVE_WINDOW_WIN_H_
|
||||
#define ATOM_BROWSER_NATIVE_WINDOW_WIN_H_
|
||||
|
||||
#include "base/strings/string16.h"
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "browser/native_window.h"
|
||||
#include "ui/gfx/size.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
@@ -65,7 +64,6 @@ class NativeWindowWin : public NativeWindow,
|
||||
virtual void FlashFrame(bool flash) OVERRIDE;
|
||||
virtual void SetKiosk(bool kiosk) OVERRIDE;
|
||||
virtual bool IsKiosk() OVERRIDE;
|
||||
virtual bool HasModalDialog() OVERRIDE;
|
||||
virtual gfx::NativeWindow GetNativeWindow() OVERRIDE;
|
||||
|
||||
void OnMenuCommand(int position, HMENU menu);
|
||||
|
||||
@@ -39,13 +39,13 @@ AtomURLRequestContextGetter::AtomURLRequestContextGetter(
|
||||
const base::FilePath& base_path,
|
||||
base::MessageLoop* io_loop,
|
||||
base::MessageLoop* file_loop,
|
||||
scoped_ptr<brightray::NetworkDelegate> network_delegate,
|
||||
base::Callback<scoped_ptr<brightray::NetworkDelegate>(void)> factory,
|
||||
content::ProtocolHandlerMap* protocol_handlers)
|
||||
: base_path_(base_path),
|
||||
io_loop_(io_loop),
|
||||
file_loop_(file_loop),
|
||||
job_factory_(NULL),
|
||||
network_delegate_(network_delegate.Pass()) {
|
||||
network_delegate_factory_(factory) {
|
||||
// Must first be created on the UI thread.
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
@@ -66,8 +66,10 @@ AtomURLRequestContextGetter::~AtomURLRequestContextGetter() {
|
||||
net::URLRequestContext* AtomURLRequestContextGetter::GetURLRequestContext() {
|
||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||
|
||||
base::AutoLock auto_lock(lock_);
|
||||
if (!url_request_context_.get()) {
|
||||
url_request_context_.reset(new net::URLRequestContext());
|
||||
network_delegate_ = network_delegate_factory_.Run().Pass();
|
||||
url_request_context_->set_network_delegate(network_delegate_.get());
|
||||
storage_.reset(
|
||||
new net::URLRequestContextStorage(url_request_context_.get()));
|
||||
@@ -89,6 +91,7 @@ net::URLRequestContext* AtomURLRequestContextGetter::GetURLRequestContext() {
|
||||
net::DhcpProxyScriptFetcherFactory dhcp_factory;
|
||||
|
||||
storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
|
||||
storage_->set_transport_security_state(new net::TransportSecurityState);
|
||||
storage_->set_proxy_service(
|
||||
net::CreateProxyServiceUsingV8ProxyResolver(
|
||||
proxy_config_service_.release(),
|
||||
@@ -116,6 +119,8 @@ net::URLRequestContext* AtomURLRequestContextGetter::GetURLRequestContext() {
|
||||
net::HttpNetworkSession::Params network_session_params;
|
||||
network_session_params.cert_verifier =
|
||||
url_request_context_->cert_verifier();
|
||||
network_session_params.transport_security_state =
|
||||
url_request_context_->transport_security_state();
|
||||
network_session_params.server_bound_cert_service =
|
||||
url_request_context_->server_bound_cert_service();
|
||||
network_session_params.proxy_service =
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_CONTEXT_GETTER_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_CONTEXT_GETTER_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
|
||||
@@ -34,7 +36,7 @@ class AtomURLRequestContextGetter : public net::URLRequestContextGetter {
|
||||
const base::FilePath& base_path,
|
||||
base::MessageLoop* io_loop,
|
||||
base::MessageLoop* file_loop,
|
||||
scoped_ptr<brightray::NetworkDelegate> network_delegate,
|
||||
base::Callback<scoped_ptr<brightray::NetworkDelegate>(void)>,
|
||||
content::ProtocolHandlerMap* protocol_handlers);
|
||||
|
||||
// net::URLRequestContextGetter implementations:
|
||||
@@ -53,7 +55,12 @@ class AtomURLRequestContextGetter : public net::URLRequestContextGetter {
|
||||
base::FilePath base_path_;
|
||||
base::MessageLoop* io_loop_;
|
||||
base::MessageLoop* file_loop_;
|
||||
|
||||
AtomURLRequestJobFactory* job_factory_;
|
||||
base::Callback<scoped_ptr<brightray::NetworkDelegate>(void)>
|
||||
network_delegate_factory_;
|
||||
|
||||
base::Lock lock_;
|
||||
|
||||
scoped_ptr<net::ProxyConfigService> proxy_config_service_;
|
||||
scoped_ptr<brightray::NetworkDelegate> network_delegate_;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.8.0</string>
|
||||
<string>0.10.6</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 345 KiB |
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,8,0,0
|
||||
PRODUCTVERSION 0,8,0,0
|
||||
FILEVERSION 0,10,6,0
|
||||
PRODUCTVERSION 0,10,6,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Atom-Shell"
|
||||
VALUE "FileVersion", "0.8.0"
|
||||
VALUE "FileVersion", "0.10.6"
|
||||
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.8.0"
|
||||
VALUE "ProductVersion", "0.10.6"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
20
browser/ui/accelerator_util_gtk.cc
Normal file
20
browser/ui/accelerator_util_gtk.cc
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/ui/accelerator_util.h"
|
||||
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
#include "ui/base/accelerators/platform_accelerator_gtk.h"
|
||||
|
||||
namespace accelerator_util {
|
||||
|
||||
void SetPlatformAccelerator(ui::Accelerator* accelerator) {
|
||||
scoped_ptr<ui::PlatformAccelerator> platform_accelerator(
|
||||
new ui::PlatformAcceleratorGtk(
|
||||
GetGdkKeyCodeForAccelerator(*accelerator),
|
||||
GetGdkModifierForAccelerator(*accelerator)));
|
||||
accelerator->set_platform_accelerator(platform_accelerator.Pass());
|
||||
}
|
||||
|
||||
} // namespace accelerator_util
|
||||
@@ -3,8 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_ATOM_MENU_CONTROLLER_MAC_H_
|
||||
#define ATOM_BROWSER_UI_ATOM_MENU_CONTROLLER_MAC_H_
|
||||
#ifndef ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_
|
||||
#define ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@@ -69,4 +69,4 @@ class MenuModel;
|
||||
- (NSMenu*)menuFromModel:(ui::MenuModel*)model;
|
||||
@end
|
||||
|
||||
#endif // ATOM_BROWSER_UI_ATOM_MENU_CONTROLLER_MAC_H_
|
||||
#endif // ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_
|
||||
@@ -3,7 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "browser/ui/atom_menu_controller_mac.h"
|
||||
#import "browser/ui/cocoa/atom_menu_controller.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
@@ -2,15 +2,15 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_UI_ATOM_EVENT_PROCESSING_WINDOW_H_
|
||||
#define ATOM_BROWSER_UI_ATOM_EVENT_PROCESSING_WINDOW_H_
|
||||
#ifndef ATOM_BROWSER_UI_COCOA_EVENT_PROCESSING_WINDOW_H_
|
||||
#define ATOM_BROWSER_UI_COCOA_EVENT_PROCESSING_WINDOW_H_
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
// Override NSWindow to access unhandled keyboard events (for command
|
||||
// processing); subclassing NSWindow is the only method to do
|
||||
// this.
|
||||
@interface AtomEventProcessingWindow : NSWindow {
|
||||
@interface EventProcessingWindow : NSWindow {
|
||||
@private
|
||||
BOOL redispatchingEvent_;
|
||||
BOOL eventHandled_;
|
||||
@@ -27,4 +27,4 @@
|
||||
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent;
|
||||
@end
|
||||
|
||||
#endif // ATOM_BROWSER_UI_ATOM_EVENT_PROCESSING_WINDOW_H_
|
||||
#endif // ATOM_BROWSER_UI_COCOA_EVENT_PROCESSING_WINDOW_H_
|
||||
@@ -2,17 +2,17 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "browser/ui/atom_event_processing_window.h"
|
||||
#import "browser/ui/cocoa/event_processing_window.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#import "content/public/browser/render_widget_host_view_mac_base.h"
|
||||
|
||||
@interface AtomEventProcessingWindow ()
|
||||
@interface EventProcessingWindow ()
|
||||
// Duplicate the given key event, but changing the associated window.
|
||||
- (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event;
|
||||
@end
|
||||
|
||||
@implementation AtomEventProcessingWindow
|
||||
@implementation EventProcessingWindow
|
||||
|
||||
- (BOOL)redispatchKeyEvent:(NSEvent*)event {
|
||||
DCHECK(event);
|
||||
@@ -103,4 +103,4 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end // AtomEventProcessingWindow
|
||||
@end // EventProcessingWindow
|
||||
41
browser/ui/file_dialog_gtk.cc
Normal file
41
browser/ui/file_dialog_gtk.cc
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/ui/file_dialog.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace file_dialog {
|
||||
|
||||
bool ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
int properties,
|
||||
std::vector<base::FilePath>* paths) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowOpenDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
int properties,
|
||||
const OpenDialogCallback& callback) {
|
||||
callback.Run(false, std::vector<base::FilePath>());
|
||||
}
|
||||
|
||||
bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
base::FilePath* path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowSaveDialog(atom::NativeWindow* parent_window,
|
||||
const std::string& title,
|
||||
const base::FilePath& default_path,
|
||||
const SaveDialogCallback& callback) {
|
||||
callback.Run(false, base::FilePath());
|
||||
}
|
||||
|
||||
} // namespace file_dialog
|
||||
@@ -163,6 +163,7 @@ class FileDialog {
|
||||
}
|
||||
|
||||
bool Show(atom::NativeWindow* parent_window) {
|
||||
atom::NativeWindow::DialogScope dialog_scope(parent_window);
|
||||
HWND window = parent_window ? parent_window->GetNativeWindow() : NULL;
|
||||
return dialog_->DoModal(window) == IDOK;
|
||||
}
|
||||
|
||||
150
browser/ui/gtk/gtk_custom_menu.cc
Normal file
150
browser/ui/gtk/gtk_custom_menu.cc
Normal file
@@ -0,0 +1,150 @@
|
||||
// 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 "browser/ui/gtk/gtk_custom_menu.h"
|
||||
|
||||
#include "browser/ui/gtk/gtk_custom_menu_item.h"
|
||||
|
||||
G_DEFINE_TYPE(GtkCustomMenu, gtk_custom_menu, GTK_TYPE_MENU)
|
||||
|
||||
// Stolen directly from gtkmenushell.c. I'd love to call the library version
|
||||
// instead, but it's static and isn't exported. :(
|
||||
static gint gtk_menu_shell_is_item(GtkMenuShell* menu_shell,
|
||||
GtkWidget* child) {
|
||||
GtkWidget *parent;
|
||||
|
||||
g_return_val_if_fail(GTK_IS_MENU_SHELL(menu_shell), FALSE);
|
||||
g_return_val_if_fail(child != NULL, FALSE);
|
||||
|
||||
parent = gtk_widget_get_parent(child);
|
||||
while (GTK_IS_MENU_SHELL(parent)) {
|
||||
if (parent == reinterpret_cast<GtkWidget*>(menu_shell))
|
||||
return TRUE;
|
||||
parent = GTK_MENU_SHELL(parent)->parent_menu_shell;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Stolen directly from gtkmenushell.c. I'd love to call the library version
|
||||
// instead, but it's static and isn't exported. :(
|
||||
static GtkWidget* gtk_menu_shell_get_item(GtkMenuShell* menu_shell,
|
||||
GdkEvent* event) {
|
||||
GtkWidget* menu_item = gtk_get_event_widget(event);
|
||||
|
||||
while (menu_item && !GTK_IS_MENU_ITEM(menu_item))
|
||||
menu_item = gtk_widget_get_parent(menu_item);
|
||||
|
||||
if (menu_item && gtk_menu_shell_is_item(menu_shell, menu_item))
|
||||
return menu_item;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// When processing a button event, abort processing if the cursor isn't in a
|
||||
// clickable region.
|
||||
static gboolean gtk_custom_menu_button_press(GtkWidget* widget,
|
||||
GdkEventButton* event) {
|
||||
GtkWidget* menu_item = gtk_menu_shell_get_item(
|
||||
GTK_MENU_SHELL(widget), reinterpret_cast<GdkEvent*>(event));
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
if (!gtk_custom_menu_item_is_in_clickable_region(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item))) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
|
||||
button_press_event(widget, event);
|
||||
}
|
||||
|
||||
// When processing a button event, abort processing if the cursor isn't in a
|
||||
// clickable region. If it's in a button that doesn't dismiss the menu, fire
|
||||
// that event and abort having the normal GtkMenu code run.
|
||||
static gboolean gtk_custom_menu_button_release(GtkWidget* widget,
|
||||
GdkEventButton* event) {
|
||||
GtkWidget* menu_item = gtk_menu_shell_get_item(
|
||||
GTK_MENU_SHELL(widget), reinterpret_cast<GdkEvent*>(event));
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
if (!gtk_custom_menu_item_is_in_clickable_region(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item))) {
|
||||
// Stop processing this event. This isn't a clickable region.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (gtk_custom_menu_item_try_no_dismiss_command(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item))) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
|
||||
button_release_event(widget, event);
|
||||
}
|
||||
|
||||
// Manually forward button press events to the menu item (and then do what we'd
|
||||
// do normally).
|
||||
static gboolean gtk_custom_menu_motion_notify(GtkWidget* widget,
|
||||
GdkEventMotion* event) {
|
||||
GtkWidget* menu_item = gtk_menu_shell_get_item(
|
||||
GTK_MENU_SHELL(widget), (GdkEvent*)event);
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
gtk_custom_menu_item_receive_motion_event(GTK_CUSTOM_MENU_ITEM(menu_item),
|
||||
event->x, event->y);
|
||||
}
|
||||
|
||||
return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
|
||||
motion_notify_event(widget, event);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_move_current(GtkMenuShell* menu_shell,
|
||||
GtkMenuDirectionType direction) {
|
||||
// If the currently selected item is custom, we give it first chance to catch
|
||||
// up/down events.
|
||||
|
||||
// TODO(erg): We are breaking a GSEAL by directly accessing this. We'll need
|
||||
// to fix this by the time gtk3 comes out.
|
||||
GtkWidget* menu_item = GTK_MENU_SHELL(menu_shell)->active_menu_item;
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
switch (direction) {
|
||||
case GTK_MENU_DIR_PREV:
|
||||
case GTK_MENU_DIR_NEXT:
|
||||
if (gtk_custom_menu_item_handle_move(GTK_CUSTOM_MENU_ITEM(menu_item),
|
||||
direction))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GTK_MENU_SHELL_CLASS(gtk_custom_menu_parent_class)->
|
||||
move_current(menu_shell, direction);
|
||||
|
||||
// In the case of hitting PREV and transitioning to a custom menu, we want to
|
||||
// make sure we're selecting the final item in the list, not the first one.
|
||||
menu_item = GTK_MENU_SHELL(menu_shell)->active_menu_item;
|
||||
if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
|
||||
gtk_custom_menu_item_select_item_by_direction(
|
||||
GTK_CUSTOM_MENU_ITEM(menu_item), direction);
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_init(GtkCustomMenu* menu) {
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_class_init(GtkCustomMenuClass* klass) {
|
||||
GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
|
||||
GtkMenuShellClass* menu_shell_class = GTK_MENU_SHELL_CLASS(klass);
|
||||
|
||||
widget_class->button_press_event = gtk_custom_menu_button_press;
|
||||
widget_class->button_release_event = gtk_custom_menu_button_release;
|
||||
widget_class->motion_notify_event = gtk_custom_menu_motion_notify;
|
||||
|
||||
menu_shell_class->move_current = gtk_custom_menu_move_current;
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_new() {
|
||||
return GTK_WIDGET(g_object_new(GTK_TYPE_CUSTOM_MENU, NULL));
|
||||
}
|
||||
51
browser/ui/gtk/gtk_custom_menu.h
Normal file
51
browser/ui/gtk/gtk_custom_menu.h
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2011 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_UI_GTK_GTK_CUSTOM_MENU_H_
|
||||
#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_H_
|
||||
|
||||
// GtkCustomMenu is a GtkMenu subclass that can contain, and collaborates with,
|
||||
// GtkCustomMenuItem instances. GtkCustomMenuItem is a GtkMenuItem that can
|
||||
// have buttons and other normal widgets embeded in it. GtkCustomMenu exists
|
||||
// only to override most of the button/motion/move callback functions so
|
||||
// that the normal GtkMenu implementation doesn't handle events related to
|
||||
// GtkCustomMenuItem items.
|
||||
//
|
||||
// For a more through overview of this system, see the comments in
|
||||
// gtk_custom_menu_item.h.
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CUSTOM_MENU \
|
||||
(gtk_custom_menu_get_type())
|
||||
#define GTK_CUSTOM_MENU(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU, GtkCustomMenu))
|
||||
#define GTK_CUSTOM_MENU_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU, GtkCustomMenuClass))
|
||||
#define GTK_IS_CUSTOM_MENU(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU))
|
||||
#define GTK_IS_CUSTOM_MENU_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU))
|
||||
#define GTK_CUSTOM_MENU_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU, GtkCustomMenuClass))
|
||||
|
||||
typedef struct _GtkCustomMenu GtkCustomMenu;
|
||||
typedef struct _GtkCustomMenuClass GtkCustomMenuClass;
|
||||
|
||||
struct _GtkCustomMenu {
|
||||
GtkMenu menu;
|
||||
};
|
||||
|
||||
struct _GtkCustomMenuClass {
|
||||
GtkMenuClass parent_class;
|
||||
};
|
||||
|
||||
GType gtk_custom_menu_get_type(void) G_GNUC_CONST;
|
||||
GtkWidget* gtk_custom_menu_new();
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_H_
|
||||
493
browser/ui/gtk/gtk_custom_menu_item.cc
Normal file
493
browser/ui/gtk/gtk_custom_menu_item.cc
Normal file
@@ -0,0 +1,493 @@
|
||||
// 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 "browser/ui/gtk/gtk_custom_menu_item.h"
|
||||
|
||||
#include "base/i18n/rtl.h"
|
||||
#include "browser/ui/gtk/gtk_custom_menu.h"
|
||||
#include "ui/gfx/gtk_compat.h"
|
||||
|
||||
// This method was autogenerated by the program glib-genmarshall, which
|
||||
// generated it from the line "BOOL:INT". Two different attempts at getting gyp
|
||||
// to autogenerate this didn't work. If we need more non-standard marshallers,
|
||||
// this should be deleted, and an actual build step should be added.
|
||||
void chrome_marshall_BOOLEAN__INT(GClosure* closure,
|
||||
GValue* return_value G_GNUC_UNUSED,
|
||||
guint n_param_values,
|
||||
const GValue* param_values,
|
||||
gpointer invocation_hint G_GNUC_UNUSED,
|
||||
gpointer marshal_data) {
|
||||
typedef gboolean(*GMarshalFunc_BOOLEAN__INT)(gpointer data1,
|
||||
gint arg_1,
|
||||
gpointer data2);
|
||||
register GMarshalFunc_BOOLEAN__INT callback;
|
||||
register GCClosure *cc = (GCClosure*)closure;
|
||||
register gpointer data1, data2;
|
||||
gboolean v_return;
|
||||
|
||||
g_return_if_fail(return_value != NULL);
|
||||
g_return_if_fail(n_param_values == 2);
|
||||
|
||||
if (G_CCLOSURE_SWAP_DATA(closure)) {
|
||||
data1 = closure->data;
|
||||
// Note: This line (and the line setting data1 in the other if branch)
|
||||
// were macros in the original autogenerated output. This is with the
|
||||
// macro resolved for release mode. In debug mode, it uses an accessor
|
||||
// that asserts saying that the object pointed to by param_values doesn't
|
||||
// hold a pointer. This appears to be the cause of http://crbug.com/58945.
|
||||
//
|
||||
// This is more than a little odd because the gtype on this first param
|
||||
// isn't set correctly by the time we get here, while I watched it
|
||||
// explicitly set upstack. I verified that v_pointer is still set
|
||||
// correctly. I'm not sure what's going on. :(
|
||||
data2 = (param_values + 0)->data[0].v_pointer;
|
||||
} else {
|
||||
data1 = (param_values + 0)->data[0].v_pointer;
|
||||
data2 = closure->data;
|
||||
}
|
||||
callback = (GMarshalFunc_BOOLEAN__INT)(marshal_data ? marshal_data :
|
||||
cc->callback);
|
||||
|
||||
v_return = callback(data1,
|
||||
g_value_get_int(param_values + 1),
|
||||
data2);
|
||||
|
||||
g_value_set_boolean(return_value, v_return);
|
||||
}
|
||||
|
||||
enum {
|
||||
BUTTON_PUSHED,
|
||||
TRY_BUTTON_PUSHED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint custom_menu_item_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE(GtkCustomMenuItem, gtk_custom_menu_item, GTK_TYPE_MENU_ITEM)
|
||||
|
||||
static void set_selected(GtkCustomMenuItem* item, GtkWidget* selected) {
|
||||
if (selected != item->currently_selected_button) {
|
||||
if (item->currently_selected_button) {
|
||||
gtk_widget_set_state(item->currently_selected_button, GTK_STATE_NORMAL);
|
||||
gtk_widget_set_state(
|
||||
gtk_bin_get_child(GTK_BIN(item->currently_selected_button)),
|
||||
GTK_STATE_NORMAL);
|
||||
}
|
||||
|
||||
item->currently_selected_button = selected;
|
||||
if (item->currently_selected_button) {
|
||||
gtk_widget_set_state(item->currently_selected_button, GTK_STATE_SELECTED);
|
||||
gtk_widget_set_state(
|
||||
gtk_bin_get_child(GTK_BIN(item->currently_selected_button)),
|
||||
GTK_STATE_PRELIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When GtkButtons set the label text, they rebuild the widget hierarchy each
|
||||
// and every time. Therefore, we can't just fish out the label from the button
|
||||
// and set some properties; we have to create this callback function that
|
||||
// listens on the button's "notify" signal, which is emitted right after the
|
||||
// label has been (re)created. (Label values can change dynamically.)
|
||||
static void on_button_label_set(GObject* object) {
|
||||
GtkButton* button = GTK_BUTTON(object);
|
||||
GtkWidget* child = gtk_bin_get_child(GTK_BIN(button));
|
||||
gtk_widget_set_sensitive(child, FALSE);
|
||||
gtk_misc_set_padding(GTK_MISC(child), 2, 0);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_finalize(GObject *object);
|
||||
static gint gtk_custom_menu_item_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event);
|
||||
static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event,
|
||||
GtkCustomMenuItem* menu_item);
|
||||
static void gtk_custom_menu_item_select(GtkItem *item);
|
||||
static void gtk_custom_menu_item_deselect(GtkItem *item);
|
||||
static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item);
|
||||
|
||||
static void gtk_custom_menu_item_init(GtkCustomMenuItem* item) {
|
||||
item->all_widgets = NULL;
|
||||
item->button_widgets = NULL;
|
||||
item->currently_selected_button = NULL;
|
||||
item->previously_selected_button = NULL;
|
||||
|
||||
GtkWidget* menu_hbox = gtk_hbox_new(FALSE, 0);
|
||||
gtk_container_add(GTK_CONTAINER(item), menu_hbox);
|
||||
|
||||
item->label = gtk_label_new(NULL);
|
||||
gtk_misc_set_alignment(GTK_MISC(item->label), 0.0, 0.5);
|
||||
gtk_box_pack_start(GTK_BOX(menu_hbox), item->label, TRUE, TRUE, 0);
|
||||
|
||||
item->hbox = gtk_hbox_new(FALSE, 0);
|
||||
gtk_box_pack_end(GTK_BOX(menu_hbox), item->hbox, FALSE, FALSE, 0);
|
||||
|
||||
g_signal_connect(item->hbox, "expose-event",
|
||||
G_CALLBACK(gtk_custom_menu_item_hbox_expose),
|
||||
item);
|
||||
|
||||
gtk_widget_show_all(menu_hbox);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_class_init(GtkCustomMenuItemClass* klass) {
|
||||
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
|
||||
GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
|
||||
GtkItemClass* item_class = GTK_ITEM_CLASS(klass);
|
||||
GtkMenuItemClass* menu_item_class = GTK_MENU_ITEM_CLASS(klass);
|
||||
|
||||
gobject_class->finalize = gtk_custom_menu_item_finalize;
|
||||
|
||||
widget_class->expose_event = gtk_custom_menu_item_expose;
|
||||
|
||||
item_class->select = gtk_custom_menu_item_select;
|
||||
item_class->deselect = gtk_custom_menu_item_deselect;
|
||||
|
||||
menu_item_class->activate = gtk_custom_menu_item_activate;
|
||||
|
||||
custom_menu_item_signals[BUTTON_PUSHED] =
|
||||
g_signal_new("button-pushed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_FIRST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__INT,
|
||||
G_TYPE_NONE, 1, G_TYPE_INT);
|
||||
custom_menu_item_signals[TRY_BUTTON_PUSHED] =
|
||||
g_signal_new("try-button-pushed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
chrome_marshall_BOOLEAN__INT,
|
||||
G_TYPE_BOOLEAN, 1, G_TYPE_INT);
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_finalize(GObject *object) {
|
||||
GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(object);
|
||||
g_list_free(item->all_widgets);
|
||||
g_list_free(item->button_widgets);
|
||||
|
||||
G_OBJECT_CLASS(gtk_custom_menu_item_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static gint gtk_custom_menu_item_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event) {
|
||||
if (gtk_widget_get_visible(widget) &&
|
||||
gtk_widget_get_mapped(widget) &&
|
||||
gtk_bin_get_child(GTK_BIN(widget))) {
|
||||
// We skip the drawing in the GtkMenuItem class it draws the highlighted
|
||||
// background and we don't want that.
|
||||
gtk_container_propagate_expose(GTK_CONTAINER(widget),
|
||||
gtk_bin_get_child(GTK_BIN(widget)),
|
||||
event);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_expose_button(GtkWidget* hbox,
|
||||
GdkEventExpose* event,
|
||||
GList* button_item) {
|
||||
// We search backwards to find the leftmost and rightmost buttons. The
|
||||
// current button may be that button.
|
||||
GtkWidget* current_button = GTK_WIDGET(button_item->data);
|
||||
GtkWidget* first_button = current_button;
|
||||
for (GList* i = button_item; i && GTK_IS_BUTTON(i->data);
|
||||
i = g_list_previous(i)) {
|
||||
first_button = GTK_WIDGET(i->data);
|
||||
}
|
||||
|
||||
GtkWidget* last_button = current_button;
|
||||
for (GList* i = button_item; i && GTK_IS_BUTTON(i->data);
|
||||
i = g_list_next(i)) {
|
||||
last_button = GTK_WIDGET(i->data);
|
||||
}
|
||||
|
||||
if (base::i18n::IsRTL())
|
||||
std::swap(first_button, last_button);
|
||||
|
||||
GtkAllocation first_allocation;
|
||||
gtk_widget_get_allocation(first_button, &first_allocation);
|
||||
GtkAllocation current_allocation;
|
||||
gtk_widget_get_allocation(current_button, ¤t_allocation);
|
||||
GtkAllocation last_allocation;
|
||||
gtk_widget_get_allocation(last_button, &last_allocation);
|
||||
|
||||
int x = first_allocation.x;
|
||||
int y = first_allocation.y;
|
||||
int width = last_allocation.width + last_allocation.x - first_allocation.x;
|
||||
int height = last_allocation.height;
|
||||
|
||||
gtk_paint_box(gtk_widget_get_style(hbox),
|
||||
gtk_widget_get_window(hbox),
|
||||
gtk_widget_get_state(current_button),
|
||||
GTK_SHADOW_OUT,
|
||||
¤t_allocation, hbox, "button",
|
||||
x, y, width, height);
|
||||
|
||||
// Propagate to the button's children.
|
||||
gtk_container_propagate_expose(
|
||||
GTK_CONTAINER(current_button),
|
||||
gtk_bin_get_child(GTK_BIN(current_button)),
|
||||
event);
|
||||
}
|
||||
|
||||
static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget,
|
||||
GdkEventExpose* event,
|
||||
GtkCustomMenuItem* menu_item) {
|
||||
// First render all the buttons that aren't the currently selected item.
|
||||
for (GList* current_item = menu_item->all_widgets;
|
||||
current_item != NULL; current_item = g_list_next(current_item)) {
|
||||
if (GTK_IS_BUTTON(current_item->data)) {
|
||||
if (GTK_WIDGET(current_item->data) !=
|
||||
menu_item->currently_selected_button) {
|
||||
gtk_custom_menu_item_expose_button(widget, event, current_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As a separate pass, draw the buton separators above. We need to draw the
|
||||
// separators in a separate pass because we are drawing on top of the
|
||||
// buttons. Otherwise, the vlines are overwritten by the next button.
|
||||
for (GList* current_item = menu_item->all_widgets;
|
||||
current_item != NULL; current_item = g_list_next(current_item)) {
|
||||
if (GTK_IS_BUTTON(current_item->data)) {
|
||||
// Check to see if this is the last button in a run.
|
||||
GList* next_item = g_list_next(current_item);
|
||||
if (next_item && GTK_IS_BUTTON(next_item->data)) {
|
||||
GtkWidget* current_button = GTK_WIDGET(current_item->data);
|
||||
GtkAllocation button_allocation;
|
||||
gtk_widget_get_allocation(current_button, &button_allocation);
|
||||
GtkAllocation child_alloc;
|
||||
gtk_widget_get_allocation(gtk_bin_get_child(GTK_BIN(current_button)),
|
||||
&child_alloc);
|
||||
GtkStyle* style = gtk_widget_get_style(widget);
|
||||
int half_offset = style->xthickness / 2;
|
||||
gtk_paint_vline(style,
|
||||
gtk_widget_get_window(widget),
|
||||
gtk_widget_get_state(current_button),
|
||||
&event->area, widget, "button",
|
||||
child_alloc.y,
|
||||
child_alloc.y + child_alloc.height,
|
||||
button_allocation.x +
|
||||
button_allocation.width - half_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, draw the selected item on top of the separators so there are no
|
||||
// artifacts inside the button area.
|
||||
GList* selected = g_list_find(menu_item->all_widgets,
|
||||
menu_item->currently_selected_button);
|
||||
if (selected) {
|
||||
gtk_custom_menu_item_expose_button(widget, event, selected);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_select(GtkItem* item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
|
||||
|
||||
// When we are selected, the only thing we do is clear information from
|
||||
// previous selections. Actual selection of a button is done either in the
|
||||
// "mouse-motion-event" or is manually set from GtkCustomMenu's overridden
|
||||
// "move-current" handler.
|
||||
custom_item->previously_selected_button = NULL;
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(item));
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_deselect(GtkItem* item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
|
||||
|
||||
// When we are deselected, we store the item that was currently selected so
|
||||
// that it can be acted on. Menu items are first deselected before they are
|
||||
// activated.
|
||||
custom_item->previously_selected_button =
|
||||
custom_item->currently_selected_button;
|
||||
if (custom_item->currently_selected_button)
|
||||
set_selected(custom_item, NULL);
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(item));
|
||||
}
|
||||
|
||||
static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
|
||||
|
||||
// We look at |previously_selected_button| because by the time we've been
|
||||
// activated, we've already gone through our deselect handler.
|
||||
if (custom_item->previously_selected_button) {
|
||||
gpointer id_ptr = g_object_get_data(
|
||||
G_OBJECT(custom_item->previously_selected_button), "command-id");
|
||||
if (id_ptr != NULL) {
|
||||
int command_id = GPOINTER_TO_INT(id_ptr);
|
||||
g_signal_emit(custom_item, custom_menu_item_signals[BUTTON_PUSHED], 0,
|
||||
command_id);
|
||||
set_selected(custom_item, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_item_new(const char* title) {
|
||||
GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(
|
||||
g_object_new(GTK_TYPE_CUSTOM_MENU_ITEM, NULL));
|
||||
gtk_label_set_text(GTK_LABEL(item->label), title);
|
||||
return GTK_WIDGET(item);
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
|
||||
int command_id) {
|
||||
GtkWidget* button = gtk_button_new();
|
||||
g_object_set_data(G_OBJECT(button), "command-id",
|
||||
GINT_TO_POINTER(command_id));
|
||||
gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
|
||||
gtk_widget_show(button);
|
||||
|
||||
menu_item->all_widgets = g_list_append(menu_item->all_widgets, button);
|
||||
menu_item->button_widgets = g_list_append(menu_item->button_widgets, button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
|
||||
int command_id) {
|
||||
GtkWidget* button = gtk_button_new_with_label("");
|
||||
g_object_set_data(G_OBJECT(button), "command-id",
|
||||
GINT_TO_POINTER(command_id));
|
||||
gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
|
||||
g_signal_connect(button, "notify::label",
|
||||
G_CALLBACK(on_button_label_set), NULL);
|
||||
gtk_widget_show(button);
|
||||
|
||||
menu_item->all_widgets = g_list_append(menu_item->all_widgets, button);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item) {
|
||||
GtkWidget* fixed = gtk_fixed_new();
|
||||
gtk_widget_set_size_request(fixed, 5, -1);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(menu_item->hbox), fixed, FALSE, FALSE, 0);
|
||||
gtk_widget_show(fixed);
|
||||
|
||||
menu_item->all_widgets = g_list_append(menu_item->all_widgets, fixed);
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
|
||||
gdouble x, gdouble y) {
|
||||
GtkWidget* new_selected_widget = NULL;
|
||||
GList* current = menu_item->button_widgets;
|
||||
for (; current != NULL; current = current->next) {
|
||||
GtkWidget* current_widget = GTK_WIDGET(current->data);
|
||||
GtkAllocation alloc;
|
||||
gtk_widget_get_allocation(current_widget, &alloc);
|
||||
int offset_x, offset_y;
|
||||
gtk_widget_translate_coordinates(current_widget, GTK_WIDGET(menu_item),
|
||||
0, 0, &offset_x, &offset_y);
|
||||
if (x >= offset_x && x < (offset_x + alloc.width) &&
|
||||
y >= offset_y && y < (offset_y + alloc.height)) {
|
||||
new_selected_widget = current_widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
set_selected(menu_item, new_selected_widget);
|
||||
}
|
||||
|
||||
gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
|
||||
GtkMenuDirectionType direction) {
|
||||
GtkWidget* current = menu_item->currently_selected_button;
|
||||
if (menu_item->button_widgets && current) {
|
||||
switch (direction) {
|
||||
case GTK_MENU_DIR_PREV: {
|
||||
if (g_list_first(menu_item->button_widgets)->data == current)
|
||||
return FALSE;
|
||||
|
||||
set_selected(menu_item, GTK_WIDGET(g_list_previous(g_list_find(
|
||||
menu_item->button_widgets, current))->data));
|
||||
break;
|
||||
}
|
||||
case GTK_MENU_DIR_NEXT: {
|
||||
if (g_list_last(menu_item->button_widgets)->data == current)
|
||||
return FALSE;
|
||||
|
||||
set_selected(menu_item, GTK_WIDGET(g_list_next(g_list_find(
|
||||
menu_item->button_widgets, current))->data));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_select_item_by_direction(
|
||||
GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction) {
|
||||
menu_item->previously_selected_button = NULL;
|
||||
|
||||
// If we're just told to be selected by the menu system, select the first
|
||||
// item.
|
||||
if (menu_item->button_widgets) {
|
||||
switch (direction) {
|
||||
case GTK_MENU_DIR_PREV: {
|
||||
GtkWidget* last_button =
|
||||
GTK_WIDGET(g_list_last(menu_item->button_widgets)->data);
|
||||
if (last_button)
|
||||
set_selected(menu_item, last_button);
|
||||
break;
|
||||
}
|
||||
case GTK_MENU_DIR_NEXT: {
|
||||
GtkWidget* first_button =
|
||||
GTK_WIDGET(g_list_first(menu_item->button_widgets)->data);
|
||||
if (first_button)
|
||||
set_selected(menu_item, first_button);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw(GTK_WIDGET(menu_item));
|
||||
}
|
||||
|
||||
gboolean gtk_custom_menu_item_is_in_clickable_region(
|
||||
GtkCustomMenuItem* menu_item) {
|
||||
return menu_item->currently_selected_button != NULL;
|
||||
}
|
||||
|
||||
gboolean gtk_custom_menu_item_try_no_dismiss_command(
|
||||
GtkCustomMenuItem* menu_item) {
|
||||
GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
|
||||
gboolean activated = TRUE;
|
||||
|
||||
// We work with |currently_selected_button| instead of
|
||||
// |previously_selected_button| since we haven't been "deselect"ed yet.
|
||||
gpointer id_ptr = g_object_get_data(
|
||||
G_OBJECT(custom_item->currently_selected_button), "command-id");
|
||||
if (id_ptr != NULL) {
|
||||
int command_id = GPOINTER_TO_INT(id_ptr);
|
||||
g_signal_emit(custom_item, custom_menu_item_signals[TRY_BUTTON_PUSHED], 0,
|
||||
command_id, &activated);
|
||||
}
|
||||
|
||||
return activated;
|
||||
}
|
||||
|
||||
void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data) {
|
||||
// Even though we're filtering |all_widgets| on GTK_IS_BUTTON(), this isn't
|
||||
// equivalent to |button_widgets| because we also want the button-labels.
|
||||
for (GList* i = menu_item->all_widgets; i && GTK_IS_BUTTON(i->data);
|
||||
i = g_list_next(i)) {
|
||||
if (GTK_IS_BUTTON(i->data)) {
|
||||
callback(GTK_WIDGET(i->data), callback_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
139
browser/ui/gtk/gtk_custom_menu_item.h
Normal file
139
browser/ui/gtk/gtk_custom_menu_item.h
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) 2011 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_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
|
||||
#define CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
|
||||
|
||||
// GtkCustomMenuItem is a GtkMenuItem subclass that has buttons in it and acts
|
||||
// to support this. GtkCustomMenuItems only render properly when put in a
|
||||
// GtkCustomMenu; there's a lot of collaboration between these two classes
|
||||
// necessary to work around how gtk normally does menus.
|
||||
//
|
||||
// We can't rely on the normal event infrastructure. While a menu is up, the
|
||||
// GtkMenu has a grab on all events. Instead of trying to pump events through
|
||||
// the normal channels, we have the GtkCustomMenu selectively forward mouse
|
||||
// motion through a back channel. The GtkCustomMenu only listens for button
|
||||
// press information so it can block the effects of the click if the cursor
|
||||
// isn't in a button in the menu item.
|
||||
//
|
||||
// A GtkCustomMenuItem doesn't try to take these signals and forward them to
|
||||
// the buttons it owns. The GtkCustomMenu class keeps track of which button is
|
||||
// selected (due to key events and mouse movement) and otherwise acts like a
|
||||
// normal GtkItem. The buttons are only for sizing and rendering; they don't
|
||||
// respond to events. Instead, when the GtkCustomMenuItem is activated by the
|
||||
// GtkMenu, it uses which button was selected as a signal of what to do.
|
||||
//
|
||||
// Users should connect to the "button-pushed" signal to be notified when a
|
||||
// button was pushed. We don't go through the normal "activate" signal because
|
||||
// we need to communicate additional information, namely which button was
|
||||
// activated.
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CUSTOM_MENU_ITEM \
|
||||
(gtk_custom_menu_item_get_type())
|
||||
#define GTK_CUSTOM_MENU_ITEM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
|
||||
GtkCustomMenuItem))
|
||||
#define GTK_CUSTOM_MENU_ITEM_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU_ITEM, \
|
||||
GtkCustomMenuItemClass))
|
||||
#define GTK_IS_CUSTOM_MENU_ITEM(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU_ITEM))
|
||||
#define GTK_IS_CUSTOM_MENU_ITEM_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU_ITEM))
|
||||
#define GTK_CUSTOM_MENU_ITEM_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
|
||||
GtkCustomMenuItemClass))
|
||||
|
||||
typedef struct _GtkCustomMenuItem GtkCustomMenuItem;
|
||||
typedef struct _GtkCustomMenuItemClass GtkCustomMenuItemClass;
|
||||
|
||||
struct _GtkCustomMenuItem {
|
||||
GtkMenuItem menu_item;
|
||||
|
||||
// Container for button widgets.
|
||||
GtkWidget* hbox;
|
||||
|
||||
// Label on left side of menu item.
|
||||
GtkWidget* label;
|
||||
|
||||
// List of all widgets we added. Used to find the leftmost and rightmost
|
||||
// continuous buttons.
|
||||
GList* all_widgets;
|
||||
|
||||
// Possible button widgets. Used for keyboard navigation.
|
||||
GList* button_widgets;
|
||||
|
||||
// The widget that currently has highlight.
|
||||
GtkWidget* currently_selected_button;
|
||||
|
||||
// The widget that was selected *before* |currently_selected_button|. Why do
|
||||
// we hang on to this? Because the menu system sends us a deselect signal
|
||||
// right before activating us. We need to listen to deselect since that's
|
||||
// what we receive when the mouse cursor leaves us entirely.
|
||||
GtkWidget* previously_selected_button;
|
||||
};
|
||||
|
||||
struct _GtkCustomMenuItemClass {
|
||||
GtkMenuItemClass parent_class;
|
||||
};
|
||||
|
||||
GType gtk_custom_menu_item_get_type(void) G_GNUC_CONST;
|
||||
GtkWidget* gtk_custom_menu_item_new(const char* title);
|
||||
|
||||
// Adds a button to our list of items in the |hbox|.
|
||||
GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
|
||||
int command_id);
|
||||
|
||||
// Adds a button to our list of items in the |hbox|, but that isn't part of
|
||||
// |button_widgets| to prevent it from being activatable.
|
||||
GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item,
|
||||
int command_id);
|
||||
|
||||
// Adds a vertical space in the |hbox|.
|
||||
void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item);
|
||||
|
||||
// Receives a motion event from the GtkCustomMenu that contains us. We can't
|
||||
// just subscribe to motion-event or the individual widget enter/leave events
|
||||
// because the top level GtkMenu has an event grab.
|
||||
void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
|
||||
gdouble x, gdouble y);
|
||||
|
||||
// Notification that the menu got a cursor key event. Used to move up/down
|
||||
// within the menu buttons. Returns TRUE to stop the default signal handler
|
||||
// from running.
|
||||
gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
|
||||
GtkMenuDirectionType direction);
|
||||
|
||||
// Because we only get a generic "selected" signal when we've changed, we need
|
||||
// to have a way for the GtkCustomMenu to tell us that we were just
|
||||
// selected.
|
||||
void gtk_custom_menu_item_select_item_by_direction(
|
||||
GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction);
|
||||
|
||||
// Whether we are currently hovering over a clickable region on the menu
|
||||
// item. Used by GtkCustomMenu to determine whether it should discard click
|
||||
// events.
|
||||
gboolean gtk_custom_menu_item_is_in_clickable_region(
|
||||
GtkCustomMenuItem* menu_item);
|
||||
|
||||
// If the button is released while the |currently_selected_button| isn't
|
||||
// supposed to dismiss the menu, this signals to our listeners that we want to
|
||||
// run this command if it doesn't dismiss the menu. Returns TRUE if we acted
|
||||
// on this button click (and should prevent the normal GtkMenu machinery from
|
||||
// firing an "activate" signal).
|
||||
gboolean gtk_custom_menu_item_try_no_dismiss_command(
|
||||
GtkCustomMenuItem* menu_item);
|
||||
|
||||
// Calls |callback| with every button and button-label in the container.
|
||||
void gtk_custom_menu_item_foreach_button(GtkCustomMenuItem* menu_item,
|
||||
GtkCallback callback,
|
||||
gpointer callback_data);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif // CHROME_BROWSER_UI_GTK_GTK_CUSTOM_MENU_ITEM_H_
|
||||
295
browser/ui/gtk/gtk_window_util.cc
Normal file
295
browser/ui/gtk/gtk_window_util.cc
Normal file
@@ -0,0 +1,295 @@
|
||||
// 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 "browser/ui/gtk/gtk_window_util.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_contents_view.h"
|
||||
|
||||
using content::RenderWidgetHost;
|
||||
using content::WebContents;
|
||||
|
||||
namespace gtk_window_util {
|
||||
|
||||
const int kFrameBorderThickness = 4;
|
||||
const int kResizeAreaCornerSize = 16;
|
||||
|
||||
// Keep track of the last click time and the last click position so we can
|
||||
// filter out extra GDK_BUTTON_PRESS events when a double click happens.
|
||||
static guint32 last_click_time;
|
||||
static int last_click_x;
|
||||
static int last_click_y;
|
||||
|
||||
// Performs Cut/Copy/Paste operation on the |window|.
|
||||
// If the current render view is focused, then just call the specified |method|
|
||||
// against the current render view host, otherwise emit the specified |signal|
|
||||
// against the focused widget.
|
||||
// TODO(suzhe): This approach does not work for plugins.
|
||||
void DoCutCopyPaste(GtkWindow* window,
|
||||
WebContents* web_contents,
|
||||
void (RenderWidgetHost::*method)(),
|
||||
const char* signal) {
|
||||
GtkWidget* widget = gtk_window_get_focus(window);
|
||||
if (widget == NULL)
|
||||
return; // Do nothing if no focused widget.
|
||||
|
||||
if (web_contents &&
|
||||
widget == web_contents->GetView()->GetContentNativeView()) {
|
||||
(web_contents->GetRenderViewHost()->*method)();
|
||||
} else {
|
||||
guint id;
|
||||
if ((id = g_signal_lookup(signal, G_OBJECT_TYPE(widget))) != 0)
|
||||
g_signal_emit(widget, id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DoCut(GtkWindow* window, WebContents* web_contents) {
|
||||
DoCutCopyPaste(window, web_contents,
|
||||
&RenderWidgetHost::Cut, "cut-clipboard");
|
||||
}
|
||||
|
||||
void DoCopy(GtkWindow* window, WebContents* web_contents) {
|
||||
DoCutCopyPaste(window, web_contents,
|
||||
&RenderWidgetHost::Copy, "copy-clipboard");
|
||||
}
|
||||
|
||||
void DoPaste(GtkWindow* window, WebContents* web_contents) {
|
||||
DoCutCopyPaste(window, web_contents,
|
||||
&RenderWidgetHost::Paste, "paste-clipboard");
|
||||
}
|
||||
|
||||
// Ubuntu patches their version of GTK+ so that there is always a
|
||||
// gripper in the bottom right corner of the window. We dynamically
|
||||
// look up this symbol because it's a non-standard Ubuntu extension to
|
||||
// GTK+. We always need to disable this feature since we can't
|
||||
// communicate this to WebKit easily.
|
||||
typedef void (*gtk_window_set_has_resize_grip_func)(GtkWindow*, gboolean);
|
||||
gtk_window_set_has_resize_grip_func gtk_window_set_has_resize_grip_sym;
|
||||
|
||||
void DisableResizeGrip(GtkWindow* window) {
|
||||
static bool resize_grip_looked_up = false;
|
||||
if (!resize_grip_looked_up) {
|
||||
resize_grip_looked_up = true;
|
||||
gtk_window_set_has_resize_grip_sym =
|
||||
reinterpret_cast<gtk_window_set_has_resize_grip_func>(
|
||||
dlsym(NULL, "gtk_window_set_has_resize_grip"));
|
||||
}
|
||||
if (gtk_window_set_has_resize_grip_sym)
|
||||
gtk_window_set_has_resize_grip_sym(window, FALSE);
|
||||
}
|
||||
|
||||
GdkCursorType GdkWindowEdgeToGdkCursorType(GdkWindowEdge edge) {
|
||||
switch (edge) {
|
||||
case GDK_WINDOW_EDGE_NORTH_WEST:
|
||||
return GDK_TOP_LEFT_CORNER;
|
||||
case GDK_WINDOW_EDGE_NORTH:
|
||||
return GDK_TOP_SIDE;
|
||||
case GDK_WINDOW_EDGE_NORTH_EAST:
|
||||
return GDK_TOP_RIGHT_CORNER;
|
||||
case GDK_WINDOW_EDGE_WEST:
|
||||
return GDK_LEFT_SIDE;
|
||||
case GDK_WINDOW_EDGE_EAST:
|
||||
return GDK_RIGHT_SIDE;
|
||||
case GDK_WINDOW_EDGE_SOUTH_WEST:
|
||||
return GDK_BOTTOM_LEFT_CORNER;
|
||||
case GDK_WINDOW_EDGE_SOUTH:
|
||||
return GDK_BOTTOM_SIDE;
|
||||
case GDK_WINDOW_EDGE_SOUTH_EAST:
|
||||
return GDK_BOTTOM_RIGHT_CORNER;
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
return GDK_LAST_CURSOR;
|
||||
}
|
||||
|
||||
bool BoundsMatchMonitorSize(GtkWindow* window, gfx::Rect bounds) {
|
||||
// A screen can be composed of multiple monitors.
|
||||
GdkScreen* screen = gtk_window_get_screen(window);
|
||||
GdkRectangle monitor_size;
|
||||
|
||||
if (gtk_widget_get_realized(GTK_WIDGET(window))) {
|
||||
// |window| has been realized.
|
||||
gint monitor_num = gdk_screen_get_monitor_at_window(screen,
|
||||
gtk_widget_get_window(GTK_WIDGET(window)));
|
||||
gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor_size);
|
||||
return bounds.size() == gfx::Size(monitor_size.width, monitor_size.height);
|
||||
}
|
||||
|
||||
// Make sure the window doesn't match any monitor size. We compare against
|
||||
// all monitors because we don't know which monitor the window is going to
|
||||
// open on before window realized.
|
||||
gint num_monitors = gdk_screen_get_n_monitors(screen);
|
||||
for (gint i = 0; i < num_monitors; ++i) {
|
||||
GdkRectangle monitor_size;
|
||||
gdk_screen_get_monitor_geometry(screen, i, &monitor_size);
|
||||
if (bounds.size() == gfx::Size(monitor_size.width, monitor_size.height))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleTitleBarLeftMousePress(
|
||||
GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
GdkEventButton* event) {
|
||||
// We want to start a move when the user single clicks, but not start a
|
||||
// move when the user double clicks. However, a double click sends the
|
||||
// following GDK events: GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE,
|
||||
// GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE. If we
|
||||
// start a gtk_window_begin_move_drag on the second GDK_BUTTON_PRESS,
|
||||
// the call to gtk_window_maximize fails. To work around this, we
|
||||
// keep track of the last click and if it's going to be a double click,
|
||||
// we don't call gtk_window_begin_move_drag.
|
||||
DCHECK_EQ(event->type, GDK_BUTTON_PRESS);
|
||||
DCHECK_EQ(event->button, 1);
|
||||
|
||||
static GtkSettings* settings = gtk_settings_get_default();
|
||||
gint double_click_time = 250;
|
||||
gint double_click_distance = 5;
|
||||
g_object_get(G_OBJECT(settings),
|
||||
"gtk-double-click-time", &double_click_time,
|
||||
"gtk-double-click-distance", &double_click_distance,
|
||||
NULL);
|
||||
|
||||
guint32 click_time = event->time - last_click_time;
|
||||
int click_move_x = abs(event->x - last_click_x);
|
||||
int click_move_y = abs(event->y - last_click_y);
|
||||
|
||||
last_click_time = event->time;
|
||||
last_click_x = static_cast<int>(event->x);
|
||||
last_click_y = static_cast<int>(event->y);
|
||||
|
||||
if (click_time > static_cast<guint32>(double_click_time) ||
|
||||
click_move_x > double_click_distance ||
|
||||
click_move_y > double_click_distance) {
|
||||
// Ignore drag requests if the window is the size of the screen.
|
||||
// We do this to avoid triggering fullscreen mode in metacity
|
||||
// (without the --no-force-fullscreen flag) and in compiz (with
|
||||
// Legacy Fullscreen Mode enabled).
|
||||
if (!BoundsMatchMonitorSize(window, bounds)) {
|
||||
gtk_window_begin_move_drag(window, event->button,
|
||||
static_cast<gint>(event->x_root),
|
||||
static_cast<gint>(event->y_root),
|
||||
event->time);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void UnMaximize(GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
const gfx::Rect& restored_bounds) {
|
||||
gtk_window_unmaximize(window);
|
||||
|
||||
// It can happen that you end up with a window whose restore size is the same
|
||||
// as the size of the screen, so unmaximizing it merely remaximizes it due to
|
||||
// the same WM feature that SetWindowSize() works around. We try to detect
|
||||
// this and resize the window to work around the issue.
|
||||
if (bounds.size() == restored_bounds.size())
|
||||
gtk_window_resize(window, bounds.width(), bounds.height() - 1);
|
||||
}
|
||||
|
||||
void SetWindowCustomClass(GtkWindow* window, const std::string& wmclass) {
|
||||
gtk_window_set_wmclass(window,
|
||||
wmclass.c_str(),
|
||||
gdk_get_program_class());
|
||||
|
||||
// Set WM_WINDOW_ROLE for session management purposes.
|
||||
// See http://tronche.com/gui/x/icccm/sec-5.html .
|
||||
gtk_window_set_role(window, wmclass.c_str());
|
||||
}
|
||||
|
||||
void SetWindowSize(GtkWindow* window, const gfx::Size& size) {
|
||||
gfx::Size new_size = size;
|
||||
gint current_width = 0;
|
||||
gint current_height = 0;
|
||||
gtk_window_get_size(window, ¤t_width, ¤t_height);
|
||||
GdkRectangle size_with_decorations = {0};
|
||||
GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
||||
if (gdk_window) {
|
||||
gdk_window_get_frame_extents(gdk_window,
|
||||
&size_with_decorations);
|
||||
}
|
||||
|
||||
if (current_width == size_with_decorations.width &&
|
||||
current_height == size_with_decorations.height) {
|
||||
// Make sure the window doesn't match any monitor size. We compare against
|
||||
// all monitors because we don't know which monitor the window is going to
|
||||
// open on (the WM decides that).
|
||||
GdkScreen* screen = gtk_window_get_screen(window);
|
||||
gint num_monitors = gdk_screen_get_n_monitors(screen);
|
||||
for (gint i = 0; i < num_monitors; ++i) {
|
||||
GdkRectangle monitor_size;
|
||||
gdk_screen_get_monitor_geometry(screen, i, &monitor_size);
|
||||
if (gfx::Size(monitor_size.width, monitor_size.height) == size) {
|
||||
gtk_window_resize(window, size.width(), size.height() - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// gtk_window_resize is the size of the window not including decorations,
|
||||
// but we are given the |size| including window decorations.
|
||||
if (size_with_decorations.width > current_width) {
|
||||
new_size.set_width(size.width() - size_with_decorations.width +
|
||||
current_width);
|
||||
}
|
||||
if (size_with_decorations.height > current_height) {
|
||||
new_size.set_height(size.height() - size_with_decorations.height +
|
||||
current_height);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_window_resize(window, new_size.width(), new_size.height());
|
||||
}
|
||||
|
||||
bool GetWindowEdge(const gfx::Size& window_size,
|
||||
int top_edge_inset,
|
||||
int x,
|
||||
int y,
|
||||
GdkWindowEdge* edge) {
|
||||
gfx::Rect middle(window_size);
|
||||
middle.Inset(kFrameBorderThickness,
|
||||
kFrameBorderThickness - top_edge_inset,
|
||||
kFrameBorderThickness,
|
||||
kFrameBorderThickness);
|
||||
if (middle.Contains(x, y))
|
||||
return false;
|
||||
|
||||
gfx::Rect north(0, 0, window_size.width(),
|
||||
kResizeAreaCornerSize - top_edge_inset);
|
||||
gfx::Rect west(0, 0, kResizeAreaCornerSize, window_size.height());
|
||||
gfx::Rect south(0, window_size.height() - kResizeAreaCornerSize,
|
||||
window_size.width(), kResizeAreaCornerSize);
|
||||
gfx::Rect east(window_size.width() - kResizeAreaCornerSize, 0,
|
||||
kResizeAreaCornerSize, window_size.height());
|
||||
|
||||
if (north.Contains(x, y)) {
|
||||
if (west.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_NORTH_WEST;
|
||||
else if (east.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_NORTH_EAST;
|
||||
else
|
||||
*edge = GDK_WINDOW_EDGE_NORTH;
|
||||
} else if (south.Contains(x, y)) {
|
||||
if (west.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_SOUTH_WEST;
|
||||
else if (east.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_SOUTH_EAST;
|
||||
else
|
||||
*edge = GDK_WINDOW_EDGE_SOUTH;
|
||||
} else {
|
||||
if (west.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_WEST;
|
||||
else if (east.Contains(x, y))
|
||||
*edge = GDK_WINDOW_EDGE_EAST;
|
||||
else
|
||||
return false; // The cursor must be outside the window.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gtk_window_util
|
||||
74
browser/ui/gtk/gtk_window_util.h
Normal file
74
browser/ui/gtk/gtk_window_util.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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 ATOM_BROWSER_UI_GTK_GTK_WINDOW_UTIL_H_
|
||||
#define ATOM_BROWSER_UI_GTK_GTK_WINDOW_UTIL_H_
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdk.h>
|
||||
#include "ui/gfx/rect.h"
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace gtk_window_util {
|
||||
|
||||
// The frame border is only visible in restored mode and is hardcoded to 4 px
|
||||
// on each side regardless of the system window border size.
|
||||
extern const int kFrameBorderThickness;
|
||||
// In the window corners, the resize areas don't actually expand bigger, but
|
||||
// the 16 px at the end of each edge triggers diagonal resizing.
|
||||
extern const int kResizeAreaCornerSize;
|
||||
|
||||
// Performs Cut/Copy/Paste operation on the |window|'s |web_contents|.
|
||||
void DoCut(GtkWindow* window, content::WebContents* web_contents);
|
||||
void DoCopy(GtkWindow* window, content::WebContents* web_contents);
|
||||
void DoPaste(GtkWindow* window, content::WebContents* web_contents);
|
||||
|
||||
// Ubuntu patches their version of GTK+ to that there is always a
|
||||
// gripper in the bottom right corner of the window. We always need to
|
||||
// disable this feature since we can't communicate this to WebKit easily.
|
||||
void DisableResizeGrip(GtkWindow* window);
|
||||
|
||||
// Returns the resize cursor corresponding to the window |edge|.
|
||||
GdkCursorType GdkWindowEdgeToGdkCursorType(GdkWindowEdge edge);
|
||||
|
||||
// Returns |true| if the window bounds match the monitor size.
|
||||
bool BoundsMatchMonitorSize(GtkWindow* window, gfx::Rect bounds);
|
||||
|
||||
bool HandleTitleBarLeftMousePress(GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
GdkEventButton* event);
|
||||
|
||||
// Request the underlying window to unmaximize. Also tries to work around
|
||||
// a window manager "feature" that can prevent this in some edge cases.
|
||||
void UnMaximize(GtkWindow* window,
|
||||
const gfx::Rect& bounds,
|
||||
const gfx::Rect& restored_bounds);
|
||||
|
||||
// Set a custom WM_CLASS for a window.
|
||||
void SetWindowCustomClass(GtkWindow* window, const std::string& wmclass);
|
||||
|
||||
// A helper method for setting the GtkWindow size that should be used in place
|
||||
// of calling gtk_window_resize directly. This is done to avoid a WM "feature"
|
||||
// where setting the window size to the monitor size causes the WM to set the
|
||||
// EWMH for full screen mode.
|
||||
void SetWindowSize(GtkWindow* window, const gfx::Size& size);
|
||||
|
||||
// If the point (|x|, |y|) is within the resize border area of the window,
|
||||
// returns true and sets |edge| to the appropriate GdkWindowEdge value.
|
||||
// Otherwise, returns false.
|
||||
// |top_edge_inset| specifies how much smaller (in px) than the default edge
|
||||
// size the top edge should be, used by browser windows to make it easier to
|
||||
// move the window since a lot of title bar space is taken by the tabs.
|
||||
bool GetWindowEdge(const gfx::Size& window_size,
|
||||
int top_edge_inset,
|
||||
int x,
|
||||
int y,
|
||||
GdkWindowEdge* edge);
|
||||
|
||||
} // namespace gtk_window_util
|
||||
|
||||
#endif // ATOM_BROWSER_UI_GTK_GTK_WINDOW_UTIL_H_
|
||||
30
browser/ui/message_box_gtk.cc
Normal file
30
browser/ui/message_box_gtk.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/ui/message_box.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
MessageBoxType type,
|
||||
const std::vector<std::string>& buttons,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const MessageBoxCallback& callback) {
|
||||
callback.Run(0);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
@@ -9,24 +9,27 @@
|
||||
#include "base/callback.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "browser/native_window.h"
|
||||
#include "browser/ui/nsalert_synchronous_sheet_mac.h"
|
||||
|
||||
@interface ModalDelegate : NSObject {
|
||||
@private
|
||||
atom::MessageBoxCallback callback_;
|
||||
NSAlert* alert_;
|
||||
bool callEndModal_;
|
||||
}
|
||||
- (id)initWithCallback:(const atom::MessageBoxCallback&)callback
|
||||
andAlert:(NSAlert*)alert;
|
||||
andAlert:(NSAlert*)alert
|
||||
callEndModal:(bool)flag;
|
||||
@end
|
||||
|
||||
@implementation ModalDelegate
|
||||
|
||||
- (id)initWithCallback:(const atom::MessageBoxCallback&)callback
|
||||
andAlert:(NSAlert*)alert {
|
||||
andAlert:(NSAlert*)alert
|
||||
callEndModal:(bool)flag {
|
||||
if ((self = [super init])) {
|
||||
callback_ = callback;
|
||||
alert_ = alert;
|
||||
callEndModal_ = flag;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -37,6 +40,9 @@
|
||||
callback_.Run(returnCode);
|
||||
[alert_ release];
|
||||
[self release];
|
||||
|
||||
if (callEndModal_)
|
||||
[NSApp stopModal];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -76,6 +82,10 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
||||
return alert;
|
||||
}
|
||||
|
||||
void SetReturnCode(int* ret_code, int result) {
|
||||
*ret_code = result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ShowMessageBox(NativeWindow* parent_window,
|
||||
@@ -86,12 +96,26 @@ int ShowMessageBox(NativeWindow* parent_window,
|
||||
const std::string& detail) {
|
||||
NSAlert* alert = CreateNSAlert(
|
||||
parent_window, type, buttons, title, message, detail);
|
||||
[alert autorelease];
|
||||
|
||||
if (parent_window)
|
||||
return [alert runModalSheetForWindow:parent_window->GetNativeWindow()];
|
||||
else
|
||||
return [alert runModal];
|
||||
// Use runModal for synchronous alert without parent, since we don't have a
|
||||
// window to wait for.
|
||||
if (!parent_window)
|
||||
return [[alert autorelease] runModal];
|
||||
|
||||
int ret_code = -1;
|
||||
ModalDelegate* delegate = [[ModalDelegate alloc]
|
||||
initWithCallback:base::Bind(&SetReturnCode, &ret_code)
|
||||
andAlert:alert
|
||||
callEndModal:true];
|
||||
|
||||
NSWindow* window = parent_window->GetNativeWindow();
|
||||
[alert beginSheetModalForWindow:window
|
||||
modalDelegate:delegate
|
||||
didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
|
||||
contextInfo:nil];
|
||||
|
||||
[NSApp runModalForWindow:window];
|
||||
return ret_code;
|
||||
}
|
||||
|
||||
void ShowMessageBox(NativeWindow* parent_window,
|
||||
@@ -104,7 +128,8 @@ void ShowMessageBox(NativeWindow* parent_window,
|
||||
NSAlert* alert = CreateNSAlert(
|
||||
parent_window, type, buttons, title, message, detail);
|
||||
ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback
|
||||
andAlert:alert];
|
||||
andAlert:alert
|
||||
callEndModal:false];
|
||||
|
||||
NSWindow* window = parent_window ? parent_window->GetNativeWindow() : nil;
|
||||
[alert beginSheetModalForWindow:window
|
||||
|
||||
@@ -76,6 +76,7 @@ class MessageDialog : public base::MessageLoop::Dispatcher,
|
||||
string16 title_;
|
||||
views::Widget* widget_;
|
||||
views::MessageBoxView* message_box_view_;
|
||||
scoped_ptr<NativeWindow::DialogScope> dialog_scope_;
|
||||
std::vector<views::LabelButton*> buttons_;
|
||||
MessageBoxCallback callback_;
|
||||
|
||||
@@ -96,7 +97,8 @@ MessageDialog::MessageDialog(NativeWindow* parent_window,
|
||||
result_(-1),
|
||||
title_(UTF8ToUTF16(title)),
|
||||
widget_(NULL),
|
||||
message_box_view_(NULL) {
|
||||
message_box_view_(NULL),
|
||||
dialog_scope_(new NativeWindow::DialogScope(parent_window)) {
|
||||
DCHECK_GT(buttons.size(), 0u);
|
||||
set_owned_by_client();
|
||||
|
||||
@@ -174,6 +176,7 @@ string16 MessageDialog::GetWindowTitle() const {
|
||||
|
||||
void MessageDialog::WindowClosing() {
|
||||
should_close_ = true;
|
||||
dialog_scope_.reset();
|
||||
|
||||
if (delete_on_close_) {
|
||||
callback_.Run(GetResult());
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface NSAlert (SynchronousSheet)
|
||||
-(NSInteger) runModalSheetForWindow:(NSWindow*)aWindow;
|
||||
-(NSInteger) runModalSheet;
|
||||
@end
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "browser/ui/nsalert_synchronous_sheet_mac.h"
|
||||
|
||||
// Private methods -- use prefixes to avoid collisions with Apple's methods
|
||||
@interface NSAlert (SynchronousSheetPrivate)
|
||||
-(IBAction) BE_stopSynchronousSheet:(id)sender; // hide sheet & stop modal
|
||||
-(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow;
|
||||
@end
|
||||
|
||||
@implementation NSAlert (SynchronousSheet)
|
||||
|
||||
-(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow {
|
||||
// Set ourselves as the target for button clicks
|
||||
for (NSButton *button in [self buttons]) {
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(BE_stopSynchronousSheet:)];
|
||||
}
|
||||
|
||||
// Bring up the sheet and wait until stopSynchronousSheet is triggered by a button click
|
||||
[self performSelectorOnMainThread:@selector(BE_beginSheetModalForWindow:) withObject:aWindow waitUntilDone:YES];
|
||||
NSInteger modalCode = [NSApp runModalForWindow:[self window]];
|
||||
|
||||
// This is called only after stopSynchronousSheet is called (that is,
|
||||
// one of the buttons is clicked)
|
||||
[NSApp performSelectorOnMainThread:@selector(endSheet:) withObject:[self window] waitUntilDone:YES];
|
||||
|
||||
// Remove the sheet from the screen
|
||||
[[self window] performSelectorOnMainThread:@selector(orderOut:) withObject:self waitUntilDone:YES];
|
||||
|
||||
return modalCode;
|
||||
}
|
||||
|
||||
-(NSInteger) runModalSheet {
|
||||
return [self runModalSheetForWindow:[NSApp mainWindow]];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Private methods
|
||||
|
||||
-(IBAction) BE_stopSynchronousSheet:(id)sender {
|
||||
[NSApp stopModalWithCode:[sender tag]];
|
||||
}
|
||||
|
||||
-(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow {
|
||||
[self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -19,14 +19,17 @@ class WindowListObserver;
|
||||
class WindowList {
|
||||
public:
|
||||
typedef std::vector<NativeWindow*> WindowVector;
|
||||
typedef WindowVector::iterator iterator;
|
||||
typedef WindowVector::const_iterator const_iterator;
|
||||
typedef WindowVector::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
// Windows are added to the list before they have constructed windows,
|
||||
// so the |window()| member function may return NULL.
|
||||
const_iterator begin() const { return windows_.begin(); }
|
||||
const_iterator end() const { return windows_.end(); }
|
||||
|
||||
iterator begin() { return windows_.begin(); }
|
||||
iterator end() { return windows_.end(); }
|
||||
|
||||
bool empty() const { return windows_.empty(); }
|
||||
size_t size() const { return windows_.size(); }
|
||||
|
||||
|
||||
81
common.gypi
81
common.gypi
@@ -2,7 +2,7 @@
|
||||
'variables': {
|
||||
'clang': 0,
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
['OS=="mac" or OS=="linux"', {
|
||||
'clang': 1,
|
||||
}],
|
||||
['OS=="win" and (MSVS_VERSION=="2012e" or MSVS_VERSION=="2010e")', {
|
||||
@@ -31,11 +31,13 @@
|
||||
'node_use_perfctr': 'false',
|
||||
'node_use_systemtap': 'false',
|
||||
'v8_postmortem_support': 'false',
|
||||
# Required by Linux (empty for now, should support it in future).
|
||||
'sysroot': '',
|
||||
},
|
||||
# Settings to compile node under Windows.
|
||||
'target_defaults': {
|
||||
'target_conditions': [
|
||||
['_target_name in ["libuv", "http_parser", "cares", "openssl", "node_lib", "zlib"]', {
|
||||
['_target_name in ["libuv", "http_parser", "cares", "openssl", "openssl-cli", "node_lib", "zlib"]', {
|
||||
'msvs_disabled_warnings': [
|
||||
4013, # 'free' undefined; assuming extern returning int
|
||||
4054, #
|
||||
@@ -49,13 +51,14 @@
|
||||
4204, # non-constant aggregate initializer
|
||||
4214, # bit field types other than int
|
||||
4232, # address of dllimport 'free' is not static, identity not guaranteed
|
||||
4291, # no matching operator delete found
|
||||
4295, # array is too small to include a terminating null character
|
||||
4389, # '==' : signed/unsigned mismatch
|
||||
4505, # unreferenced local function has been removed
|
||||
4701, # potentially uninitialized local variable 'sizew' used
|
||||
4706, # assignment within conditional expression
|
||||
4804, # unsafe use of type 'bool' in operation
|
||||
4996, #
|
||||
4804, # unsafe use of type 'bool' in operation
|
||||
4996, # this function or variable may be unsafe.
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
@@ -75,6 +78,21 @@
|
||||
'-Wno-return-type',
|
||||
],
|
||||
},
|
||||
'conditions': [
|
||||
['OS=="linux"', {
|
||||
'cflags': [
|
||||
'-Wno-parentheses-equality',
|
||||
'-Wno-unused-function',
|
||||
'-Wno-sometimes-uninitialized',
|
||||
'-Wno-pointer-sign',
|
||||
'-Wno-string-plus-int',
|
||||
'-Wno-unused-variable',
|
||||
'-Wno-unused-value',
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-return-type',
|
||||
],
|
||||
}],
|
||||
],
|
||||
}],
|
||||
['_target_name in ["node_lib", "atom_lib"]', {
|
||||
'include_dirs': [
|
||||
@@ -92,13 +110,22 @@
|
||||
],
|
||||
}],
|
||||
['_target_name.startswith("breakpad") or _target_name in ["crash_report_sender", "dump_syms"]', {
|
||||
'xcode_settings': {
|
||||
'WARNING_CFLAGS': [
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-unused-private-field',
|
||||
'-Wno-unused-function',
|
||||
],
|
||||
},
|
||||
'conditions': [
|
||||
['OS=="mac"', {
|
||||
'xcode_settings': {
|
||||
'WARNING_CFLAGS': [
|
||||
'-Wno-deprecated-declarations',
|
||||
'-Wno-unused-private-field',
|
||||
'-Wno-unused-function',
|
||||
],
|
||||
},
|
||||
}], # OS=="mac"
|
||||
['OS=="linux"', {
|
||||
'cflags': [
|
||||
'-Wno-empty-body',
|
||||
],
|
||||
}], # OS=="linux"
|
||||
],
|
||||
}],
|
||||
],
|
||||
'msvs_cygwin_shell': 0, # Strangely setting it to 1 would make building under cygwin fail.
|
||||
@@ -152,7 +179,9 @@
|
||||
],
|
||||
'target_defaults': {
|
||||
'cflags_cc': [
|
||||
'-std=c++11',
|
||||
# Use gnu++11 instead of c++11 here, see:
|
||||
# https://code.google.com/p/chromium/issues/detail?id=224515
|
||||
'-std=gnu++11',
|
||||
],
|
||||
'xcode_settings': {
|
||||
'CC': '/usr/bin/clang',
|
||||
@@ -206,5 +235,33 @@
|
||||
},
|
||||
},
|
||||
}], # OS=="win"
|
||||
# The breakdpad on Mac assumes Release_Base configuration.
|
||||
['OS=="mac"', {
|
||||
'target_defaults': {
|
||||
'configurations': {
|
||||
'Release_Base': {
|
||||
},
|
||||
},
|
||||
},
|
||||
}], # OS=="mac"
|
||||
# The breakpad on Linux needs the binary to be built with -g to generate
|
||||
# unmangled symbols.
|
||||
['OS=="linux"', {
|
||||
'target_defaults': {
|
||||
'cflags': [ '-g' ],
|
||||
'conditions': [
|
||||
['target_arch=="ia32"', {
|
||||
'target_conditions': [
|
||||
['_toolset=="target"', {
|
||||
'ldflags': [
|
||||
# Workaround for linker OOM.
|
||||
'-Wl,--no-keep-memory',
|
||||
],
|
||||
}],
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
}
|
||||
|
||||
@@ -74,8 +74,6 @@ void Clipboard::Clear(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// static
|
||||
void Clipboard::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
NODE_SET_METHOD(target, "has", Has);
|
||||
NODE_SET_METHOD(target, "read", Read);
|
||||
NODE_SET_METHOD(target, "readText", ReadText);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/api/atom_api_event_emitter.h"
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -21,6 +21,11 @@ EventEmitter::EventEmitter(v8::Handle<v8::Object> wrapper) {
|
||||
}
|
||||
|
||||
EventEmitter::~EventEmitter() {
|
||||
// Use Locker in browser process.
|
||||
scoped_ptr<v8::Locker> locker;
|
||||
if (node::g_standalone_mode)
|
||||
locker.reset(new v8::Locker(node_isolate));
|
||||
|
||||
// Clear the aligned pointer, it should have been done by ObjectWrap but
|
||||
// somehow node v0.11.x changed this behaviour.
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
@@ -33,6 +38,11 @@ bool EventEmitter::Emit(const std::string& name) {
|
||||
}
|
||||
|
||||
bool EventEmitter::Emit(const std::string& name, base::ListValue* args) {
|
||||
// Use Locker in browser process.
|
||||
scoped_ptr<v8::Locker> locker;
|
||||
if (node::g_standalone_mode)
|
||||
locker.reset(new v8::Locker(node_isolate));
|
||||
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Handle<v8::Context> context = v8::Context::GetCurrent();
|
||||
@@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_EVENT_EMITTER_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_EVENT_EMITTER_H_
|
||||
#ifndef ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_
|
||||
#define ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -40,4 +40,4 @@ class EventEmitter : public node::ObjectWrap {
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_EVENT_EMITTER_H_
|
||||
#endif // ATOM_COMMON_API_ATOM_API_EVENT_EMITTER_H_
|
||||
@@ -122,8 +122,6 @@ void IDWeakMap::Remove(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// static
|
||||
void IDWeakMap::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("IDWeakMap"));
|
||||
|
||||
93
common/api/atom_api_screen.cc
Normal file
93
common/api/atom_api_screen.cc
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "common/api/atom_api_screen.h"
|
||||
|
||||
#include "common/v8/native_type_conversions.h"
|
||||
#include "ui/gfx/screen.h"
|
||||
|
||||
#include "common/v8/node_common.h"
|
||||
|
||||
#if defined(TOOLKIT_GTK)
|
||||
#include "base/command_line.h"
|
||||
#include "ui/gfx/gtk_util.h"
|
||||
#endif
|
||||
|
||||
#define UNWRAP_SCREEN_AND_CHECK \
|
||||
Screen* self = ObjectWrap::Unwrap<Screen>(args.This()); \
|
||||
if (self == NULL) \
|
||||
return node::ThrowError("Screen is already destroyed")
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
|
||||
v8::Handle<v8::Object> DisplayToV8Value(const gfx::Display& display) {
|
||||
v8::Handle<v8::Object> obj = v8::Object::New();
|
||||
obj->Set(ToV8Value("bounds"), ToV8Value(display.bounds()));
|
||||
obj->Set(ToV8Value("workArea"), ToV8Value(display.work_area()));
|
||||
obj->Set(ToV8Value("size"), ToV8Value(display.size()));
|
||||
obj->Set(ToV8Value("workAreaSize"), ToV8Value(display.work_area_size()));
|
||||
obj->Set(ToV8Value("scaleFactor"), ToV8Value(display.device_scale_factor()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Screen::Screen(v8::Handle<v8::Object> wrapper)
|
||||
: EventEmitter(wrapper),
|
||||
screen_(gfx::Screen::GetNativeScreen()) {
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
}
|
||||
|
||||
// static
|
||||
void Screen::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope scope(args.GetIsolate());
|
||||
|
||||
if (!args.IsConstructCall())
|
||||
return node::ThrowError("Require constructor call");
|
||||
|
||||
new Screen(args.This());
|
||||
}
|
||||
|
||||
// static
|
||||
void Screen::GetCursorScreenPoint(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
UNWRAP_SCREEN_AND_CHECK;
|
||||
args.GetReturnValue().Set(ToV8Value(self->screen_->GetCursorScreenPoint()));
|
||||
}
|
||||
|
||||
// static
|
||||
void Screen::GetPrimaryDisplay(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
UNWRAP_SCREEN_AND_CHECK;
|
||||
gfx::Display display = self->screen_->GetPrimaryDisplay();
|
||||
args.GetReturnValue().Set(DisplayToV8Value(display));
|
||||
}
|
||||
|
||||
// static
|
||||
void Screen::Initialize(v8::Handle<v8::Object> target) {
|
||||
#if defined(TOOLKIT_GTK)
|
||||
gfx::GdkInitFromCommandLine(*CommandLine::ForCurrentProcess());
|
||||
#endif
|
||||
|
||||
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);
|
||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
t->SetClassName(v8::String::NewSymbol("Screen"));
|
||||
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "getCursorScreenPoint", GetCursorScreenPoint);
|
||||
NODE_SET_PROTOTYPE_METHOD(t, "getPrimaryDisplay", GetPrimaryDisplay);
|
||||
|
||||
target->Set(v8::String::NewSymbol("Screen"), t->GetFunction());
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
NODE_MODULE(atom_common_screen, atom::api::Screen::Initialize)
|
||||
44
common/api/atom_api_screen.h
Normal file
44
common/api/atom_api_screen.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_API_ATOM_API_SCREEN_H_
|
||||
#define ATOM_COMMON_API_ATOM_API_SCREEN_H_
|
||||
|
||||
#include "common/api/atom_api_event_emitter.h"
|
||||
|
||||
namespace gfx {
|
||||
class Screen;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class Screen : public EventEmitter {
|
||||
public:
|
||||
virtual ~Screen();
|
||||
|
||||
static void Initialize(v8::Handle<v8::Object> target);
|
||||
|
||||
protected:
|
||||
explicit Screen(v8::Handle<v8::Object> wrapper);
|
||||
|
||||
private:
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
static void GetCursorScreenPoint(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetPrimaryDisplay(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
gfx::Screen* screen_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Screen);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_COMMON_API_ATOM_API_SCREEN_H_
|
||||
@@ -60,8 +60,6 @@ void Shell::Beep(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
// static
|
||||
void Shell::Initialize(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
NODE_SET_METHOD(target, "showItemInFolder", ShowItemInFolder);
|
||||
NODE_SET_METHOD(target, "openItem", OpenItem);
|
||||
NODE_SET_METHOD(target, "openExternal", OpenExternal);
|
||||
|
||||
@@ -46,8 +46,6 @@ void TakeHeapSnapshot(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
} // namespace
|
||||
|
||||
void InitializeV8Util(v8::Handle<v8::Object> target) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
NODE_SET_METHOD(target, "createObjectWithName", CreateObjectWithName);
|
||||
NODE_SET_METHOD(target, "getHiddenValue", GetHiddenValue);
|
||||
NODE_SET_METHOD(target, "setHiddenValue", SetHiddenValue);
|
||||
|
||||
@@ -16,11 +16,48 @@ namespace {
|
||||
|
||||
static int kMaxCallStackSize = 200; // Same with WebKit.
|
||||
|
||||
static uv_async_t dummy_uv_handle;
|
||||
// Async handle to wake up uv loop.
|
||||
static uv_async_t g_next_tick_uv_handle;
|
||||
|
||||
// Async handle to execute the stored v8 callback.
|
||||
static uv_async_t g_callback_uv_handle;
|
||||
|
||||
// Stored v8 callback, to be called by the async handler.
|
||||
RefCountedV8Function g_v8_callback;
|
||||
|
||||
// Dummy class type that used for crashing the program.
|
||||
struct DummyClass { bool crash; };
|
||||
|
||||
void UvNoOp(uv_async_t* handle, int status) {
|
||||
// Async handler to call next process.nextTick callbacks.
|
||||
void UvCallNextTick(uv_async_t* handle, int status) {
|
||||
node::Environment* env = node::Environment::GetCurrent(node_isolate);
|
||||
node::Environment::TickInfo* tick_info = env->tick_info();
|
||||
|
||||
if (tick_info->in_tick())
|
||||
return;
|
||||
|
||||
if (tick_info->length() == 0) {
|
||||
tick_info->set_index(0);
|
||||
return;
|
||||
}
|
||||
|
||||
tick_info->set_in_tick(true);
|
||||
env->tick_callback_function()->Call(env->process_object(), 0, NULL);
|
||||
tick_info->set_in_tick(false);
|
||||
}
|
||||
|
||||
// Async handler to execute the stored v8 callback.
|
||||
void UvOnCallback(uv_async_t* handle, int status) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
v8::Handle<v8::Object> global = v8::Context::GetCurrent()->Global();
|
||||
g_v8_callback->NewHandle()->Call(global, 0, NULL);
|
||||
}
|
||||
|
||||
// Called when there is a fatal error in V8, we just crash the process here so
|
||||
// we can get the stack trace.
|
||||
void FatalErrorCallback(const char* location, const char* message) {
|
||||
LOG(ERROR) << "Fatal error in V8: " << location << " " << message;
|
||||
static_cast<DummyClass*>(NULL)->crash = true;
|
||||
}
|
||||
|
||||
v8::Handle<v8::Object> DumpStackFrame(v8::Handle<v8::StackFrame> stack_frame) {
|
||||
@@ -44,20 +81,21 @@ v8::Handle<v8::Object> DumpStackFrame(v8::Handle<v8::StackFrame> stack_frame) {
|
||||
node::node_module_struct* GetBuiltinModule(const char *name, bool is_browser);
|
||||
|
||||
AtomBindings::AtomBindings() {
|
||||
uv_async_init(uv_default_loop(), &dummy_uv_handle, UvNoOp);
|
||||
uv_async_init(uv_default_loop(), &g_next_tick_uv_handle, UvCallNextTick);
|
||||
uv_async_init(uv_default_loop(), &g_callback_uv_handle, UvOnCallback);
|
||||
v8::V8::SetFatalErrorHandler(FatalErrorCallback);
|
||||
}
|
||||
|
||||
AtomBindings::~AtomBindings() {
|
||||
}
|
||||
|
||||
void AtomBindings::BindTo(v8::Handle<v8::Object> process) {
|
||||
v8::HandleScope handle_scope(node_isolate);
|
||||
|
||||
NODE_SET_METHOD(process, "atomBinding", Binding);
|
||||
NODE_SET_METHOD(process, "crash", Crash);
|
||||
NODE_SET_METHOD(process, "activateUvLoop", ActivateUVLoop);
|
||||
NODE_SET_METHOD(process, "log", Log);
|
||||
NODE_SET_METHOD(process, "getCurrentStackTrace", GetCurrentStackTrace);
|
||||
NODE_SET_METHOD(process, "scheduleCallback", ScheduleCallback);
|
||||
|
||||
process->Get(v8::String::New("versions"))->ToObject()->
|
||||
Set(v8::String::New("atom-shell"), v8::String::New(ATOM_VERSION_STRING));
|
||||
@@ -115,23 +153,18 @@ void AtomBindings::Crash(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
// static
|
||||
void AtomBindings::ActivateUVLoop(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
uv_async_send(&dummy_uv_handle);
|
||||
uv_async_send(&g_next_tick_uv_handle);
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomBindings::Log(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
std::string message;
|
||||
for (int i = 0; i < args.Length(); ++i)
|
||||
message += *v8::String::Utf8Value(args[i]);
|
||||
|
||||
logging::LogMessage("CONSOLE", 0, 0).stream() << message;
|
||||
v8::String::Utf8Value str(args[0]);
|
||||
logging::LogMessage("CONSOLE", 0, 0).stream() << *str;
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomBindings::GetCurrentStackTrace(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::HandleScope handle_scope(args.GetIsolate());
|
||||
|
||||
int stack_limit = kMaxCallStackSize;
|
||||
FromV8Arguments(args, &stack_limit);
|
||||
|
||||
@@ -146,4 +179,12 @@ void AtomBindings::GetCurrentStackTrace(
|
||||
args.GetReturnValue().Set(result);
|
||||
}
|
||||
|
||||
// static
|
||||
void AtomBindings::ScheduleCallback(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (!FromV8Arguments(args, &g_v8_callback))
|
||||
return node::ThrowTypeError("Bad arguments");
|
||||
uv_async_send(&g_callback_uv_handle);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -26,6 +26,7 @@ class AtomBindings {
|
||||
static void Log(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void GetCurrentStackTrace(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void ScheduleCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomBindings);
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ NODE_EXT_LIST_ITEM(atom_renderer_ipc)
|
||||
NODE_EXT_LIST_ITEM(atom_common_clipboard)
|
||||
NODE_EXT_LIST_ITEM(atom_common_crash_reporter)
|
||||
NODE_EXT_LIST_ITEM(atom_common_id_weak_map)
|
||||
NODE_EXT_LIST_ITEM(atom_common_screen)
|
||||
NODE_EXT_LIST_ITEM(atom_common_shell)
|
||||
NODE_EXT_LIST_ITEM(atom_common_v8_util)
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@ class CrashReporter
|
||||
|
||||
start = -> binding.start productName, companyName, submitUrl, autoSubmit, ignoreSystemCrashHandler, extra
|
||||
|
||||
if process.platform is 'darwin'
|
||||
start()
|
||||
else
|
||||
if process.platform is 'win32'
|
||||
args = [
|
||||
"--reporter-url=#{submitUrl}"
|
||||
"--application-name=#{productName}"
|
||||
@@ -34,5 +32,7 @@ class CrashReporter
|
||||
|
||||
spawn process.execPath, args, {env, detached: true}
|
||||
start()
|
||||
else
|
||||
start()
|
||||
|
||||
module.exports = new CrashReporter
|
||||
|
||||
3
common/api/lib/screen.coffee
Normal file
3
common/api/lib/screen.coffee
Normal file
@@ -0,0 +1,3 @@
|
||||
{Screen} = process.atomBinding 'screen'
|
||||
|
||||
module.exports = new Screen
|
||||
@@ -6,8 +6,8 @@
|
||||
#define ATOM_VERSION_H
|
||||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 8
|
||||
#define ATOM_PATCH_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 10
|
||||
#define ATOM_PATCH_VERSION 6
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
134
common/crash_reporter/crash_reporter_linux.cc
Normal file
134
common/crash_reporter/crash_reporter_linux.cc
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// 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 "common/crash_reporter/crash_reporter_linux.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/debug/crash_logging.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/linux_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/process/memory.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "vendor/breakpad/src/client/linux/handler/exception_handler.h"
|
||||
#include "vendor/breakpad/src/common/linux/linux_libc_support.h"
|
||||
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::MinidumpDescriptor;
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
namespace {
|
||||
|
||||
static const size_t kDistroSize = 128;
|
||||
|
||||
// Define a preferred limit on minidump sizes, because Crash Server currently
|
||||
// throws away any larger than 1.2MB (1.2 * 1024 * 1024). A value of -1 means
|
||||
// no limit.
|
||||
static const off_t kMaxMinidumpFileSize = 1258291;
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashReporterLinux::CrashReporterLinux()
|
||||
: process_start_time_(0),
|
||||
pid_(getpid()) {
|
||||
// Set the base process start time value.
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
uint64_t ret = tv.tv_sec;
|
||||
ret *= 1000;
|
||||
ret += tv.tv_usec / 1000;
|
||||
process_start_time_ = ret;
|
||||
}
|
||||
|
||||
// Make base::g_linux_distro work.
|
||||
base::SetLinuxDistro(base::GetLinuxDistro());
|
||||
}
|
||||
|
||||
CrashReporterLinux::~CrashReporterLinux() {
|
||||
}
|
||||
|
||||
void CrashReporterLinux::InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) {
|
||||
EnableCrashDumping();
|
||||
|
||||
crash_keys_.SetKeyValue("prod", "Atom-Shell");
|
||||
crash_keys_.SetKeyValue("ver", version.c_str());
|
||||
upload_url_ = submit_url;
|
||||
|
||||
for (StringMap::const_iterator iter = upload_parameters_.begin();
|
||||
iter != upload_parameters_.end(); ++iter)
|
||||
crash_keys_.SetKeyValue(iter->first.c_str(), iter->second.c_str());
|
||||
}
|
||||
|
||||
void CrashReporterLinux::SetUploadParameters() {
|
||||
upload_parameters_["platform"] = "linux";
|
||||
}
|
||||
|
||||
void CrashReporterLinux::EnableCrashDumping() {
|
||||
base::FilePath tmp_path("/tmp");
|
||||
PathService::Get(base::DIR_TEMP, &tmp_path);
|
||||
|
||||
base::FilePath dumps_path(tmp_path);
|
||||
MinidumpDescriptor minidump_descriptor(dumps_path.value());
|
||||
minidump_descriptor.set_size_limit(kMaxMinidumpFileSize);
|
||||
|
||||
breakpad_.reset(new ExceptionHandler(
|
||||
minidump_descriptor,
|
||||
NULL,
|
||||
CrashDone,
|
||||
this,
|
||||
true, // Install handlers.
|
||||
-1));
|
||||
}
|
||||
|
||||
bool CrashReporterLinux::CrashDone(const MinidumpDescriptor& minidump,
|
||||
void* context,
|
||||
const bool succeeded) {
|
||||
CrashReporterLinux* self = static_cast<CrashReporterLinux*>(context);
|
||||
|
||||
// WARNING: this code runs in a compromised context. It may not call into
|
||||
// libc nor allocate memory normally.
|
||||
if (!succeeded) {
|
||||
const char msg[] = "Failed to generate minidump.";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
DCHECK(!minidump.IsFD());
|
||||
|
||||
BreakpadInfo info = {0};
|
||||
info.filename = minidump.path();
|
||||
info.fd = minidump.fd();
|
||||
info.distro = base::g_linux_distro;
|
||||
info.distro_length = my_strlen(base::g_linux_distro);
|
||||
info.upload = true;
|
||||
info.process_start_time = self->process_start_time_;
|
||||
info.oom_size = base::g_oom_size;
|
||||
info.pid = self->pid_;
|
||||
info.upload_url = self->upload_url_.c_str();
|
||||
info.crash_keys = &self->crash_keys_;
|
||||
HandleCrashDump(info);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporterLinux* CrashReporterLinux::GetInstance() {
|
||||
return Singleton<CrashReporterLinux>::get();
|
||||
}
|
||||
|
||||
// static
|
||||
CrashReporter* CrashReporter::GetInstance() {
|
||||
return CrashReporterLinux::GetInstance();
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
||||
57
common/crash_reporter/crash_reporter_linux.h
Normal file
57
common/crash_reporter/crash_reporter_linux.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
|
||||
#define ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "common/crash_reporter/crash_reporter.h"
|
||||
#include "common/crash_reporter/linux/crash_dump_handler.h"
|
||||
|
||||
template <typename T> struct DefaultSingletonTraits;
|
||||
|
||||
namespace google_breakpad {
|
||||
class ExceptionHandler;
|
||||
class MinidumpDescriptor;
|
||||
}
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
class CrashReporterLinux : public CrashReporter {
|
||||
public:
|
||||
static CrashReporterLinux* GetInstance();
|
||||
|
||||
virtual void InitBreakpad(const std::string& product_name,
|
||||
const std::string& version,
|
||||
const std::string& company_name,
|
||||
const std::string& submit_url,
|
||||
bool auto_submit,
|
||||
bool skip_system_crash_handler) OVERRIDE;
|
||||
virtual void SetUploadParameters() OVERRIDE;
|
||||
|
||||
private:
|
||||
friend struct DefaultSingletonTraits<CrashReporterLinux>;
|
||||
|
||||
CrashReporterLinux();
|
||||
virtual ~CrashReporterLinux();
|
||||
|
||||
void EnableCrashDumping();
|
||||
|
||||
static bool CrashDone(const google_breakpad::MinidumpDescriptor& minidump,
|
||||
void* context,
|
||||
const bool succeeded);
|
||||
|
||||
scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
|
||||
CrashKeyStorage crash_keys_;
|
||||
|
||||
uint64_t process_start_time_;
|
||||
pid_t pid_;
|
||||
std::string upload_url_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReporterLinux);
|
||||
};
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_COMMON_CRASH_REPORTER_CRASH_REPORTER_LINUX_H_
|
||||
668
common/crash_reporter/linux/crash_dump_handler.cc
Normal file
668
common/crash_reporter/linux/crash_dump_handler.cc
Normal file
@@ -0,0 +1,668 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// 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.
|
||||
|
||||
// For linux_syscall_support.h. This makes it safe to call embedded system
|
||||
// calls when in seccomp mode.
|
||||
|
||||
#include "common/crash_reporter/linux/crash_dump_handler.h"
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "vendor/breakpad/src/client/linux/minidump_writer/directory_reader.h"
|
||||
#include "vendor/breakpad/src/common/linux/linux_libc_support.h"
|
||||
#include "vendor/breakpad/src/common/memory.h"
|
||||
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
// Some versions of gcc are prone to warn about unused return values. In cases
|
||||
// where we either a) know the call cannot fail, or b) there is nothing we
|
||||
// can do when a call fails, we mark the return code as ignored. This avoids
|
||||
// spurious compiler warnings.
|
||||
#define IGNORE_RET(x) do { if (x); } while (0)
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
namespace {
|
||||
|
||||
// String buffer size to use to convert a uint64_t to string.
|
||||
const size_t kUint64StringSize = 21;
|
||||
|
||||
// Writes the value |v| as 16 hex characters to the memory pointed at by
|
||||
// |output|.
|
||||
void write_uint64_hex(char* output, uint64_t v) {
|
||||
static const char hextable[] = "0123456789abcdef";
|
||||
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
output[i] = hextable[v & 15];
|
||||
v >>= 4;
|
||||
}
|
||||
}
|
||||
|
||||
// uint64_t version of my_int_len() from
|
||||
// breakpad/src/common/linux/linux_libc_support.h. Return the length of the
|
||||
// given, non-negative integer when expressed in base 10.
|
||||
unsigned my_uint64_len(uint64_t i) {
|
||||
if (!i)
|
||||
return 1;
|
||||
|
||||
unsigned len = 0;
|
||||
while (i) {
|
||||
len++;
|
||||
i /= 10;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// uint64_t version of my_uitos() from
|
||||
// breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
|
||||
// integer to a string (not null-terminated).
|
||||
void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
|
||||
for (unsigned index = i_len; index; --index, i /= 10)
|
||||
output[index - 1] = '0' + (i % 10);
|
||||
}
|
||||
|
||||
// Converts a struct timeval to milliseconds.
|
||||
uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) {
|
||||
uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
|
||||
ret *= 1000;
|
||||
ret += tv->tv_usec / 1000;
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t LengthWithoutTrailingSpaces(const char* str, size_t len) {
|
||||
while (len > 0 && str[len - 1] == ' ') {
|
||||
len--;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// MIME substrings.
|
||||
const char g_rn[] = "\r\n";
|
||||
const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
|
||||
const char g_quote_msg[] = "\"";
|
||||
const char g_dashdash_msg[] = "--";
|
||||
const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
|
||||
const char g_content_type_msg[] = "Content-Type: application/octet-stream";
|
||||
|
||||
// MimeWriter manages an iovec for writing MIMEs to a file.
|
||||
class MimeWriter {
|
||||
public:
|
||||
static const int kIovCapacity = 30;
|
||||
static const size_t kMaxCrashChunkSize = 64;
|
||||
|
||||
MimeWriter(int fd, const char* const mime_boundary);
|
||||
~MimeWriter();
|
||||
|
||||
// Append boundary.
|
||||
virtual void AddBoundary();
|
||||
|
||||
// Append end of file boundary.
|
||||
virtual void AddEnd();
|
||||
|
||||
// Append key/value pair with specified sizes.
|
||||
virtual void AddPairData(const char* msg_type,
|
||||
size_t msg_type_size,
|
||||
const char* msg_data,
|
||||
size_t msg_data_size);
|
||||
|
||||
// Append key/value pair.
|
||||
void AddPairString(const char* msg_type,
|
||||
const char* msg_data) {
|
||||
AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
|
||||
}
|
||||
|
||||
// Append key/value pair, splitting value into chunks no larger than
|
||||
// |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
|
||||
// The msg_type string will have a counter suffix to distinguish each chunk.
|
||||
virtual void AddPairDataInChunks(const char* msg_type,
|
||||
size_t msg_type_size,
|
||||
const char* msg_data,
|
||||
size_t msg_data_size,
|
||||
size_t chunk_size,
|
||||
bool strip_trailing_spaces);
|
||||
|
||||
// Add binary file contents to be uploaded with the specified filename.
|
||||
virtual void AddFileContents(const char* filename_msg,
|
||||
uint8_t* file_data,
|
||||
size_t file_size);
|
||||
|
||||
// Flush any pending iovecs to the output file.
|
||||
void Flush() {
|
||||
IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
|
||||
iov_index_ = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
void AddItem(const void* base, size_t size);
|
||||
// Minor performance trade-off for easier-to-maintain code.
|
||||
void AddString(const char* str) {
|
||||
AddItem(str, my_strlen(str));
|
||||
}
|
||||
void AddItemWithoutTrailingSpaces(const void* base, size_t size);
|
||||
|
||||
struct kernel_iovec iov_[kIovCapacity];
|
||||
int iov_index_;
|
||||
|
||||
// Output file descriptor.
|
||||
int fd_;
|
||||
|
||||
const char* const mime_boundary_;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MimeWriter);
|
||||
};
|
||||
|
||||
MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
|
||||
: iov_index_(0),
|
||||
fd_(fd),
|
||||
mime_boundary_(mime_boundary) {
|
||||
}
|
||||
|
||||
MimeWriter::~MimeWriter() {
|
||||
}
|
||||
|
||||
void MimeWriter::AddBoundary() {
|
||||
AddString(mime_boundary_);
|
||||
AddString(g_rn);
|
||||
}
|
||||
|
||||
void MimeWriter::AddEnd() {
|
||||
AddString(mime_boundary_);
|
||||
AddString(g_dashdash_msg);
|
||||
AddString(g_rn);
|
||||
}
|
||||
|
||||
void MimeWriter::AddPairData(const char* msg_type,
|
||||
size_t msg_type_size,
|
||||
const char* msg_data,
|
||||
size_t msg_data_size) {
|
||||
AddString(g_form_data_msg);
|
||||
AddItem(msg_type, msg_type_size);
|
||||
AddString(g_quote_msg);
|
||||
AddString(g_rn);
|
||||
AddString(g_rn);
|
||||
AddItem(msg_data, msg_data_size);
|
||||
AddString(g_rn);
|
||||
}
|
||||
|
||||
void MimeWriter::AddPairDataInChunks(const char* msg_type,
|
||||
size_t msg_type_size,
|
||||
const char* msg_data,
|
||||
size_t msg_data_size,
|
||||
size_t chunk_size,
|
||||
bool strip_trailing_spaces) {
|
||||
if (chunk_size > kMaxCrashChunkSize)
|
||||
return;
|
||||
|
||||
unsigned i = 0;
|
||||
size_t done = 0, msg_length = msg_data_size;
|
||||
|
||||
while (msg_length) {
|
||||
char num[kUint64StringSize];
|
||||
const unsigned num_len = my_uint_len(++i);
|
||||
my_uitos(num, i, num_len);
|
||||
|
||||
size_t chunk_len = std::min(chunk_size, msg_length);
|
||||
|
||||
AddString(g_form_data_msg);
|
||||
AddItem(msg_type, msg_type_size);
|
||||
AddItem(num, num_len);
|
||||
AddString(g_quote_msg);
|
||||
AddString(g_rn);
|
||||
AddString(g_rn);
|
||||
if (strip_trailing_spaces) {
|
||||
AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
|
||||
} else {
|
||||
AddItem(msg_data + done, chunk_len);
|
||||
}
|
||||
AddString(g_rn);
|
||||
AddBoundary();
|
||||
Flush();
|
||||
|
||||
done += chunk_len;
|
||||
msg_length -= chunk_len;
|
||||
}
|
||||
}
|
||||
|
||||
void MimeWriter::AddFileContents(const char* filename_msg, uint8_t* file_data,
|
||||
size_t file_size) {
|
||||
AddString(g_form_data_msg);
|
||||
AddString(filename_msg);
|
||||
AddString(g_rn);
|
||||
AddString(g_content_type_msg);
|
||||
AddString(g_rn);
|
||||
AddString(g_rn);
|
||||
AddItem(file_data, file_size);
|
||||
AddString(g_rn);
|
||||
}
|
||||
|
||||
void MimeWriter::AddItem(const void* base, size_t size) {
|
||||
// Check if the iovec is full and needs to be flushed to output file.
|
||||
if (iov_index_ == kIovCapacity) {
|
||||
Flush();
|
||||
}
|
||||
iov_[iov_index_].iov_base = const_cast<void*>(base);
|
||||
iov_[iov_index_].iov_len = size;
|
||||
++iov_index_;
|
||||
}
|
||||
|
||||
void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
|
||||
AddItem(base, LengthWithoutTrailingSpaces(static_cast<const char*>(base),
|
||||
size));
|
||||
}
|
||||
|
||||
void LoadDataFromFD(google_breakpad::PageAllocator* allocator,
|
||||
int fd, bool close_fd, uint8_t** file_data, size_t* size) {
|
||||
struct kernel_stat st;
|
||||
if (sys_fstat(fd, &st) != 0) {
|
||||
static const char msg[] = "Cannot upload crash dump: stat failed\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
if (close_fd)
|
||||
IGNORE_RET(sys_close(fd));
|
||||
return;
|
||||
}
|
||||
|
||||
*file_data = reinterpret_cast<uint8_t*>(allocator->Alloc(st.st_size));
|
||||
if (!(*file_data)) {
|
||||
static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
if (close_fd)
|
||||
IGNORE_RET(sys_close(fd));
|
||||
return;
|
||||
}
|
||||
my_memset(*file_data, 0xf, st.st_size);
|
||||
|
||||
*size = st.st_size;
|
||||
int byte_read = sys_read(fd, *file_data, *size);
|
||||
if (byte_read == -1) {
|
||||
static const char msg[] = "Cannot upload crash dump: read failed\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
if (close_fd)
|
||||
IGNORE_RET(sys_close(fd));
|
||||
return;
|
||||
}
|
||||
|
||||
if (close_fd)
|
||||
IGNORE_RET(sys_close(fd));
|
||||
}
|
||||
|
||||
void LoadDataFromFile(google_breakpad::PageAllocator* allocator,
|
||||
const char* filename,
|
||||
int* fd, uint8_t** file_data, size_t* size) {
|
||||
// WARNING: this code runs in a compromised context. It may not call into
|
||||
// libc nor allocate memory normally.
|
||||
*fd = sys_open(filename, O_RDONLY, 0);
|
||||
*size = 0;
|
||||
|
||||
if (*fd < 0) {
|
||||
static const char msg[] = "Cannot upload crash dump: failed to open\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
LoadDataFromFD(allocator, *fd, true, file_data, size);
|
||||
}
|
||||
|
||||
// Spawn the appropriate upload process for the current OS:
|
||||
// - generic Linux invokes wget.
|
||||
// - ChromeOS invokes crash_reporter.
|
||||
// |dumpfile| is the path to the dump data file.
|
||||
// |mime_boundary| is only used on Linux.
|
||||
// |exe_buf| is only used on CrOS and is the crashing process' name.
|
||||
void ExecUploadProcessOrTerminate(const BreakpadInfo& info,
|
||||
const char* dumpfile,
|
||||
const char* mime_boundary,
|
||||
const char* exe_buf,
|
||||
google_breakpad::PageAllocator* allocator) {
|
||||
// The --header argument to wget looks like:
|
||||
// --header=Content-Type: multipart/form-data; boundary=XYZ
|
||||
// where the boundary has two fewer leading '-' chars
|
||||
static const char header_msg[] =
|
||||
"--header=Content-Type: multipart/form-data; boundary=";
|
||||
char* const header = reinterpret_cast<char*>(allocator->Alloc(
|
||||
sizeof(header_msg) - 1 + strlen(mime_boundary) - 2 + 1));
|
||||
memcpy(header, header_msg, sizeof(header_msg) - 1);
|
||||
memcpy(header + sizeof(header_msg) - 1, mime_boundary + 2,
|
||||
strlen(mime_boundary) - 2);
|
||||
// We grab the NUL byte from the end of |mime_boundary|.
|
||||
|
||||
// The --post-file argument to wget looks like:
|
||||
// --post-file=/tmp/...
|
||||
static const char post_file_msg[] = "--post-file=";
|
||||
char* const post_file = reinterpret_cast<char*>(allocator->Alloc(
|
||||
sizeof(post_file_msg) - 1 + strlen(dumpfile) + 1));
|
||||
memcpy(post_file, post_file_msg, sizeof(post_file_msg) - 1);
|
||||
memcpy(post_file + sizeof(post_file_msg) - 1, dumpfile, strlen(dumpfile));
|
||||
|
||||
static const char kWgetBinary[] = "/usr/bin/wget";
|
||||
const char* args[] = {
|
||||
kWgetBinary,
|
||||
header,
|
||||
post_file,
|
||||
// TODO(zcbenz): Enabling custom upload url.
|
||||
info.upload_url,
|
||||
"--timeout=10", // Set a timeout so we don't hang forever.
|
||||
"--tries=1", // Don't retry if the upload fails.
|
||||
"-O", // output reply to fd 3
|
||||
"/dev/fd/3",
|
||||
NULL,
|
||||
};
|
||||
static const char msg[] = "Cannot upload crash dump: cannot exec "
|
||||
"/usr/bin/wget\n";
|
||||
execve(args[0], const_cast<char**>(args), environ);
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
sys__exit(1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void HandleCrashDump(const BreakpadInfo& info) {
|
||||
int dumpfd;
|
||||
bool keep_fd = false;
|
||||
size_t dump_size;
|
||||
uint8_t* dump_data;
|
||||
google_breakpad::PageAllocator allocator;
|
||||
const char* exe_buf = NULL;
|
||||
|
||||
if (info.fd != -1) {
|
||||
// Dump is provided with an open FD.
|
||||
keep_fd = true;
|
||||
dumpfd = info.fd;
|
||||
|
||||
// The FD is pointing to the end of the file.
|
||||
// Rewind, we'll read the data next.
|
||||
if (lseek(dumpfd, 0, SEEK_SET) == -1) {
|
||||
static const char msg[] = "Cannot upload crash dump: failed to "
|
||||
"reposition minidump FD\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
IGNORE_RET(sys_close(dumpfd));
|
||||
return;
|
||||
}
|
||||
LoadDataFromFD(&allocator, info.fd, false, &dump_data, &dump_size);
|
||||
} else {
|
||||
// Dump is provided with a path.
|
||||
keep_fd = false;
|
||||
LoadDataFromFile(
|
||||
&allocator, info.filename, &dumpfd, &dump_data, &dump_size);
|
||||
}
|
||||
|
||||
// We need to build a MIME block for uploading to the server. Since we are
|
||||
// going to fork and run wget, it needs to be written to a temp file.
|
||||
const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
|
||||
if (ufd < 0) {
|
||||
static const char msg[] = "Cannot upload crash dump because /dev/urandom"
|
||||
" is missing\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
static const char temp_file_template[] =
|
||||
"/tmp/chromium-upload-XXXXXXXXXXXXXXXX";
|
||||
char temp_file[sizeof(temp_file_template)];
|
||||
int temp_file_fd = -1;
|
||||
if (keep_fd) {
|
||||
temp_file_fd = dumpfd;
|
||||
// Rewind the destination, we are going to overwrite it.
|
||||
if (lseek(dumpfd, 0, SEEK_SET) == -1) {
|
||||
static const char msg[] = "Cannot upload crash dump: failed to "
|
||||
"reposition minidump FD (2)\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
IGNORE_RET(sys_close(dumpfd));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (info.upload) {
|
||||
memcpy(temp_file, temp_file_template, sizeof(temp_file_template));
|
||||
|
||||
for (unsigned i = 0; i < 10; ++i) {
|
||||
uint64_t t;
|
||||
sys_read(ufd, &t, sizeof(t));
|
||||
write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t);
|
||||
|
||||
temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (temp_file_fd >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (temp_file_fd < 0) {
|
||||
static const char msg[] = "Failed to create temporary file in /tmp: "
|
||||
"cannot upload crash dump\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
IGNORE_RET(sys_close(ufd));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
|
||||
if (temp_file_fd < 0) {
|
||||
static const char msg[] = "Failed to save crash dump: failed to open\n";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
IGNORE_RET(sys_close(ufd));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
|
||||
char mime_boundary[28 + 16 + 1];
|
||||
my_memset(mime_boundary, '-', 28);
|
||||
uint64_t boundary_rand;
|
||||
sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
|
||||
write_uint64_hex(mime_boundary + 28, boundary_rand);
|
||||
mime_boundary[28 + 16] = 0;
|
||||
IGNORE_RET(sys_close(ufd));
|
||||
|
||||
// The MIME block looks like this:
|
||||
// BOUNDARY \r\n
|
||||
// Content-Disposition: form-data; name="prod" \r\n \r\n
|
||||
// Chrome_Linux \r\n
|
||||
// BOUNDARY \r\n
|
||||
// Content-Disposition: form-data; name="ver" \r\n \r\n
|
||||
// 1.2.3.4 \r\n
|
||||
// BOUNDARY \r\n
|
||||
//
|
||||
// zero or one:
|
||||
// Content-Disposition: form-data; name="ptime" \r\n \r\n
|
||||
// abcdef \r\n
|
||||
// BOUNDARY \r\n
|
||||
//
|
||||
// zero or one:
|
||||
// Content-Disposition: form-data; name="ptype" \r\n \r\n
|
||||
// abcdef \r\n
|
||||
// BOUNDARY \r\n
|
||||
//
|
||||
// zero or one:
|
||||
// Content-Disposition: form-data; name="lsb-release" \r\n \r\n
|
||||
// abcdef \r\n
|
||||
// BOUNDARY \r\n
|
||||
//
|
||||
// zero or one:
|
||||
// Content-Disposition: form-data; name="oom-size" \r\n \r\n
|
||||
// 1234567890 \r\n
|
||||
// BOUNDARY \r\n
|
||||
//
|
||||
// zero or more (up to CrashKeyStorage::num_entries = 64):
|
||||
// Content-Disposition: form-data; name=crash-key-name \r\n
|
||||
// crash-key-value \r\n
|
||||
// BOUNDARY \r\n
|
||||
//
|
||||
// Content-Disposition: form-data; name="dump"; filename="dump" \r\n
|
||||
// Content-Type: application/octet-stream \r\n \r\n
|
||||
// <dump contents>
|
||||
// \r\n BOUNDARY -- \r\n
|
||||
|
||||
MimeWriter writer(temp_file_fd, mime_boundary);
|
||||
{
|
||||
writer.AddBoundary();
|
||||
if (info.pid > 0) {
|
||||
char pid_value_buf[kUint64StringSize];
|
||||
uint64_t pid_value_len = my_uint64_len(info.pid);
|
||||
my_uint64tos(pid_value_buf, info.pid, pid_value_len);
|
||||
static const char pid_key_name[] = "pid";
|
||||
writer.AddPairData(pid_key_name, sizeof(pid_key_name) - 1,
|
||||
pid_value_buf, pid_value_len);
|
||||
writer.AddBoundary();
|
||||
}
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
if (info.process_start_time > 0) {
|
||||
struct kernel_timeval tv;
|
||||
if (!sys_gettimeofday(&tv, NULL)) {
|
||||
uint64_t time = kernel_timeval_to_ms(&tv);
|
||||
if (time > info.process_start_time) {
|
||||
time -= info.process_start_time;
|
||||
char time_str[kUint64StringSize];
|
||||
const unsigned time_len = my_uint64_len(time);
|
||||
my_uint64tos(time_str, time, time_len);
|
||||
|
||||
static const char process_time_msg[] = "ptime";
|
||||
writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
|
||||
time_str, time_len);
|
||||
writer.AddBoundary();
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.distro_length) {
|
||||
static const char distro_msg[] = "lsb-release";
|
||||
writer.AddPairString(distro_msg, info.distro);
|
||||
writer.AddBoundary();
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
if (info.oom_size) {
|
||||
char oom_size_str[kUint64StringSize];
|
||||
const unsigned oom_size_len = my_uint64_len(info.oom_size);
|
||||
my_uint64tos(oom_size_str, info.oom_size, oom_size_len);
|
||||
static const char oom_size_msg[] = "oom-size";
|
||||
writer.AddPairData(oom_size_msg, sizeof(oom_size_msg) - 1,
|
||||
oom_size_str, oom_size_len);
|
||||
writer.AddBoundary();
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
if (info.crash_keys) {
|
||||
CrashKeyStorage::Iterator crash_key_iterator(*info.crash_keys);
|
||||
const CrashKeyStorage::Entry* entry;
|
||||
while ((entry = crash_key_iterator.Next())) {
|
||||
writer.AddPairString(entry->key, entry->value);
|
||||
writer.AddBoundary();
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
writer.AddFileContents(g_dump_msg, dump_data, dump_size);
|
||||
writer.AddEnd();
|
||||
writer.Flush();
|
||||
|
||||
IGNORE_RET(sys_close(temp_file_fd));
|
||||
|
||||
if (!info.upload)
|
||||
return;
|
||||
|
||||
const pid_t child = sys_fork();
|
||||
if (!child) {
|
||||
// Spawned helper process.
|
||||
//
|
||||
// This code is called both when a browser is crashing (in which case,
|
||||
// nothing really matters any more) and when a renderer/plugin crashes, in
|
||||
// which case we need to continue.
|
||||
//
|
||||
// Since we are a multithreaded app, if we were just to fork(), we might
|
||||
// grab file descriptors which have just been created in another thread and
|
||||
// hold them open for too long.
|
||||
//
|
||||
// Thus, we have to loop and try and close everything.
|
||||
const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
for (unsigned i = 3; i < 8192; ++i)
|
||||
IGNORE_RET(sys_close(i));
|
||||
} else {
|
||||
google_breakpad::DirectoryReader reader(fd);
|
||||
const char* name;
|
||||
while (reader.GetNextEntry(&name)) {
|
||||
int i;
|
||||
if (my_strtoui(&i, name) && i > 2 && i != fd)
|
||||
IGNORE_RET(sys_close(i));
|
||||
reader.PopEntry();
|
||||
}
|
||||
|
||||
IGNORE_RET(sys_close(fd));
|
||||
}
|
||||
|
||||
IGNORE_RET(sys_setsid());
|
||||
|
||||
// Leave one end of a pipe in the upload process and watch for it getting
|
||||
// closed by the upload process exiting.
|
||||
int fds[2];
|
||||
if (sys_pipe(fds) >= 0) {
|
||||
const pid_t upload_child = sys_fork();
|
||||
if (!upload_child) {
|
||||
// Upload process.
|
||||
IGNORE_RET(sys_close(fds[0]));
|
||||
IGNORE_RET(sys_dup2(fds[1], 3));
|
||||
ExecUploadProcessOrTerminate(info, temp_file, mime_boundary, exe_buf,
|
||||
&allocator);
|
||||
}
|
||||
|
||||
// Helper process.
|
||||
if (upload_child > 0) {
|
||||
IGNORE_RET(sys_close(fds[1]));
|
||||
char id_buf[17]; // Crash report IDs are expected to be 16 chars.
|
||||
ssize_t len = -1;
|
||||
// Upload should finish in about 10 seconds. Add a few more 500 ms
|
||||
// internals to account for process startup time.
|
||||
for (size_t wait_count = 0; wait_count < 24; ++wait_count) {
|
||||
struct kernel_pollfd poll_fd;
|
||||
poll_fd.fd = fds[0];
|
||||
poll_fd.events = POLLIN | POLLPRI | POLLERR;
|
||||
int ret = sys_poll(&poll_fd, 1, 500);
|
||||
if (ret < 0) {
|
||||
// Error
|
||||
break;
|
||||
} else if (ret > 0) {
|
||||
// There is data to read.
|
||||
len = HANDLE_EINTR(sys_read(fds[0], id_buf, sizeof(id_buf) - 1));
|
||||
break;
|
||||
}
|
||||
// ret == 0 -> timed out, continue waiting.
|
||||
}
|
||||
if (len > 0) {
|
||||
// Write crash dump id to stderr.
|
||||
id_buf[len] = 0;
|
||||
static const char msg[] = "\nCrash dump id: ";
|
||||
WriteLog(msg, sizeof(msg) - 1);
|
||||
WriteLog(id_buf, my_strlen(id_buf));
|
||||
WriteLog("\n", 1);
|
||||
}
|
||||
if (sys_waitpid(upload_child, NULL, WNOHANG) == 0) {
|
||||
// Upload process is still around, kill it.
|
||||
sys_kill(upload_child, SIGKILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper process.
|
||||
IGNORE_RET(sys_unlink(info.filename));
|
||||
IGNORE_RET(sys_unlink(temp_file));
|
||||
sys__exit(0);
|
||||
}
|
||||
|
||||
// Main browser process.
|
||||
if (child <= 0)
|
||||
return;
|
||||
(void) HANDLE_EINTR(sys_waitpid(child, NULL, 0));
|
||||
}
|
||||
|
||||
size_t WriteLog(const char* buf, size_t nbytes) {
|
||||
return sys_write(2, buf, nbytes);
|
||||
}
|
||||
|
||||
} // namespace crash_reporter
|
||||
38
common/crash_reporter/linux/crash_dump_handler.h
Normal file
38
common/crash_reporter/linux/crash_dump_handler.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
|
||||
// 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 ATOM_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
|
||||
#define ATOM_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "vendor/breakpad/src/common/simple_string_dictionary.h"
|
||||
|
||||
namespace crash_reporter {
|
||||
|
||||
typedef google_breakpad::NonAllocatingMap<256, 256, 64> CrashKeyStorage;
|
||||
|
||||
// BreakpadInfo describes a crash report.
|
||||
// The minidump information can either be contained in a file descriptor (fd) or
|
||||
// in a file (whose path is in filename).
|
||||
struct BreakpadInfo {
|
||||
int fd; // File descriptor to the Breakpad dump data.
|
||||
const char* filename; // Path to the Breakpad dump data.
|
||||
const char* distro; // Linux distro string.
|
||||
unsigned distro_length; // Length of |distro|.
|
||||
bool upload; // Whether to upload or save crash dump.
|
||||
uint64_t process_start_time; // Uptime of the crashing process.
|
||||
size_t oom_size; // Amount of memory requested if OOM.
|
||||
uint64_t pid; // PID where applicable.
|
||||
const char* upload_url; // URL to upload the minidump.
|
||||
CrashKeyStorage* crash_keys;
|
||||
};
|
||||
|
||||
void HandleCrashDump(const BreakpadInfo& info);
|
||||
|
||||
size_t WriteLog(const char* buf, size_t nbytes);
|
||||
|
||||
} // namespace crash_reporter
|
||||
|
||||
#endif // ATOM_COMMON_CRASH_REPORTER_LINUX_CRASH_DUMP_HANDLER_H_
|
||||
21
common/lib/init.coffee
Normal file
21
common/lib/init.coffee
Normal file
@@ -0,0 +1,21 @@
|
||||
path = require 'path'
|
||||
timers = require 'timers'
|
||||
Module = require 'module'
|
||||
|
||||
# Add common/api/lib to module search paths.
|
||||
globalPaths = Module.globalPaths
|
||||
globalPaths.push path.join(process.resourcesPath, 'common', 'api', 'lib')
|
||||
|
||||
# setImmediate and process.nextTick makes use of uv_check and uv_prepare to
|
||||
# run the callbacks, however since we only run uv loop on requests, the
|
||||
# callbacks wouldn't be called until something else activated the uv loop,
|
||||
# which would delay the callbacks for arbitrary long time. So we should
|
||||
# initiatively activate the uv loop once setImmediate and process.nextTick is
|
||||
# called.
|
||||
wrapWithActivateUvLoop = (func) ->
|
||||
->
|
||||
process.activateUvLoop()
|
||||
func.apply this, arguments
|
||||
process.nextTick = wrapWithActivateUvLoop process.nextTick
|
||||
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
|
||||
global.clearImmediate = timers.clearImmediate
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user