mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2c28725722 | ||
|
|
53a4f34433 | ||
|
|
406f0b7bc7 | ||
|
|
6912a0513a | ||
|
|
0398577e93 | ||
|
|
42dc9c1ec6 | ||
|
|
2f798c5116 | ||
|
|
a4253e3899 | ||
|
|
52b5f769f0 | ||
|
|
b73a114f8f | ||
|
|
7ca152070a | ||
|
|
f24ccd3841 | ||
|
|
66ac11ca5f | ||
|
|
7afef0fcdb | ||
|
|
627f487b36 | ||
|
|
c64a793364 | ||
|
|
63852a8c82 | ||
|
|
c97afdbdb3 | ||
|
|
6c5ea4ea32 |
@@ -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);
|
||||
}
|
||||
|
||||
@@ -54,8 +60,8 @@ void AtomMainDelegate::PreSandboxStartup() {
|
||||
std::string process_type = command_line->GetSwitchValueASCII(
|
||||
switches::kProcessType);
|
||||
|
||||
// Don't append arguments for renderer process.
|
||||
if (process_type == switches::kRendererProcess)
|
||||
// Only append arguments for browser process.
|
||||
if (!process_type.empty())
|
||||
return;
|
||||
|
||||
// Add a flag to mark the start of switches added by atom-shell.
|
||||
|
||||
80
atom.gyp
80
atom.gyp
@@ -51,6 +51,8 @@
|
||||
'browser/api/atom_api_event.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',
|
||||
@@ -66,6 +68,7 @@
|
||||
'browser/auto_updater.cc',
|
||||
'browser/auto_updater.h',
|
||||
'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 +86,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,6 +111,7 @@
|
||||
'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/cocoa/atom_menu_controller.h',
|
||||
@@ -112,9 +121,17 @@
|
||||
'browser/ui/cocoa/nsalert_synchronous_sheet.h',
|
||||
'browser/ui/cocoa/nsalert_synchronous_sheet.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/win/menu_2.cc',
|
||||
@@ -147,18 +164,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',
|
||||
@@ -166,6 +190,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',
|
||||
@@ -209,9 +234,8 @@
|
||||
],
|
||||
'configurations': {
|
||||
'Debug': {
|
||||
'defines': [
|
||||
'DEBUG',
|
||||
],
|
||||
'defines': [ 'DEBUG' ],
|
||||
'cflags': [ '-g', '-O0' ],
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -306,6 +330,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)
|
||||
{
|
||||
@@ -321,6 +363,7 @@
|
||||
'include_dirs': [
|
||||
'.',
|
||||
'vendor',
|
||||
'vendor/brightray',
|
||||
# Include directories for uv and node.
|
||||
'vendor/node/src',
|
||||
'vendor/node/deps/http_parser',
|
||||
@@ -353,12 +396,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
|
||||
{
|
||||
@@ -385,8 +450,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',
|
||||
],
|
||||
@@ -396,7 +460,7 @@
|
||||
'<(RULE_INPUT_PATH)',
|
||||
'<(PRODUCT_DIR)/resources/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).js',
|
||||
],
|
||||
}], # OS=="win"
|
||||
}], # OS=="win" or OS=="linux"
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -80,7 +80,9 @@ void AutoUpdater::CheckForUpdates(
|
||||
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
|
||||
|
||||
@@ -340,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
|
||||
|
||||
|
||||
@@ -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_
|
||||
@@ -368,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);
|
||||
|
||||
@@ -414,6 +414,12 @@ 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;
|
||||
self->window_->DebugDevTools();
|
||||
}
|
||||
|
||||
// static
|
||||
void Window::FocusOnWebView(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
UNWRAP_WINDOW_AND_CHECK;
|
||||
@@ -663,6 +669,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,4 +8,18 @@ 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()
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
63
browser/devtools_delegate.cc
Normal file
63
browser/devtools_delegate.cc
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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/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"
|
||||
|
||||
namespace atom {
|
||||
|
||||
DevToolsDelegate::DevToolsDelegate(NativeWindow* window,
|
||||
content::WebContents* target_web_contents)
|
||||
: content::WebContentsObserver(window->GetWebContents()),
|
||||
owner_window_(window) {
|
||||
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);
|
||||
web_contents->GetController().LoadURL(
|
||||
GURL("chrome-devtools://devtools/devtools.html"),
|
||||
content::Referrer(),
|
||||
content::PAGE_TRANSITION_AUTO_TOPLEVEL,
|
||||
std::string());
|
||||
}
|
||||
|
||||
DevToolsDelegate::~DevToolsDelegate() {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::DispatchOnEmbedder(const std::string& message) {
|
||||
}
|
||||
|
||||
void DevToolsDelegate::InspectedContentsClosing() {
|
||||
delete owner_window_;
|
||||
}
|
||||
|
||||
void DevToolsDelegate::AboutToNavigateRenderView(
|
||||
content::RenderViewHost* render_view_host) {
|
||||
content::DevToolsClientHost::SetupDevToolsFrontendClient(
|
||||
owner_window_->GetWebContents()->GetRenderViewHost());
|
||||
}
|
||||
|
||||
void DevToolsDelegate::OnWindowClosed() {
|
||||
delete owner_window_;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
53
browser/devtools_delegate.h
Normal file
53
browser/devtools_delegate.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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"
|
||||
|
||||
namespace content {
|
||||
class DevToolsAgentHost;
|
||||
class DevToolsClientHost;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindow;
|
||||
|
||||
class DevToolsDelegate : public content::DevToolsFrontendHostDelegate,
|
||||
public content::WebContentsObserver,
|
||||
public NativeWindowObserver {
|
||||
public:
|
||||
DevToolsDelegate(NativeWindow* window,
|
||||
content::WebContents* target_web_contents);
|
||||
virtual ~DevToolsDelegate();
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
NativeWindow* owner_window_;
|
||||
|
||||
scoped_refptr<content::DevToolsAgentHost> devtools_agent_host_;
|
||||
scoped_ptr<content::DevToolsClientHost> devtools_client_host_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DevToolsDelegate);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_DEVTOOLS_DELEGATE_H_
|
||||
@@ -18,6 +18,7 @@
|
||||
#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"
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "ui/gfx/point.h"
|
||||
#include "ui/gfx/rect.h"
|
||||
#include "ui/gfx/size.h"
|
||||
#include "vendor/brightray/browser/inspectable_web_contents_impl.h"
|
||||
#include "webkit/common/user_agent/user_agent_util.h"
|
||||
|
||||
using content::NavigationEntry;
|
||||
@@ -183,6 +185,16 @@ void NativeWindow::InspectElement(int x, int y) {
|
||||
agent->InspectElement(x, y);
|
||||
}
|
||||
|
||||
void NativeWindow::DebugDevTools() {
|
||||
if (!IsDevToolsOpened())
|
||||
return;
|
||||
|
||||
base::DictionaryValue options;
|
||||
NativeWindow* window = NativeWindow::Create(&options);
|
||||
window->devtools_delegate_.reset(new DevToolsDelegate(
|
||||
window, GetDevToolsWebContents()));
|
||||
}
|
||||
|
||||
void NativeWindow::FocusOnWebView() {
|
||||
GetWebContents()->GetRenderViewHost()->Focus();
|
||||
}
|
||||
@@ -248,7 +260,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(),
|
||||
@@ -269,6 +282,13 @@ content::WebContents* NativeWindow::GetWebContents() const {
|
||||
return inspectable_web_contents_->GetWebContents();
|
||||
}
|
||||
|
||||
content::WebContents* NativeWindow::GetDevToolsWebContents() const {
|
||||
brightray::InspectableWebContentsImpl* inspectable_web_contents_impl =
|
||||
static_cast<brightray::InspectableWebContentsImpl*>(
|
||||
inspectable_web_contents());
|
||||
return inspectable_web_contents_impl->devtools_web_contents();
|
||||
}
|
||||
|
||||
void NativeWindow::NotifyWindowClosed() {
|
||||
if (is_closed_)
|
||||
return;
|
||||
|
||||
@@ -45,6 +45,7 @@ class Message;
|
||||
namespace atom {
|
||||
|
||||
class AtomJavaScriptDialogManager;
|
||||
class DevToolsDelegate;
|
||||
struct DraggableRegion;
|
||||
|
||||
class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
@@ -56,7 +57,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
|
||||
class DialogScope {
|
||||
public:
|
||||
DialogScope(NativeWindow* window)
|
||||
explicit DialogScope(NativeWindow* window)
|
||||
: window_(window) {
|
||||
if (window_ != NULL)
|
||||
window_->set_has_dialog_attached(true);
|
||||
@@ -128,6 +129,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
virtual bool IsDevToolsOpened();
|
||||
virtual void InspectElement(int x, int y);
|
||||
|
||||
// Creates a new window to debug the devtools.
|
||||
virtual void DebugDevTools();
|
||||
|
||||
virtual void FocusOnWebView();
|
||||
virtual void BlurWebView();
|
||||
virtual bool IsWebViewFocused();
|
||||
@@ -149,6 +153,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
virtual void CloseWebContents();
|
||||
|
||||
content::WebContents* GetWebContents() const;
|
||||
content::WebContents* GetDevToolsWebContents() const;
|
||||
|
||||
void AddObserver(NativeWindowObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
@@ -210,7 +215,7 @@ 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;
|
||||
@@ -255,6 +260,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
|
||||
|
||||
base::WeakPtrFactory<NativeWindow> weak_factory_;
|
||||
|
||||
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_
|
||||
@@ -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"
|
||||
|
||||
@@ -66,6 +66,7 @@ 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();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#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"
|
||||
|
||||
@@ -59,6 +60,8 @@ class AtomURLRequestContextGetter : public net::URLRequestContextGetter {
|
||||
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_;
|
||||
scoped_ptr<net::URLRequestContextStorage> storage_;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.9.2</string>
|
||||
<string>0.10.2</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,9,2,0
|
||||
PRODUCTVERSION 0,9,2,0
|
||||
FILEVERSION 0,10,2,0
|
||||
PRODUCTVERSION 0,10,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Atom-Shell"
|
||||
VALUE "FileVersion", "0.9.2"
|
||||
VALUE "FileVersion", "0.10.2"
|
||||
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.9.2"
|
||||
VALUE "ProductVersion", "0.10.2"
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -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(); }
|
||||
|
||||
|
||||
46
common.gypi
46
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,6 +31,8 @@
|
||||
'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': {
|
||||
@@ -76,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': [
|
||||
@@ -93,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.
|
||||
@@ -153,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',
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
|
||||
#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) \
|
||||
@@ -67,6 +72,10 @@ void Screen::GetPrimaryDisplay(
|
||||
|
||||
// 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"));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define ATOM_VERSION_H
|
||||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 9
|
||||
#define ATOM_MINOR_VERSION 10
|
||||
#define ATOM_PATCH_VERSION 2
|
||||
|
||||
#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_
|
||||
19
common/linux/application_info.cc
Normal file
19
common/linux/application_info.cc
Normal file
@@ -0,0 +1,19 @@
|
||||
// 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 <string>
|
||||
|
||||
#include "common/atom_version.h"
|
||||
|
||||
namespace brightray {
|
||||
|
||||
std::string GetApplicationName() {
|
||||
return "Atom-Shell";
|
||||
}
|
||||
|
||||
std::string GetApplicationVersion() {
|
||||
return ATOM_VERSION_STRING;
|
||||
}
|
||||
|
||||
} // namespace brightray
|
||||
57
common/node_bindings_linux.cc
Normal file
57
common/node_bindings_linux.cc
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.
|
||||
|
||||
#include "common/node_bindings_linux.h"
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
namespace atom {
|
||||
|
||||
NodeBindingsLinux::NodeBindingsLinux(bool is_browser)
|
||||
: NodeBindings(is_browser),
|
||||
epoll_(epoll_create(1)) {
|
||||
int backend_fd = uv_backend_fd(uv_loop_);
|
||||
struct epoll_event ev = { 0 };
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = backend_fd;
|
||||
epoll_ctl(epoll_, EPOLL_CTL_ADD, backend_fd, &ev);
|
||||
}
|
||||
|
||||
NodeBindingsLinux::~NodeBindingsLinux() {
|
||||
}
|
||||
|
||||
void NodeBindingsLinux::RunMessageLoop() {
|
||||
// Get notified when libuv's watcher queue changes.
|
||||
uv_loop_->data = this;
|
||||
uv_loop_->on_watcher_queue_updated = OnWatcherQueueChanged;
|
||||
|
||||
NodeBindings::RunMessageLoop();
|
||||
}
|
||||
|
||||
// static
|
||||
void NodeBindingsLinux::OnWatcherQueueChanged(uv_loop_t* loop) {
|
||||
NodeBindingsLinux* self = static_cast<NodeBindingsLinux*>(loop->data);
|
||||
|
||||
// We need to break the io polling in the epoll thread when loop's watcher
|
||||
// queue changes, otherwise new events cannot be notified.
|
||||
self->WakeupEmbedThread();
|
||||
}
|
||||
|
||||
void NodeBindingsLinux::PollEvents() {
|
||||
int timeout = uv_backend_timeout(uv_loop_);
|
||||
|
||||
// Wait for new libuv events.
|
||||
int r;
|
||||
do {
|
||||
struct epoll_event ev;
|
||||
r = epoll_wait(epoll_, &ev, 1, timeout);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
// static
|
||||
NodeBindings* NodeBindings::Create(bool is_browser) {
|
||||
return new NodeBindingsLinux(is_browser);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
34
common/node_bindings_linux.h
Normal file
34
common/node_bindings_linux.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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_NODE_BINDINGS_LINUX_H_
|
||||
#define ATOM_COMMON_NODE_BINDINGS_LINUX_H_
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "common/node_bindings.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NodeBindingsLinux : public NodeBindings {
|
||||
public:
|
||||
explicit NodeBindingsLinux(bool is_browser);
|
||||
virtual ~NodeBindingsLinux();
|
||||
|
||||
virtual void RunMessageLoop() OVERRIDE;
|
||||
|
||||
private:
|
||||
// Called when uv's watcher queue changes.
|
||||
static void OnWatcherQueueChanged(uv_loop_t* loop);
|
||||
|
||||
virtual void PollEvents() OVERRIDE;
|
||||
|
||||
// Epoll to poll for uv's backend fd.
|
||||
int epoll_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(NodeBindingsLinux);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_COMMON_NODE_BINDINGS_LINUX_H_
|
||||
80
common/platform_util_linux.cc
Normal file
80
common/platform_util_linux.cc
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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/platform_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "base/process/kill.h"
|
||||
#include "base/process/launch.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void XDGUtil(const std::string& util, const std::string& arg) {
|
||||
std::vector<std::string> argv;
|
||||
argv.push_back(util);
|
||||
argv.push_back(arg);
|
||||
|
||||
base::LaunchOptions options;
|
||||
// xdg-open can fall back on mailcap which eventually might plumb through
|
||||
// to a command that needs a terminal. Set the environment variable telling
|
||||
// it that we definitely don't have a terminal available and that it should
|
||||
// bring up a new terminal if necessary. See "man mailcap".
|
||||
options.environ["MM_NOTTTY"] = "1";
|
||||
|
||||
base::ProcessHandle handle;
|
||||
if (base::LaunchProcess(argv, options, &handle))
|
||||
base::EnsureProcessGetsReaped(handle);
|
||||
}
|
||||
|
||||
void XDGOpen(const std::string& path) {
|
||||
XDGUtil("xdg-open", path);
|
||||
}
|
||||
|
||||
void XDGEmail(const std::string& email) {
|
||||
XDGUtil("xdg-email", email);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace platform_util {
|
||||
|
||||
// TODO(estade): It would be nice to be able to select the file in the file
|
||||
// manager, but that probably requires extending xdg-open. For now just
|
||||
// show the folder.
|
||||
void ShowItemInFolder(const base::FilePath& full_path) {
|
||||
base::FilePath dir = full_path.DirName();
|
||||
if (!base::DirectoryExists(dir))
|
||||
return;
|
||||
|
||||
XDGOpen(dir.value());
|
||||
}
|
||||
|
||||
void OpenItem(const base::FilePath& full_path) {
|
||||
XDGOpen(full_path.value());
|
||||
}
|
||||
|
||||
void OpenExternal(const GURL& url) {
|
||||
if (url.SchemeIs("mailto"))
|
||||
XDGEmail(url.spec());
|
||||
else
|
||||
XDGOpen(url.spec());
|
||||
}
|
||||
|
||||
void MoveItemToTrash(const base::FilePath& full_path) {
|
||||
XDGUtil("gvfs-trash", full_path.value());
|
||||
}
|
||||
|
||||
void Beep() {
|
||||
// echo '\a' > /dev/console
|
||||
FILE* console = fopen("/dev/console", "r");
|
||||
if (console == NULL)
|
||||
return;
|
||||
fprintf(console, "\a");
|
||||
fclose(console);
|
||||
}
|
||||
|
||||
} // namespace platform_util
|
||||
@@ -3,7 +3,7 @@
|
||||
## Guides
|
||||
|
||||
* [Quick start](quick-start.md)
|
||||
* [Build native modules](build-native-modules.md)
|
||||
* [Use native modules](use-native-modules.md)
|
||||
|
||||
## Development
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* [Source code directory structure](development/source-code-directory-structure.md)
|
||||
* [Build instructions (Mac)](development/build-instructions-mac.md)
|
||||
* [Build instructions (Windows)](development/build-instructions-windows.md)
|
||||
* [Build instructions (Linux)](development/build-instructions-linux.md)
|
||||
|
||||
## API References
|
||||
|
||||
|
||||
@@ -116,6 +116,22 @@ shouldn't!).
|
||||
Emitted when the memory taken by the native window is released. Usually you
|
||||
should dereference the javascript object when received this event.
|
||||
|
||||
### Event: 'unresponsive'
|
||||
|
||||
Emiited when the web page becomes unresponsive.
|
||||
|
||||
### Event: 'responsive'
|
||||
|
||||
Emitted when the unresponsive web page becomes responsive again.
|
||||
|
||||
### Event: 'crashed'
|
||||
|
||||
Emitted when the renderer process is crashed.
|
||||
|
||||
### Event: 'blur'
|
||||
|
||||
Emiited when window loses focus.
|
||||
|
||||
### Class Method: BrowserWindow.getAllWindows()
|
||||
|
||||
Returns an array of all opened browser windows.
|
||||
|
||||
71
docs/development/build-instructions-linux.md
Normal file
71
docs/development/build-instructions-linux.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Build instructions (Linux)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* [node.js](http://nodejs.org)
|
||||
* clang and headers of GTK+ and libnotify
|
||||
|
||||
On Ubuntu you could install the libraries via:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install clang libgtk2.0-dev libnotify-dev
|
||||
```
|
||||
|
||||
## Getting the code
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/atom/atom-shell.git
|
||||
```
|
||||
|
||||
## Bootstrapping
|
||||
|
||||
The bootstrap script will download all necessary build dependencies and create
|
||||
build project files. Notice that we're using `ninja` to build `atom-shell` so
|
||||
there is no `Makefile` generated.
|
||||
|
||||
```bash
|
||||
$ cd atom-shell
|
||||
$ ./script/bootstrap.py
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Build both `Release` and `Debug` targets:
|
||||
|
||||
```bash
|
||||
$ ./script/build.py
|
||||
```
|
||||
|
||||
You can also only build the `Debug` target:
|
||||
|
||||
```bash
|
||||
$ ./script/build.py -c Debug
|
||||
```
|
||||
|
||||
After building is done, you can find `Atom.app` under `out/Debug`.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you got an error like this:
|
||||
|
||||
````
|
||||
In file included from /usr/include/stdio.h:28:0,
|
||||
from ../../../svnsrc/libgcc/../gcc/tsystem.h:88,
|
||||
from ../../../svnsrc/libgcc/libgcc2.c:29:
|
||||
/usr/include/features.h:324:26: fatal error: bits/predefs.h: No such file or directory
|
||||
#include <bits/predefs.h>
|
||||
````
|
||||
|
||||
Then you need to install `gcc-multilib` and `g++-multilib`, on Ubuntu you can do
|
||||
this:
|
||||
|
||||
```bash
|
||||
$ sudo apt-get install gcc-multilib g++-multilib
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
$ ./script/test.py
|
||||
```
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
# Build native modules
|
||||
# Use native modules
|
||||
|
||||
Since atom-shell is using a different V8 version from the official node, you
|
||||
need to build native module against atom-shell's headers to use them.
|
||||
|
||||
You need to use node-gyp to compile native modules, you can install node-gyp
|
||||
via npm if you hadn't:
|
||||
The [apm](https://github.com/atom/apm) provided a easy way to do this, after
|
||||
installing it you could use it to install dependencies just like using `npm`:
|
||||
|
||||
```bash
|
||||
$ npm install -g node-gyp
|
||||
$ cd /path/to/atom-shell/project/
|
||||
$ apm install .
|
||||
```
|
||||
|
||||
But you should notice that `apm install module` wont' work because it will
|
||||
install a user package for [Atom](https://github.com/atom/atom) instead.
|
||||
|
||||
Apart from `apm`, you can also use `node-gyp` and `npm` to manually build the
|
||||
native modules.
|
||||
|
||||
## The node-gyp way
|
||||
|
||||
First you need to check which node release atom-shell is carrying via
|
||||
`process.version` (at the time of writing it is v0.10.5), then you can
|
||||
configure and build native modules via following commands:
|
||||
@@ -23,13 +32,7 @@ The `HOME=~/.atom-shell-gyp` changes where to find development headers. The
|
||||
`--target=0.10.5` is specifying node's version. The `--dist-url=...` specifies
|
||||
where to download the headers.
|
||||
|
||||
## Use npm to build native modules
|
||||
|
||||
Under most circumstances you would want to use npm to install modules, if
|
||||
you're using npm >= v1.2.19 (because [a
|
||||
patch](https://github.com/TooTallNate/node-gyp/commit/afbcdea1ffd25c02bc88d119b10337852c44d400)
|
||||
is needed to make `npm_config_disturl` work) you can use following code to
|
||||
download and build native modules against atom-shell's headers:
|
||||
## The npm way
|
||||
|
||||
```bash
|
||||
export npm_config_disturl=https://gh-contractor-zcbenz.s3.amazonaws.com/atom-shell/dist
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "atom-shell",
|
||||
"version": "0.9.2",
|
||||
"version": "0.10.2",
|
||||
|
||||
"devDependencies": {
|
||||
"coffee-script": "~1.6.3",
|
||||
@@ -9,7 +9,7 @@
|
||||
"mocha": "~1.13.0",
|
||||
"pathwatcher": "0.14.0",
|
||||
"q": "0.9.7",
|
||||
"runas": "0.3.0",
|
||||
"runas": "0.5.*",
|
||||
"temp": "~0.6.0",
|
||||
"walkdir": "~0.0.7"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/options_switches.h"
|
||||
#include "renderer/api/atom_renderer_bindings.h"
|
||||
#include "renderer/atom_render_view_observer.h"
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
|
||||
#include "common/v8/node_common.h"
|
||||
@@ -19,11 +20,15 @@ namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Security tokens.
|
||||
const char* kExceptIframe = "except-iframe";
|
||||
const char* kManualEnableIframe = "manual-enable-iframe";
|
||||
const char* kDisable = "disable";
|
||||
const char* kEnableNodeIntegration = "enable-node-integration";
|
||||
|
||||
// Scheme used by devtools
|
||||
const char* kChromeDevToolsScheme = "chrome-devtools";
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomRendererClient::AtomRendererClient()
|
||||
@@ -150,6 +155,10 @@ bool AtomRendererClient::ShouldFork(WebKit::WebFrame* frame,
|
||||
bool AtomRendererClient::IsNodeBindingEnabled(WebKit::WebFrame* frame) {
|
||||
if (node_integration_ == DISABLE)
|
||||
return false;
|
||||
// Do not pollute devtools.
|
||||
else if (frame != NULL &&
|
||||
GURL(frame->document().url()).SchemeIs(kChromeDevToolsScheme))
|
||||
return false;
|
||||
// Node integration is enabled in main frame unless explictly disabled.
|
||||
else if (frame == main_frame_)
|
||||
return true;
|
||||
|
||||
@@ -50,6 +50,11 @@ window.onerror = (error) ->
|
||||
else
|
||||
false
|
||||
|
||||
# Override default window.close, see:
|
||||
# https://github.com/atom/atom-shell/issues/70
|
||||
window.close = ->
|
||||
require('remote').getCurrentWindow().close()
|
||||
|
||||
# Override default window.open.
|
||||
window.open = (url, name, features) ->
|
||||
options = {}
|
||||
|
||||
@@ -11,6 +11,8 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def main():
|
||||
os.environ['CI'] = '1'
|
||||
|
||||
rm_rf(os.path.join(SOURCE_ROOT, 'out'))
|
||||
rm_rf(os.path.join(SOURCE_ROOT, 'node_modules'))
|
||||
rm_rf(os.path.join(SOURCE_ROOT, 'frameworks'))
|
||||
|
||||
29
script/cpplint.py
vendored
29
script/cpplint.py
vendored
@@ -2,21 +2,24 @@
|
||||
|
||||
import fnmatch
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from lib.util import execute
|
||||
|
||||
IGNORE_FILES = [
|
||||
'browser/atom_application_mac.h',
|
||||
'browser/atom_application_delegate_mac.h',
|
||||
'browser/native_window_mac.h',
|
||||
'browser/resources/win/resource.h',
|
||||
'browser/ui/cocoa/event_processing_window.h',
|
||||
'browser/ui/cocoa/atom_menu_controller.h',
|
||||
'browser/ui/cocoa/nsalert_synchronous_sheet.h',
|
||||
'common/api/api_messages.cc',
|
||||
'common/api/api_messages.h',
|
||||
'common/atom_version.h',
|
||||
'common/swap_or_assign.h',
|
||||
os.path.join('browser', 'atom_application_mac.h'),
|
||||
os.path.join('browser', 'atom_application_delegate_mac.h'),
|
||||
os.path.join('browser', 'native_window_mac.h'),
|
||||
os.path.join('browser', 'resources', 'win', 'resource.h'),
|
||||
os.path.join('browser', 'ui', 'cocoa', 'event_processing_window.h'),
|
||||
os.path.join('browser', 'ui', 'cocoa', 'atom_menu_controller.h'),
|
||||
os.path.join('browser', 'ui', 'cocoa', 'nsalert_synchronous_sheet.h'),
|
||||
os.path.join('browser', 'ui', 'gtk', 'gtk_custom_menu.cc'),
|
||||
os.path.join('browser', 'ui', 'gtk', 'gtk_custom_menu_item.cc'),
|
||||
os.path.join('common', 'api', 'api_messages.cc'),
|
||||
os.path.join('common', 'api', 'api_messages.h'),
|
||||
os.path.join('common', 'atom_version.h'),
|
||||
os.path.join('common', 'swap_or_assign.h'),
|
||||
]
|
||||
|
||||
SOURCE_ROOT = os.path.dirname(os.path.dirname(__file__))
|
||||
@@ -42,7 +45,7 @@ def list_files(directories, filters):
|
||||
def call_cpplint(files):
|
||||
cpplint = os.path.join(SOURCE_ROOT, 'vendor', 'depot_tools', 'cpplint.py')
|
||||
rules = '--filter=-build/header_guard,-build/include_what_you_use'
|
||||
subprocess.check_call([sys.executable, cpplint, rules] + files)
|
||||
execute([sys.executable, cpplint, rules] + files)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -9,7 +9,7 @@ import tarfile
|
||||
|
||||
from lib.config import LIBCHROMIUMCONTENT_COMMIT, BASE_URL, NODE_VERSION
|
||||
from lib.util import scoped_cwd, rm_rf, get_atom_shell_version, make_zip, \
|
||||
safe_mkdir
|
||||
safe_mkdir, execute
|
||||
|
||||
|
||||
ATOM_SHELL_VRESION = get_atom_shell_version()
|
||||
@@ -39,6 +39,11 @@ TARGET_BINARIES = {
|
||||
'icudt.dll',
|
||||
'libGLESv2.dll',
|
||||
],
|
||||
'linux': [
|
||||
'atom',
|
||||
'libchromiumcontent.so',
|
||||
'libffmpegsumo.so',
|
||||
],
|
||||
}
|
||||
TARGET_DIRECTORIES = {
|
||||
'darwin': [
|
||||
@@ -47,6 +52,9 @@ TARGET_DIRECTORIES = {
|
||||
'win32': [
|
||||
'resources',
|
||||
],
|
||||
'linux': [
|
||||
'resources',
|
||||
],
|
||||
}
|
||||
|
||||
HEADERS_SUFFIX = [
|
||||
@@ -74,8 +82,9 @@ def main():
|
||||
args = parse_args()
|
||||
|
||||
force_build()
|
||||
download_libchromiumcontent_symbols(args.url)
|
||||
create_symbols()
|
||||
if TARGET_PLATFORM != 'linux':
|
||||
download_libchromiumcontent_symbols(args.url)
|
||||
create_symbols()
|
||||
copy_binaries()
|
||||
copy_headers()
|
||||
copy_license()
|
||||
@@ -98,7 +107,7 @@ def parse_args():
|
||||
|
||||
def force_build():
|
||||
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
||||
subprocess.check_call([sys.executable, build, '-c', 'Release'])
|
||||
execute([sys.executable, build, '-c', 'Release'])
|
||||
|
||||
|
||||
def copy_binaries():
|
||||
@@ -149,9 +158,9 @@ def create_version():
|
||||
|
||||
|
||||
def download_libchromiumcontent_symbols(url):
|
||||
if sys.platform == 'darwin':
|
||||
if TARGET_PLATFORM == 'darwin':
|
||||
symbols_name = 'libchromiumcontent.dylib.dSYM'
|
||||
else:
|
||||
elif TARGET_PLATFORM == 'win32':
|
||||
symbols_name = 'chromiumcontent.dll.pdb'
|
||||
|
||||
brightray_dir = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor')
|
||||
@@ -196,6 +205,8 @@ def create_symbols_zip():
|
||||
with scoped_cwd(DIST_DIR):
|
||||
files = ['LICENSE', 'version']
|
||||
dirs = ['Atom-Shell.breakpad.syms']
|
||||
if TARGET_PLATFORM == 'linux':
|
||||
dirs = []
|
||||
make_zip(zip_file, files, dirs)
|
||||
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
NODE_VERSION = 'v0.11.10'
|
||||
BASE_URL = 'https://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = 'b27290717c08f8c6a58067d3c3725d68b4e6a2e5'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '1df8e7cdac8aa74c91c19ae0691ce512d560ab3e'
|
||||
|
||||
@@ -36,6 +36,8 @@ def download(text, url, path):
|
||||
downloaded_size = 0
|
||||
block_size = 128
|
||||
|
||||
ci = os.environ.get('CI') == '1'
|
||||
|
||||
while True:
|
||||
buf = web_file.read(block_size)
|
||||
if not buf:
|
||||
@@ -44,11 +46,15 @@ def download(text, url, path):
|
||||
downloaded_size += len(buf)
|
||||
local_file.write(buf)
|
||||
|
||||
percent = downloaded_size * 100. / file_size
|
||||
status = "\r%s %10d [%3.1f%%]" % (text, downloaded_size, percent)
|
||||
print status,
|
||||
if not ci:
|
||||
percent = downloaded_size * 100. / file_size
|
||||
status = "\r%s %10d [%3.1f%%]" % (text, downloaded_size, percent)
|
||||
print status,
|
||||
|
||||
print
|
||||
if ci:
|
||||
print "%s done." % (text)
|
||||
else:
|
||||
print
|
||||
|
||||
|
||||
def extract_tarball(tarball_path, member, destination):
|
||||
@@ -59,7 +65,7 @@ def extract_tarball(tarball_path, member, destination):
|
||||
def extract_zip(zip_path, destination):
|
||||
if sys.platform == 'darwin':
|
||||
# Use unzip command on Mac to keep symbol links in zip file work.
|
||||
subprocess.check_output(['unzip', zip_path, '-d', destination])
|
||||
execute(['unzip', zip_path, '-d', destination])
|
||||
else:
|
||||
with zipfile.ZipFile(zip_path) as z:
|
||||
z.extractall(destination)
|
||||
@@ -68,7 +74,7 @@ def make_zip(zip_file_path, files, dirs):
|
||||
safe_unlink(zip_file_path)
|
||||
if sys.platform == 'darwin':
|
||||
files += dirs
|
||||
subprocess.check_output(['zip', '-r', '-y', zip_file_path] + files)
|
||||
execute(['zip', '-r', '-y', zip_file_path] + files)
|
||||
else:
|
||||
zip_file = zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED)
|
||||
for filename in files:
|
||||
@@ -104,5 +110,13 @@ def safe_mkdir(path):
|
||||
raise
|
||||
|
||||
|
||||
def execute(argv):
|
||||
try:
|
||||
subprocess.check_output(argv, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print e.output
|
||||
raise e
|
||||
|
||||
|
||||
def get_atom_shell_version():
|
||||
return subprocess.check_output(['git', 'describe', '--tags']).strip()
|
||||
|
||||
@@ -14,8 +14,10 @@ def main():
|
||||
if sys.platform == 'darwin':
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'Atom.app',
|
||||
'Contents', 'MacOS', 'Atom')
|
||||
else:
|
||||
elif sys.platform == 'win32':
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'atom.exe')
|
||||
else:
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Debug', 'atom')
|
||||
|
||||
subprocess.check_call([atom_shell, 'spec'] + sys.argv[1:])
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ def update_gyp():
|
||||
subprocess.call([python, gyp,
|
||||
'-f', 'ninja', '--depth', '.', 'atom.gyp',
|
||||
'-Icommon.gypi', '-Ivendor/brightray/brightray.gypi',
|
||||
'-Dlinux_clang=0', # Disable brightray's clang setting
|
||||
'-Dtarget_arch={0}'.format(arch),
|
||||
'-Dlibrary=static_library'])
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import sys
|
||||
import tempfile
|
||||
|
||||
from lib.config import NODE_VERSION
|
||||
from lib.util import get_atom_shell_version, scoped_cwd, safe_mkdir
|
||||
from lib.util import get_atom_shell_version, scoped_cwd, safe_mkdir, execute
|
||||
from lib.github import GitHub
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ def main():
|
||||
|
||||
if not dist_newer_than_head():
|
||||
create_dist = os.path.join(SOURCE_ROOT, 'script', 'create-dist.py')
|
||||
subprocess.check_output([sys.executable, create_dist])
|
||||
execute([sys.executable, create_dist])
|
||||
|
||||
build_version = get_atom_shell_build_version()
|
||||
if not ATOM_SHELL_VRESION.startswith(build_version):
|
||||
@@ -70,11 +70,13 @@ def parse_args():
|
||||
|
||||
|
||||
def get_atom_shell_build_version():
|
||||
if sys.platform == 'darwin':
|
||||
if TARGET_PLATFORM == 'darwin':
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Release', 'Atom.app',
|
||||
'Contents', 'MacOS', 'Atom')
|
||||
else:
|
||||
elif TARGET_PLATFORM == 'win32':
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Release', 'atom.exe')
|
||||
else:
|
||||
atom_shell = os.path.join(SOURCE_ROOT, 'out', 'Release', 'atom')
|
||||
|
||||
return subprocess.check_output([atom_shell, '--version']).strip()
|
||||
|
||||
@@ -156,8 +158,7 @@ def upload_node(bucket, access_key, secret_key, version):
|
||||
if TARGET_PLATFORM == 'win32':
|
||||
# Generate the node.lib.
|
||||
build = os.path.join(SOURCE_ROOT, 'script', 'build.py')
|
||||
subprocess.check_output([sys.executable, build, '-c', 'Release',
|
||||
'-t', 'generate_node_lib'])
|
||||
execute([sys.executable, build, '-c', 'Release', '-t', 'generate_node_lib'])
|
||||
|
||||
# Upload the 32bit node.lib.
|
||||
node_lib = os.path.join(OUT_DIR, 'node.lib')
|
||||
@@ -201,7 +202,7 @@ def s3put(bucket, access_key, secret_key, prefix, key_prefix, files):
|
||||
'--grant', 'public-read'
|
||||
] + files
|
||||
|
||||
subprocess.check_output(args)
|
||||
execute(args)
|
||||
|
||||
|
||||
def touch_x64_node_lib():
|
||||
|
||||
@@ -33,7 +33,7 @@ describe 'browser-window module', ->
|
||||
w.loadUrl 'file://' + path.join(fixtures, 'api', 'beforeunload-false.html')
|
||||
|
||||
describe 'window.close()', ->
|
||||
xit 'should emit unload handler', (done) ->
|
||||
it 'should emit unload handler', (done) ->
|
||||
w = new BrowserWindow(show: false)
|
||||
w.on 'closed', ->
|
||||
test = path.join(fixtures, 'api', 'close')
|
||||
|
||||
@@ -32,7 +32,8 @@ ipc.on('echo', function(ev, pid, rid, msg) {
|
||||
ev.returnValue = msg;
|
||||
});
|
||||
|
||||
process.on('uncaughtException', function() {
|
||||
process.on('uncaughtException', function(error) {
|
||||
console.log(error);
|
||||
window.openDevTools();
|
||||
});
|
||||
|
||||
@@ -133,7 +134,7 @@ app.on('ready', function() {
|
||||
app.setApplicationMenu(menu);
|
||||
|
||||
// Test if using protocol module would crash.
|
||||
require('protocol').registerProtocol('test-if-crashes', function() {});
|
||||
// require('protocol').registerProtocol('test-if-crashes', function() {});
|
||||
|
||||
window = new BrowserWindow({
|
||||
title: 'atom-shell tests',
|
||||
|
||||
2
vendor/apm
vendored
2
vendor/apm
vendored
Submodule vendor/apm updated: d976d63c8d...a9e5498a83
2
vendor/node
vendored
2
vendor/node
vendored
Submodule vendor/node updated: 80c5e17c09...d1ebf08070
3674
vendor/third_party/lss/linux_syscall_support.h
vendored
Normal file
3674
vendor/third_party/lss/linux_syscall_support.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user