Merge pull request #11 from atom/master

Update from upstream
This commit is contained in:
Eran Tiktin 2016-01-15 21:00:37 +02:00
commit b9789ef191
244 changed files with 13834 additions and 7767 deletions

View file

@ -4,7 +4,7 @@
'product_name%': 'Electron', 'product_name%': 'Electron',
'company_name%': 'GitHub, Inc', 'company_name%': 'GitHub, Inc',
'company_abbr%': 'github', 'company_abbr%': 'github',
'version%': '0.36.2', 'version%': '0.36.4',
}, },
'includes': [ 'includes': [
'filenames.gypi', 'filenames.gypi',
@ -28,7 +28,7 @@
'target_name': '<(project_name)', 'target_name': '<(project_name)',
'type': 'executable', 'type': 'executable',
'dependencies': [ 'dependencies': [
'compile_coffee', 'js2asar',
'<(project_name)_lib', '<(project_name)_lib',
], ],
'sources': [ 'sources': [
@ -221,7 +221,7 @@
'target_name': '<(project_name)_lib', 'target_name': '<(project_name)_lib',
'type': 'static_library', 'type': 'static_library',
'dependencies': [ 'dependencies': [
'atom_coffee2c', 'atom_js2c',
'vendor/brightray/brightray.gyp:brightray', 'vendor/brightray/brightray.gyp:brightray',
'vendor/node/node.gyp:node', 'vendor/node/node.gyp:node',
], ],
@ -351,11 +351,11 @@
], ],
}, # target <(product_name)_lib }, # target <(product_name)_lib
{ {
'target_name': 'compile_coffee', 'target_name': 'js2asar',
'type': 'none', 'type': 'none',
'actions': [ 'actions': [
{ {
'action_name': 'compile_coffee', 'action_name': 'js2asar',
'variables': { 'variables': {
'conditions': [ 'conditions': [
['OS=="mac"', { ['OS=="mac"', {
@ -366,41 +366,41 @@
], ],
}, },
'inputs': [ 'inputs': [
'<@(coffee_sources)', '<@(js_sources)',
], ],
'outputs': [ 'outputs': [
'<(resources_path)/atom.asar', '<(resources_path)/atom.asar',
], ],
'action': [ 'action': [
'python', 'python',
'tools/coffee2asar.py', 'tools/js2asar.py',
'<@(_outputs)', '<@(_outputs)',
'<@(_inputs)', '<@(_inputs)',
], ],
} }
], ],
}, # target compile_coffee }, # target js2asar
{ {
'target_name': 'atom_coffee2c', 'target_name': 'atom_js2c',
'type': 'none', 'type': 'none',
'actions': [ 'actions': [
{ {
'action_name': 'atom_coffee2c', 'action_name': 'atom_js2c',
'inputs': [ 'inputs': [
'<@(coffee2c_sources)', '<@(js2c_sources)',
], ],
'outputs': [ 'outputs': [
'<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h',
], ],
'action': [ 'action': [
'python', 'python',
'tools/coffee2c.py', 'tools/js2c.py',
'<@(_outputs)', '<@(_outputs)',
'<@(_inputs)', '<@(_inputs)',
], ],
} }
], ],
}, # target atom_coffee2c }, # target atom_js2c
], ],
'conditions': [ 'conditions': [
['OS=="mac"', { ['OS=="mac"', {

View file

@ -37,7 +37,7 @@ bool UvTaskRunner::PostNonNestableDelayedTask(
const tracked_objects::Location& from_here, const tracked_objects::Location& from_here,
const base::Closure& task, const base::Closure& task,
base::TimeDelta delay) { base::TimeDelta delay) {
return PostDelayedTask(from_here, task, delay);; return PostDelayedTask(from_here, task, delay);
} }
// static // static

View file

@ -41,6 +41,7 @@ namespace {
void ShowMessageBox(int type, void ShowMessageBox(int type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -54,12 +55,13 @@ void ShowMessageBox(int type,
if (mate::Converter<atom::MessageBoxCallback>::FromV8(args->isolate(), if (mate::Converter<atom::MessageBoxCallback>::FromV8(args->isolate(),
peek, peek,
&callback)) { &callback)) {
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons, cancel_id, atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons,
options, title, message, detail, icon, callback); default_id, cancel_id, options, title,
message, detail, icon, callback);
} else { } else {
int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type, int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type,
buttons, cancel_id, options, title, buttons, default_id, cancel_id,
message, detail, icon); options, title, message, detail, icon);
args->Return(chosen); args->Return(chosen);
} }
} }

View file

@ -22,6 +22,7 @@
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/prefs/pref_service.h" #include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/thread_task_runner_handle.h" #include "base/thread_task_runner_handle.h"
#include "brightray/browser/net/devtools_network_conditions.h" #include "brightray/browser/net/devtools_network_conditions.h"
@ -114,14 +115,25 @@ struct Converter<net::ProxyConfig> {
static bool FromV8(v8::Isolate* isolate, static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val, v8::Local<v8::Value> val,
net::ProxyConfig* out) { net::ProxyConfig* out) {
std::string proxy; std::string proxy_rules;
if (!ConvertFromV8(isolate, val, &proxy)) GURL pac_url;
mate::Dictionary options;
// Fallback to previous API when passed String.
// https://git.io/vuhjj
if (ConvertFromV8(isolate, val, &proxy_rules)) {
pac_url = GURL(proxy_rules); // Assume it is PAC script if it is URL.
} else if (ConvertFromV8(isolate, val, &options)) {
options.Get("pacScript", &pac_url);
options.Get("proxyRules", &proxy_rules);
} else {
return false; return false;
auto pac_url = GURL(proxy); }
if (pac_url.is_valid()) {
// pacScript takes precedence over proxyRules.
if (!pac_url.is_empty() && pac_url.is_valid()) {
out->set_pac_url(pac_url); out->set_pac_url(pac_url);
} else { } else {
out->proxy_rules().ParseFromString(proxy); out->proxy_rules().ParseFromString(proxy_rules);
} }
return true; return true;
} }
@ -193,7 +205,7 @@ class ResolveProxyHelper {
}; };
// Runs the callback in UI thread. // Runs the callback in UI thread.
template <typename ...T> template<typename ...T>
void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) { void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
BrowserThread::PostTask( BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE, base::Bind(callback, result...)); BrowserThread::UI, FROM_HERE, base::Bind(callback, result...));
@ -201,19 +213,35 @@ void RunCallbackInUI(const base::Callback<void(T...)>& callback, T... result) {
// Callback of HttpCache::GetBackend. // Callback of HttpCache::GetBackend.
void OnGetBackend(disk_cache::Backend** backend_ptr, void OnGetBackend(disk_cache::Backend** backend_ptr,
Session::CacheAction action,
const net::CompletionCallback& callback, const net::CompletionCallback& callback,
int result) { int result) {
if (result != net::OK) { if (result != net::OK) {
RunCallbackInUI(callback, result); RunCallbackInUI(callback, result);
} else if (backend_ptr && *backend_ptr) { } else if (backend_ptr && *backend_ptr) {
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>, callback)); if (action == Session::CacheAction::CLEAR) {
(*backend_ptr)->DoomAllEntries(base::Bind(&RunCallbackInUI<int>,
callback));
} else if (action == Session::CacheAction::STATS) {
base::StringPairs stats;
(*backend_ptr)->GetStats(&stats);
for (size_t i = 0; i < stats.size(); ++i) {
if (stats[i].first == "Current size") {
int current_size;
base::StringToInt(stats[i].second, &current_size);
RunCallbackInUI(callback, current_size);
break;
}
}
}
} else { } else {
RunCallbackInUI<int>(callback, net::ERR_FAILED); RunCallbackInUI<int>(callback, net::ERR_FAILED);
} }
} }
void ClearHttpCacheInIO( void DoCacheActionInIO(
const scoped_refptr<net::URLRequestContextGetter>& context_getter, const scoped_refptr<net::URLRequestContextGetter>& context_getter,
Session::CacheAction action,
const net::CompletionCallback& callback) { const net::CompletionCallback& callback) {
auto request_context = context_getter->GetURLRequestContext(); auto request_context = context_getter->GetURLRequestContext();
auto http_cache = request_context->http_transaction_factory()->GetCache(); auto http_cache = request_context->http_transaction_factory()->GetCache();
@ -224,7 +252,7 @@ void ClearHttpCacheInIO(
using BackendPtr = disk_cache::Backend*; using BackendPtr = disk_cache::Backend*;
BackendPtr* backend_ptr = new BackendPtr(nullptr); BackendPtr* backend_ptr = new BackendPtr(nullptr);
net::CompletionCallback on_get_backend = net::CompletionCallback on_get_backend =
base::Bind(&OnGetBackend, base::Owned(backend_ptr), callback); base::Bind(&OnGetBackend, base::Owned(backend_ptr), action, callback);
int rv = http_cache->GetBackend(backend_ptr, on_get_backend); int rv = http_cache->GetBackend(backend_ptr, on_get_backend);
if (rv != net::ERR_IO_PENDING) if (rv != net::ERR_IO_PENDING)
on_get_backend.Run(net::OK); on_get_backend.Run(net::OK);
@ -276,10 +304,12 @@ void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
new ResolveProxyHelper(browser_context(), url, callback); new ResolveProxyHelper(browser_context(), url, callback);
} }
void Session::ClearCache(const net::CompletionCallback& callback) { template<Session::CacheAction action>
void Session::DoCacheAction(const net::CompletionCallback& callback) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&ClearHttpCacheInIO, base::Bind(&DoCacheActionInIO,
make_scoped_refptr(browser_context_->GetRequestContext()), make_scoped_refptr(browser_context_->GetRequestContext()),
action,
callback)); callback));
} }
@ -301,6 +331,12 @@ void Session::ClearStorageData(mate::Arguments* args) {
base::Time(), base::Time::Max(), callback); base::Time(), base::Time::Max(), callback);
} }
void Session::FlushStorageData() {
auto storage_partition =
content::BrowserContext::GetStoragePartition(browser_context(), nullptr);
storage_partition->Flush();
}
void Session::SetProxy(const net::ProxyConfig& config, void Session::SetProxy(const net::ProxyConfig& config,
const base::Closure& callback) { const base::Closure& callback) {
auto getter = browser_context_->GetRequestContext(); auto getter = browser_context_->GetRequestContext();
@ -403,8 +439,10 @@ void Session::BuildPrototype(v8::Isolate* isolate,
mate::ObjectTemplateBuilder(isolate, prototype) mate::ObjectTemplateBuilder(isolate, prototype)
.MakeDestroyable() .MakeDestroyable()
.SetMethod("resolveProxy", &Session::ResolveProxy) .SetMethod("resolveProxy", &Session::ResolveProxy)
.SetMethod("clearCache", &Session::ClearCache) .SetMethod("getCacheSize", &Session::DoCacheAction<CacheAction::STATS>)
.SetMethod("clearCache", &Session::DoCacheAction<CacheAction::CLEAR>)
.SetMethod("clearStorageData", &Session::ClearStorageData) .SetMethod("clearStorageData", &Session::ClearStorageData)
.SetMethod("flushStorageData", &Session::FlushStorageData)
.SetMethod("setProxy", &Session::SetProxy) .SetMethod("setProxy", &Session::SetProxy)
.SetMethod("setDownloadPath", &Session::SetDownloadPath) .SetMethod("setDownloadPath", &Session::SetDownloadPath)
.SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)

View file

@ -38,6 +38,11 @@ class Session: public mate::TrackableObject<Session>,
public: public:
using ResolveProxyCallback = base::Callback<void(std::string)>; using ResolveProxyCallback = base::Callback<void(std::string)>;
enum class CacheAction {
CLEAR,
STATS,
};
// Gets or creates Session from the |browser_context|. // Gets or creates Session from the |browser_context|.
static mate::Handle<Session> CreateFrom( static mate::Handle<Session> CreateFrom(
v8::Isolate* isolate, AtomBrowserContext* browser_context); v8::Isolate* isolate, AtomBrowserContext* browser_context);
@ -62,8 +67,10 @@ class Session: public mate::TrackableObject<Session>,
private: private:
void ResolveProxy(const GURL& url, ResolveProxyCallback callback); void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
void ClearCache(const net::CompletionCallback& callback); template<CacheAction action>
void DoCacheAction(const net::CompletionCallback& callback);
void ClearStorageData(mate::Arguments* args); void ClearStorageData(mate::Arguments* args);
void FlushStorageData();
void SetProxy(const net::ProxyConfig& config, const base::Closure& callback); void SetProxy(const net::ProxyConfig& config, const base::Closure& callback);
void SetDownloadPath(const base::FilePath& path); void SetDownloadPath(const base::FilePath& path);
void EnableNetworkEmulation(const mate::Dictionary& options); void EnableNetworkEmulation(const mate::Dictionary& options);

View file

@ -33,7 +33,6 @@
#include "chrome/browser/printing/print_view_manager_basic.h" #include "chrome/browser/printing/print_view_manager_basic.h"
#include "chrome/browser/printing/print_preview_message_handler.h" #include "chrome/browser/printing/print_preview_message_handler.h"
#include "content/common/view_messages.h" #include "content/common/view_messages.h"
#include "content/public/browser/browser_plugin_guest_manager.h"
#include "content/public/browser/favicon_status.h" #include "content/public/browser/favicon_status.h"
#include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_details.h" #include "content/public/browser/navigation_details.h"
@ -75,15 +74,6 @@ void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
user_agent)); user_agent));
} }
bool NotifyZoomLevelChanged(
double level, content::WebContents* guest_web_contents) {
guest_web_contents->SendToAllFrames(
new AtomViewMsg_SetZoomLevel(MSG_ROUTING_NONE, level));
// Return false to iterate over all guests.
return false;
}
} // namespace } // namespace
namespace mate { namespace mate {
@ -290,14 +280,17 @@ WebContents::WebContents(v8::Isolate* isolate,
} }
WebContents::~WebContents() { WebContents::~WebContents() {
if (type_ == WEB_VIEW && managed_web_contents()) { // The destroy() is called.
// When force destroying the "destroyed" event is not emitted. if (managed_web_contents()) {
// For webview we need to tell content module to do some cleanup work before
// destroying it.
if (type_ == WEB_VIEW)
guest_delegate_->Destroy();
// The WebContentsDestroyed will not be called automatically because we
// unsubscribe from webContents before destroying it. So we have to manually
// call it here to make sure "destroyed" event is emitted.
WebContentsDestroyed(); WebContentsDestroyed();
guest_delegate_->Destroy();
Observe(nullptr);
DestroyWebContents();
} }
} }
@ -625,18 +618,43 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage) IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync, IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
OnRendererMessageSync) OnRendererMessageSync)
IPC_MESSAGE_HANDLER(AtomViewHostMsg_ZoomLevelChanged, OnZoomLevelChanged)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() IPC_END_MESSAGE_MAP()
return handled; return handled;
} }
// There are three ways of destroying a webContents:
// 1. call webContents.destory();
// 2. garbage collection;
// 3. user closes the window of webContents;
// For webview only #1 will happen, for BrowserWindow both #1 and #3 may
// happen. The #2 should never happen for webContents, because webview is
// managed by GuestViewManager, and BrowserWindow's webContents is managed
// by api::Window.
// For #1, the destructor will do the cleanup work and we only need to make
// sure "destroyed" event is emitted. For #3, the content::WebContents will
// be destroyed on close, and WebContentsDestroyed would be called for it, so
// we need to make sure the api::WebContents is also deleted.
void WebContents::WebContentsDestroyed() { void WebContents::WebContentsDestroyed() {
// The RenderViewDeleted was not called when the WebContents is destroyed. // The RenderViewDeleted was not called when the WebContents is destroyed.
RenderViewDeleted(web_contents()->GetRenderViewHost()); RenderViewDeleted(web_contents()->GetRenderViewHost());
Emit("destroyed");
// This event is only for internal use, which is emitted when WebContents is
// being destroyed.
Emit("will-destroy");
// Cleanup relationships with other parts.
RemoveFromWeakMap(); RemoveFromWeakMap();
// We can not call Destroy here because we need to call Emit first, but we
// also do not want any method to be used, so just mark as destroyed here.
MarkDestroyed();
Emit("destroyed");
// Destroy the native class in next tick.
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
} }
void WebContents::NavigationEntryCommitted( void WebContents::NavigationEntryCommitted(
@ -748,11 +766,6 @@ bool WebContents::SavePage(const base::FilePath& full_file_path,
return handler->Handle(full_file_path, save_type); return handler->Handle(full_file_path, save_type);
} }
void WebContents::ExecuteJavaScript(const base::string16& code,
bool has_user_gesture) {
Send(new AtomViewMsg_ExecuteJavaScript(routing_id(), code, has_user_gesture));
}
void WebContents::OpenDevTools(mate::Arguments* args) { void WebContents::OpenDevTools(mate::Arguments* args) {
if (type_ == REMOTE) if (type_ == REMOTE)
return; return;
@ -992,7 +1005,7 @@ void WebContents::SendInputEvent(v8::Isolate* isolate,
return; return;
} }
} else if (blink::WebInputEvent::isKeyboardEventType(type)) { } else if (blink::WebInputEvent::isKeyboardEventType(type)) {
content::NativeWebKeyboardEvent keyboard_event;; content::NativeWebKeyboardEvent keyboard_event;
if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) { if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) {
host->ForwardKeyboardEvent(keyboard_event); host->ForwardKeyboardEvent(keyboard_event);
return; return;
@ -1085,7 +1098,6 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
.SetMethod("getUserAgent", &WebContents::GetUserAgent) .SetMethod("getUserAgent", &WebContents::GetUserAgent)
.SetMethod("insertCSS", &WebContents::InsertCSS) .SetMethod("insertCSS", &WebContents::InsertCSS)
.SetMethod("savePage", &WebContents::SavePage) .SetMethod("savePage", &WebContents::SavePage)
.SetMethod("_executeJavaScript", &WebContents::ExecuteJavaScript)
.SetMethod("openDevTools", &WebContents::OpenDevTools) .SetMethod("openDevTools", &WebContents::OpenDevTools)
.SetMethod("closeDevTools", &WebContents::CloseDevTools) .SetMethod("closeDevTools", &WebContents::CloseDevTools)
.SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened) .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
@ -1152,15 +1164,6 @@ void WebContents::OnRendererMessageSync(const base::string16& channel,
EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args); EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
} }
void WebContents::OnZoomLevelChanged(double level) {
auto manager = web_contents()->GetBrowserContext()->GetGuestManager();
if (!manager)
return;
manager->ForEachGuest(web_contents(),
base::Bind(&NotifyZoomLevelChanged,
level));
}
// static // static
mate::Handle<WebContents> WebContents::CreateFrom( mate::Handle<WebContents> WebContents::CreateFrom(
v8::Isolate* isolate, content::WebContents* web_contents) { v8::Isolate* isolate, content::WebContents* web_contents) {

View file

@ -74,8 +74,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
bool SavePage(const base::FilePath& full_file_path, bool SavePage(const base::FilePath& full_file_path,
const content::SavePageType& save_type, const content::SavePageType& save_type,
const SavePageHandler::SavePageCallback& callback); const SavePageHandler::SavePageCallback& callback);
void ExecuteJavaScript(const base::string16& code,
bool has_user_gesture);
void OpenDevTools(mate::Arguments* args); void OpenDevTools(mate::Arguments* args);
void CloseDevTools(); void CloseDevTools();
bool IsDevToolsOpened(); bool IsDevToolsOpened();
@ -265,10 +263,6 @@ class WebContents : public mate::TrackableObject<WebContents>,
const base::ListValue& args, const base::ListValue& args,
IPC::Message* message); IPC::Message* message);
// Called when guests need to be notified of
// embedders' zoom level change.
void OnZoomLevelChanged(double level);
v8::Global<v8::Value> session_; v8::Global<v8::Value> session_;
v8::Global<v8::Value> devtools_web_contents_; v8::Global<v8::Value> devtools_web_contents_;

View file

@ -107,7 +107,6 @@ void TranslateOldOptions(v8::Isolate* isolate, v8::Local<v8::Object> options) {
} }
} }
#if defined(OS_WIN)
// Converts binary data to Buffer. // Converts binary data to Buffer.
v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) { v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size); auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size);
@ -116,7 +115,6 @@ v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
else else
return buffer.ToLocalChecked(); return buffer.ToLocalChecked();
} }
#endif
} // namespace } // namespace
@ -594,6 +592,12 @@ void Window::SetAspectRatio(double aspect_ratio, mate::Arguments* args) {
window_->SetAspectRatio(aspect_ratio, extra_size); window_->SetAspectRatio(aspect_ratio, extra_size);
} }
v8::Local<v8::Value> Window::GetNativeWindowHandle() {
gfx::AcceleratedWidget handle = window_->GetAcceleratedWidget();
return ToBuffer(
isolate(), static_cast<void*>(&handle), sizeof(gfx::AcceleratedWidget));
}
void Window::SetVisibleOnAllWorkspaces(bool visible) { void Window::SetVisibleOnAllWorkspaces(bool visible) {
return window_->SetVisibleOnAllWorkspaces(visible); return window_->SetVisibleOnAllWorkspaces(visible);
} }
@ -634,6 +638,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setFullScreen", &Window::SetFullScreen) .SetMethod("setFullScreen", &Window::SetFullScreen)
.SetMethod("isFullScreen", &Window::IsFullscreen) .SetMethod("isFullScreen", &Window::IsFullscreen)
.SetMethod("setAspectRatio", &Window::SetAspectRatio) .SetMethod("setAspectRatio", &Window::SetAspectRatio)
.SetMethod("getNativeWindowHandle", &Window::GetNativeWindowHandle)
.SetMethod("getBounds", &Window::GetBounds) .SetMethod("getBounds", &Window::GetBounds)
.SetMethod("setBounds", &Window::SetBounds) .SetMethod("setBounds", &Window::SetBounds)
.SetMethod("getSize", &Window::GetSize) .SetMethod("getSize", &Window::GetSize)

View file

@ -137,6 +137,7 @@ class Window : public mate::TrackableObject<Window>,
void SetMenuBarVisibility(bool visible); void SetMenuBarVisibility(bool visible);
bool IsMenuBarVisible(); bool IsMenuBarVisible();
void SetAspectRatio(double aspect_ratio, mate::Arguments* args); void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
v8::Local<v8::Value> GetNativeWindowHandle();
#if defined(OS_WIN) #if defined(OS_WIN)
typedef base::Callback<void(v8::Local<v8::Value>, typedef base::Callback<void(v8::Local<v8::Value>,

View file

@ -1,72 +0,0 @@
{deprecate, session, Menu} = require 'electron'
{EventEmitter} = require 'events'
bindings = process.atomBinding 'app'
downloadItemBindings = process.atomBinding 'download_item'
app = bindings.app
app.__proto__ = EventEmitter.prototype
app.setApplicationMenu = (menu) ->
Menu.setApplicationMenu menu
app.getApplicationMenu = ->
Menu.getApplicationMenu()
app.commandLine =
appendSwitch: bindings.appendSwitch,
appendArgument: bindings.appendArgument
if process.platform is 'darwin'
app.dock =
bounce: (type='informational') -> bindings.dockBounce type
cancelBounce: bindings.dockCancelBounce
setBadge: bindings.dockSetBadgeText
getBadge: bindings.dockGetBadgeText
hide: bindings.dockHide
show: bindings.dockShow
setMenu: bindings.dockSetMenu
appPath = null
app.setAppPath = (path) ->
appPath = path
app.getAppPath = ->
appPath
# Routes the events to webContents.
for name in ['login', 'certificate-error', 'select-client-certificate']
do (name) ->
app.on name, (event, webContents, args...) ->
webContents.emit name, event, args...
# Deprecated.
app.getHomeDir = deprecate 'app.getHomeDir', 'app.getPath', ->
@getPath 'home'
app.getDataPath = deprecate 'app.getDataPath', 'app.getPath', ->
@getPath 'userData'
app.setDataPath = deprecate 'app.setDataPath', 'app.setPath', (path) ->
@setPath 'userData', path
app.resolveProxy = deprecate 'app.resolveProxy', 'session.defaultSession.resolveProxy', (url, callback) ->
session.defaultSession.resolveProxy url, callback
deprecate.rename app, 'terminate', 'quit'
deprecate.event app, 'finish-launching', 'ready', ->
setImmediate => # give default app a chance to setup default menu.
@emit 'finish-launching'
deprecate.event app, 'activate-with-no-open-windows', 'activate', (event, hasVisibleWindows) ->
@emit 'activate-with-no-open-windows', event if not hasVisibleWindows
deprecate.event app, 'select-certificate', 'select-client-certificate'
# Wrappers for native classes.
wrapDownloadItem = (downloadItem) ->
# downloadItem is an EventEmitter.
downloadItem.__proto__ = EventEmitter.prototype
# Deprecated.
deprecate.property downloadItem, 'url', 'getURL'
deprecate.property downloadItem, 'filename', 'getFilename'
deprecate.property downloadItem, 'mimeType', 'getMimeType'
deprecate.rename downloadItem, 'getUrl', 'getURL'
downloadItemBindings._setWrapDownloadItem wrapDownloadItem
# Only one App object pemitted.
module.exports = app

123
atom/browser/api/lib/app.js Normal file
View file

@ -0,0 +1,123 @@
const deprecate = require('electron').deprecate;
const session = require('electron').session;
const Menu = require('electron').Menu;
const EventEmitter = require('events').EventEmitter;
const bindings = process.atomBinding('app');
const downloadItemBindings = process.atomBinding('download_item');
const app = bindings.app;
var slice = [].slice;
app.__proto__ = EventEmitter.prototype;
app.setApplicationMenu = function(menu) {
return Menu.setApplicationMenu(menu);
};
app.getApplicationMenu = function() {
return Menu.getApplicationMenu();
};
app.commandLine = {
appendSwitch: bindings.appendSwitch,
appendArgument: bindings.appendArgument
};
if (process.platform === 'darwin') {
app.dock = {
bounce: function(type) {
if (type == null) {
type = 'informational';
}
return bindings.dockBounce(type);
},
cancelBounce: bindings.dockCancelBounce,
setBadge: bindings.dockSetBadgeText,
getBadge: bindings.dockGetBadgeText,
hide: bindings.dockHide,
show: bindings.dockShow,
setMenu: bindings.dockSetMenu
};
}
var appPath = null;
app.setAppPath = function(path) {
return appPath = path;
};
app.getAppPath = function() {
return appPath;
};
// Routes the events to webContents.
var ref1 = ['login', 'certificate-error', 'select-client-certificate'];
var fn = function(name) {
return app.on(name, function() {
var args, event, webContents;
event = arguments[0], webContents = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return webContents.emit.apply(webContents, [name, event].concat(slice.call(args)));
});
};
var i, len, name;
for (i = 0, len = ref1.length; i < len; i++) {
name = ref1[i];
fn(ref1[i]);
}
// Deprecated.
app.getHomeDir = deprecate('app.getHomeDir', 'app.getPath', function() {
return this.getPath('home');
});
app.getDataPath = deprecate('app.getDataPath', 'app.getPath', function() {
return this.getPath('userData');
});
app.setDataPath = deprecate('app.setDataPath', 'app.setPath', function(path) {
return this.setPath('userData', path);
});
app.resolveProxy = deprecate('app.resolveProxy', 'session.defaultSession.resolveProxy', function(url, callback) {
return session.defaultSession.resolveProxy(url, callback);
});
deprecate.rename(app, 'terminate', 'quit');
deprecate.event(app, 'finish-launching', 'ready', function() {
// give default app a chance to setup default menu.
return setImmediate((function(_this) {
return function() {
return _this.emit('finish-launching');
};
})(this));
});
deprecate.event(app, 'activate-with-no-open-windows', 'activate', function(event, hasVisibleWindows) {
if (!hasVisibleWindows) {
return this.emit('activate-with-no-open-windows', event);
}
});
deprecate.event(app, 'select-certificate', 'select-client-certificate');
// Wrappers for native classes.
var wrapDownloadItem = function(downloadItem) {
// downloadItem is an EventEmitter.
downloadItem.__proto__ = EventEmitter.prototype;
// Deprecated.
deprecate.property(downloadItem, 'url', 'getURL');
deprecate.property(downloadItem, 'filename', 'getFilename');
deprecate.property(downloadItem, 'mimeType', 'getMimeType');
return deprecate.rename(downloadItem, 'getUrl', 'getURL');
};
downloadItemBindings._setWrapDownloadItem(wrapDownloadItem);
// Only one App object pemitted.
module.exports = app;

View file

@ -1,12 +0,0 @@
{deprecate} = require 'electron'
autoUpdater =
if process.platform is 'win32'
require './auto-updater/auto-updater-win'
else
require './auto-updater/auto-updater-native'
# Deprecated.
deprecate.rename autoUpdater, 'setFeedUrl', 'setFeedURL'
module.exports = autoUpdater

View file

@ -0,0 +1,7 @@
const deprecate = require('electron').deprecate;
const autoUpdater = process.platform === 'win32' ? require('./auto-updater/auto-updater-win') : require('./auto-updater/auto-updater-native');
// Deprecated.
deprecate.rename(autoUpdater, 'setFeedUrl', 'setFeedURL');
module.exports = autoUpdater;

View file

@ -1,6 +0,0 @@
{EventEmitter} = require 'events'
{autoUpdater} = process.atomBinding 'auto_updater'
autoUpdater.__proto__ = EventEmitter.prototype
module.exports = autoUpdater

View file

@ -0,0 +1,6 @@
const EventEmitter = require('events').EventEmitter;
const autoUpdater = process.atomBinding('auto_updater').autoUpdater;
autoUpdater.__proto__ = EventEmitter.prototype;
module.exports = autoUpdater;

View file

@ -1,42 +0,0 @@
{app} = require 'electron'
{EventEmitter} = require 'events'
url = require 'url'
squirrelUpdate = require './squirrel-update-win'
class AutoUpdater extends EventEmitter
quitAndInstall: ->
squirrelUpdate.processStart()
app.quit()
setFeedURL: (updateURL) ->
@updateURL = updateURL
checkForUpdates: ->
return @emitError 'Update URL is not set' unless @updateURL
return @emitError 'Can not find Squirrel' unless squirrelUpdate.supported()
@emit 'checking-for-update'
squirrelUpdate.download @updateURL, (error, update) =>
return @emitError error if error?
return @emit 'update-not-available' unless update?
@emit 'update-available'
squirrelUpdate.update @updateURL, (error) =>
return @emitError error if error?
{releaseNotes, version} = update
# Following information is not available on Windows, so fake them.
date = new Date
url = @updateURL
@emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall()
# Private: Emit both error object and message, this is to keep compatibility
# with Old APIs.
emitError: (message) ->
@emit 'error', new Error(message), message
module.exports = new AutoUpdater

View file

@ -0,0 +1,71 @@
const app = require('electron').app;
const EventEmitter = require('events').EventEmitter;
const url = require('url');
const squirrelUpdate = require('./squirrel-update-win');
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
var AutoUpdater = (function(superClass) {
extend(AutoUpdater, superClass);
function AutoUpdater() {
return AutoUpdater.__super__.constructor.apply(this, arguments);
}
AutoUpdater.prototype.quitAndInstall = function() {
squirrelUpdate.processStart();
return app.quit();
};
AutoUpdater.prototype.setFeedURL = function(updateURL) {
return this.updateURL = updateURL;
};
AutoUpdater.prototype.checkForUpdates = function() {
if (!this.updateURL) {
return this.emitError('Update URL is not set');
}
if (!squirrelUpdate.supported()) {
return this.emitError('Can not find Squirrel');
}
this.emit('checking-for-update');
return squirrelUpdate.download(this.updateURL, (function(_this) {
return function(error, update) {
if (error != null) {
return _this.emitError(error);
}
if (update == null) {
return _this.emit('update-not-available');
}
_this.emit('update-available');
return squirrelUpdate.update(_this.updateURL, function(error) {
var date, releaseNotes, version;
if (error != null) {
return _this.emitError(error);
}
releaseNotes = update.releaseNotes, version = update.version;
// Following information is not available on Windows, so fake them.
date = new Date;
url = _this.updateURL;
return _this.emit('update-downloaded', {}, releaseNotes, version, date, url, function() {
return _this.quitAndInstall();
});
});
};
})(this));
};
// Private: Emit both error object and message, this is to keep compatibility
// with Old APIs.
AutoUpdater.prototype.emitError = function(message) {
return this.emit('error', new Error(message), message);
};
return AutoUpdater;
})(EventEmitter);
module.exports = new AutoUpdater;

View file

@ -1,67 +0,0 @@
fs = require 'fs'
path = require 'path'
{spawn} = require 'child_process'
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
exeName = path.basename process.execPath
# Spawn a command and invoke the callback when it completes with an error
# and the output from standard out.
spawnUpdate = (args, detached, callback) ->
try
spawnedProcess = spawn updateExe, args, {detached}
catch error
# Shouldn't happen, but still guard it.
process.nextTick -> callback error
return
stdout = ''
stderr = ''
spawnedProcess.stdout.on 'data', (data) -> stdout += data
spawnedProcess.stderr.on 'data', (data) -> stderr += data
errorEmitted = false
spawnedProcess.on 'error', (error) ->
errorEmitted = true
callback error
spawnedProcess.on 'exit', (code, signal) ->
# We may have already emitted an error.
return if errorEmitted
# Process terminated with error.
if code isnt 0
return callback "Command failed: #{signal ? code}\n#{stderr}"
# Success.
callback null, stdout
# Start an instance of the installed app.
exports.processStart = (callback) ->
spawnUpdate ['--processStart', exeName], true, ->
# Download the releases specified by the URL and write new results to stdout.
exports.download = (updateURL, callback) ->
spawnUpdate ['--download', updateURL], false, (error, stdout) ->
return callback(error) if error?
try
# Last line of output is the JSON details about the releases
json = stdout.trim().split('\n').pop()
update = JSON.parse(json)?.releasesToApply?.pop?()
catch
return callback "Invalid result:\n#{stdout}"
callback null, update
# Update the application to the latest remote version specified by URL.
exports.update = (updateURL, callback) ->
spawnUpdate ['--update', updateURL], false, callback
# Is the Update.exe installed with the current application?
exports.supported = ->
try
fs.accessSync updateExe, fs.R_OK
return true
catch
return false

View file

@ -0,0 +1,100 @@
const fs = require('fs');
const path = require('path');
const spawn = require('child_process').spawn;
// i.e. my-app/app-0.1.13/
const appFolder = path.dirname(process.execPath);
// i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
const exeName = path.basename(process.execPath);
// Spawn a command and invoke the callback when it completes with an error
// and the output from standard out.
var spawnUpdate = function(args, detached, callback) {
var error, error1, errorEmitted, spawnedProcess, stderr, stdout;
try {
spawnedProcess = spawn(updateExe, args, {
detached: detached
});
} catch (error1) {
error = error1;
// Shouldn't happen, but still guard it.
process.nextTick(function() {
return callback(error);
});
return;
}
stdout = '';
stderr = '';
spawnedProcess.stdout.on('data', function(data) {
return stdout += data;
});
spawnedProcess.stderr.on('data', function(data) {
return stderr += data;
});
errorEmitted = false;
spawnedProcess.on('error', function(error) {
errorEmitted = true;
return callback(error);
});
return spawnedProcess.on('exit', function(code, signal) {
// We may have already emitted an error.
if (errorEmitted) {
return;
}
// Process terminated with error.
if (code !== 0) {
return callback("Command failed: " + (signal != null ? signal : code) + "\n" + stderr);
}
// Success.
return callback(null, stdout);
});
};
// Start an instance of the installed app.
exports.processStart = function(callback) {
return spawnUpdate(['--processStart', exeName], true, function() {});
};
// Download the releases specified by the URL and write new results to stdout.
exports.download = function(updateURL, callback) {
return spawnUpdate(['--download', updateURL], false, function(error, stdout) {
var error1, json, ref, ref1, update;
if (error != null) {
return callback(error);
}
try {
// Last line of output is the JSON details about the releases
json = stdout.trim().split('\n').pop();
update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === "function" ? ref1.pop() : void 0 : void 0 : void 0;
} catch (error1) {
return callback("Invalid result:\n" + stdout);
}
return callback(null, update);
});
};
// Update the application to the latest remote version specified by URL.
exports.update = function(updateURL, callback) {
return spawnUpdate(['--update', updateURL], false, callback);
};
// Is the Update.exe installed with the current application?
exports.supported = function() {
var error1;
try {
fs.accessSync(updateExe, fs.R_OK);
return true;
} catch (error1) {
return false;
}
};

View file

@ -1,113 +0,0 @@
{ipcMain, deprecate} = require 'electron'
{EventEmitter} = require 'events'
{BrowserWindow} = process.atomBinding 'window'
BrowserWindow::__proto__ = EventEmitter.prototype
BrowserWindow::_init = ->
{app} = require 'electron' # avoid recursive require.
# Simulate the application menu on platforms other than OS X.
if process.platform isnt 'darwin'
menu = app.getApplicationMenu()
@setMenu menu if menu?
# Make new windows requested by links behave like "window.open"
@webContents.on '-new-window', (event, url, frameName) ->
options = show: true, width: 800, height: 600
ipcMain.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
# window.resizeTo(...)
# window.moveTo(...)
@webContents.on 'move', (event, size) =>
@setBounds size
# Hide the auto-hide menu when webContents is focused.
@webContents.on 'activate', =>
if process.platform isnt 'darwin' and @isMenuBarAutoHide() and @isMenuBarVisible()
@setMenuBarVisibility false
# Forward the crashed event.
@webContents.on 'crashed', =>
@emit 'crashed'
# Change window title to page title.
@webContents.on 'page-title-updated', (event, title, explicitSet) =>
@emit 'page-title-updated', event, title
@setTitle title unless event.defaultPrevented
# Sometimes the webContents doesn't get focus when window is shown, so we have
# to force focusing on webContents in this case. The safest way is to focus it
# when we first start to load URL, if we do it earlier it won't have effect,
# if we do it later we might move focus in the page.
# Though this hack is only needed on OS X when the app is launched from
# Finder, we still do it on all platforms in case of other bugs we don't know.
@webContents.once 'load-url', ->
@focus()
# Redirect focus/blur event to app instance too.
@on 'blur', (event) =>
app.emit 'browser-window-blur', event, this
@on 'focus', (event) =>
app.emit 'browser-window-focus', event, this
# Notify the creation of the window.
app.emit 'browser-window-created', {}, this
# Be compatible with old APIs.
@webContents.on 'devtools-focused', => @emit 'devtools-focused'
@webContents.on 'devtools-opened', => @emit 'devtools-opened'
@webContents.on 'devtools-closed', => @emit 'devtools-closed'
Object.defineProperty this, 'devToolsWebContents',
enumerable: true,
configurable: false,
get: -> @webContents.devToolsWebContents
BrowserWindow.getFocusedWindow = ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.isFocused()
BrowserWindow.fromWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.webContents?.equal webContents
BrowserWindow.fromDevToolsWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.devToolsWebContents?.equal webContents
# Helpers.
BrowserWindow::loadURL = -> @webContents.loadURL.apply @webContents, arguments
BrowserWindow::getURL = -> @webContents.getURL()
BrowserWindow::reload = -> @webContents.reload.apply @webContents, arguments
BrowserWindow::send = -> @webContents.send.apply @webContents, arguments
BrowserWindow::openDevTools = -> @webContents.openDevTools.apply @webContents, arguments
BrowserWindow::closeDevTools = -> @webContents.closeDevTools()
BrowserWindow::isDevToolsOpened = -> @webContents.isDevToolsOpened()
BrowserWindow::isDevToolsFocused = -> @webContents.isDevToolsFocused()
BrowserWindow::toggleDevTools = -> @webContents.toggleDevTools()
BrowserWindow::inspectElement = -> @webContents.inspectElement.apply @webContents, arguments
BrowserWindow::inspectServiceWorker = -> @webContents.inspectServiceWorker()
# Deprecated.
deprecate.member BrowserWindow, 'undo', 'webContents'
deprecate.member BrowserWindow, 'redo', 'webContents'
deprecate.member BrowserWindow, 'cut', 'webContents'
deprecate.member BrowserWindow, 'copy', 'webContents'
deprecate.member BrowserWindow, 'paste', 'webContents'
deprecate.member BrowserWindow, 'selectAll', 'webContents'
deprecate.member BrowserWindow, 'reloadIgnoringCache', 'webContents'
deprecate.member BrowserWindow, 'isLoading', 'webContents'
deprecate.member BrowserWindow, 'isWaitingForResponse', 'webContents'
deprecate.member BrowserWindow, 'stop', 'webContents'
deprecate.member BrowserWindow, 'isCrashed', 'webContents'
deprecate.member BrowserWindow, 'print', 'webContents'
deprecate.member BrowserWindow, 'printToPDF', 'webContents'
deprecate.rename BrowserWindow, 'restart', 'reload'
deprecate.rename BrowserWindow, 'loadUrl', 'loadURL'
deprecate.rename BrowserWindow, 'getUrl', 'getURL'
BrowserWindow::executeJavaScriptInDevTools = deprecate 'executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', (code) ->
@devToolsWebContents?.executeJavaScript code
BrowserWindow::getPageTitle = deprecate 'getPageTitle', 'webContents.getTitle', ->
@webContents?.getTitle()
module.exports = BrowserWindow

View file

@ -0,0 +1,241 @@
const ipcMain = require('electron').ipcMain;
const deprecate = require('electron').deprecate;
const EventEmitter = require('events').EventEmitter;
const BrowserWindow = process.atomBinding('window').BrowserWindow;
BrowserWindow.prototype.__proto__ = EventEmitter.prototype;
BrowserWindow.prototype._init = function() {
// avoid recursive require.
var app, menu;
app = require('electron').app;
// Simulate the application menu on platforms other than OS X.
if (process.platform !== 'darwin') {
menu = app.getApplicationMenu();
if (menu != null) {
this.setMenu(menu);
}
}
// Make new windows requested by links behave like "window.open"
this.webContents.on('-new-window', function(event, url, frameName) {
var options;
options = {
show: true,
width: 800,
height: 600
};
return ipcMain.emit('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options);
});
// window.resizeTo(...)
// window.moveTo(...)
this.webContents.on('move', (function(_this) {
return function(event, size) {
return _this.setBounds(size);
};
})(this));
// Hide the auto-hide menu when webContents is focused.
this.webContents.on('activate', (function(_this) {
return function() {
if (process.platform !== 'darwin' && _this.isMenuBarAutoHide() && _this.isMenuBarVisible()) {
return _this.setMenuBarVisibility(false);
}
};
})(this));
// Forward the crashed event.
this.webContents.on('crashed', (function(_this) {
return function() {
return _this.emit('crashed');
};
})(this));
// Change window title to page title.
this.webContents.on('page-title-updated', (function(_this) {
return function(event, title, explicitSet) {
_this.emit('page-title-updated', event, title);
if (!event.defaultPrevented) {
return _this.setTitle(title);
}
};
})(this));
// Sometimes the webContents doesn't get focus when window is shown, so we have
// to force focusing on webContents in this case. The safest way is to focus it
// when we first start to load URL, if we do it earlier it won't have effect,
// if we do it later we might move focus in the page.
// Though this hack is only needed on OS X when the app is launched from
// Finder, we still do it on all platforms in case of other bugs we don't know.
this.webContents.once('load-url', function() {
return this.focus();
});
// Redirect focus/blur event to app instance too.
this.on('blur', (function(_this) {
return function(event) {
return app.emit('browser-window-blur', event, _this);
};
})(this));
this.on('focus', (function(_this) {
return function(event) {
return app.emit('browser-window-focus', event, _this);
};
})(this));
// Notify the creation of the window.
app.emit('browser-window-created', {}, this);
// Be compatible with old APIs.
this.webContents.on('devtools-focused', (function(_this) {
return function() {
return _this.emit('devtools-focused');
};
})(this));
this.webContents.on('devtools-opened', (function(_this) {
return function() {
return _this.emit('devtools-opened');
};
})(this));
this.webContents.on('devtools-closed', (function(_this) {
return function() {
return _this.emit('devtools-closed');
};
})(this));
return Object.defineProperty(this, 'devToolsWebContents', {
enumerable: true,
configurable: false,
get: function() {
return this.webContents.devToolsWebContents;
}
});
};
BrowserWindow.getFocusedWindow = function() {
var i, len, window, windows;
windows = BrowserWindow.getAllWindows();
for (i = 0, len = windows.length; i < len; i++) {
window = windows[i];
if (window.isFocused()) {
return window;
}
}
return null;
};
BrowserWindow.fromWebContents = function(webContents) {
var i, len, ref1, window, windows;
windows = BrowserWindow.getAllWindows();
for (i = 0, len = windows.length; i < len; i++) {
window = windows[i];
if ((ref1 = window.webContents) != null ? ref1.equal(webContents) : void 0) {
return window;
}
}
};
BrowserWindow.fromDevToolsWebContents = function(webContents) {
var i, len, ref1, window, windows;
windows = BrowserWindow.getAllWindows();
for (i = 0, len = windows.length; i < len; i++) {
window = windows[i];
if ((ref1 = window.devToolsWebContents) != null ? ref1.equal(webContents) : void 0) {
return window;
}
}
};
// Helpers.
BrowserWindow.prototype.loadURL = function() {
return this.webContents.loadURL.apply(this.webContents, arguments);
};
BrowserWindow.prototype.getURL = function() {
return this.webContents.getURL();
};
BrowserWindow.prototype.reload = function() {
return this.webContents.reload.apply(this.webContents, arguments);
};
BrowserWindow.prototype.send = function() {
return this.webContents.send.apply(this.webContents, arguments);
};
BrowserWindow.prototype.openDevTools = function() {
return this.webContents.openDevTools.apply(this.webContents, arguments);
};
BrowserWindow.prototype.closeDevTools = function() {
return this.webContents.closeDevTools();
};
BrowserWindow.prototype.isDevToolsOpened = function() {
return this.webContents.isDevToolsOpened();
};
BrowserWindow.prototype.isDevToolsFocused = function() {
return this.webContents.isDevToolsFocused();
};
BrowserWindow.prototype.toggleDevTools = function() {
return this.webContents.toggleDevTools();
};
BrowserWindow.prototype.inspectElement = function() {
return this.webContents.inspectElement.apply(this.webContents, arguments);
};
BrowserWindow.prototype.inspectServiceWorker = function() {
return this.webContents.inspectServiceWorker();
};
// Deprecated.
deprecate.member(BrowserWindow, 'undo', 'webContents');
deprecate.member(BrowserWindow, 'redo', 'webContents');
deprecate.member(BrowserWindow, 'cut', 'webContents');
deprecate.member(BrowserWindow, 'copy', 'webContents');
deprecate.member(BrowserWindow, 'paste', 'webContents');
deprecate.member(BrowserWindow, 'selectAll', 'webContents');
deprecate.member(BrowserWindow, 'reloadIgnoringCache', 'webContents');
deprecate.member(BrowserWindow, 'isLoading', 'webContents');
deprecate.member(BrowserWindow, 'isWaitingForResponse', 'webContents');
deprecate.member(BrowserWindow, 'stop', 'webContents');
deprecate.member(BrowserWindow, 'isCrashed', 'webContents');
deprecate.member(BrowserWindow, 'print', 'webContents');
deprecate.member(BrowserWindow, 'printToPDF', 'webContents');
deprecate.rename(BrowserWindow, 'restart', 'reload');
deprecate.rename(BrowserWindow, 'loadUrl', 'loadURL');
deprecate.rename(BrowserWindow, 'getUrl', 'getURL');
BrowserWindow.prototype.executeJavaScriptInDevTools = deprecate('executeJavaScriptInDevTools', 'devToolsWebContents.executeJavaScript', function(code) {
var ref1;
return (ref1 = this.devToolsWebContents) != null ? ref1.executeJavaScript(code) : void 0;
});
BrowserWindow.prototype.getPageTitle = deprecate('getPageTitle', 'webContents.getTitle', function() {
var ref1;
return (ref1 = this.webContents) != null ? ref1.getTitle() : void 0;
});
module.exports = BrowserWindow;

View file

@ -1 +0,0 @@
module.exports = process.atomBinding 'content_tracing'

View file

@ -0,0 +1 @@
module.exports = process.atomBinding('content_tracing');

View file

@ -1,125 +0,0 @@
{app, BrowserWindow} = require 'electron'
binding = process.atomBinding 'dialog'
v8Util = process.atomBinding 'v8_util'
fileDialogProperties =
openFile: 1 << 0
openDirectory: 1 << 1
multiSelections: 1 << 2
createDirectory: 1 << 3
messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']
messageBoxOptions =
noLink: 1 << 0
parseArgs = (window, options, callback) ->
unless window is null or window?.constructor is BrowserWindow
# Shift.
callback = options
options = window
window = null
if not callback? and typeof options is 'function'
# Shift.
callback = options
options = null
[window, options, callback]
checkAppInitialized = ->
throw new Error('dialog module can only be used after app is ready') unless app.isReady()
module.exports =
showOpenDialog: (args...) ->
checkAppInitialized()
[window, options, callback] = parseArgs args...
options ?= title: 'Open', properties: ['openFile']
options.properties ?= ['openFile']
throw new TypeError('Properties need to be array') unless Array.isArray options.properties
properties = 0
for prop, value of fileDialogProperties
properties |= value if prop in options.properties
options.title ?= ''
options.defaultPath ?= ''
options.filters ?= []
wrappedCallback =
if typeof callback is 'function'
(success, result) -> callback(if success then result)
else
null
binding.showOpenDialog String(options.title),
String(options.defaultPath),
options.filters
properties,
window,
wrappedCallback
showSaveDialog: (args...) ->
checkAppInitialized()
[window, options, callback] = parseArgs args...
options ?= title: 'Save'
options.title ?= ''
options.defaultPath ?= ''
options.filters ?= []
wrappedCallback =
if typeof callback is 'function'
(success, result) -> callback(if success then result)
else
null
binding.showSaveDialog String(options.title),
String(options.defaultPath),
options.filters
window,
wrappedCallback
showMessageBox: (args...) ->
checkAppInitialized()
[window, options, callback] = parseArgs args...
options ?= type: 'none'
options.type ?= 'none'
messageBoxType = messageBoxTypes.indexOf options.type
throw new TypeError('Invalid message box type') unless messageBoxType > -1
throw new TypeError('Buttons need to be array') unless Array.isArray options.buttons
options.title ?= ''
options.message ?= ''
options.detail ?= ''
options.icon ?= null
# Choose a default button to get selected when dialog is cancelled.
unless options.cancelId?
options.cancelId = 0
for text, i in options.buttons
if text.toLowerCase() in ['cancel', 'no']
options.cancelId = i
break
flags = if options.noLink then messageBoxOptions.noLink else 0
binding.showMessageBox messageBoxType,
options.buttons,
options.cancelId,
flags,
options.title,
options.message,
options.detail,
options.icon,
window,
callback
showErrorBox: (args...) ->
binding.showErrorBox args...
# Mark standard asynchronous functions.
for api in ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
v8Util.setHiddenValue module.exports[api], 'asynchronous', true

View file

@ -0,0 +1,170 @@
const app = require('electron').app;
const BrowserWindow = require('electron').BrowserWindow;
const binding = process.atomBinding('dialog');
const v8Util = process.atomBinding('v8_util');
var slice = [].slice;
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
var fileDialogProperties = {
openFile: 1 << 0,
openDirectory: 1 << 1,
multiSelections: 1 << 2,
createDirectory: 1 << 3
};
var messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'];
var messageBoxOptions = {
noLink: 1 << 0
};
var parseArgs = function(window, options, callback) {
if (!(window === null || (window != null ? window.constructor : void 0) === BrowserWindow)) {
// Shift.
callback = options;
options = window;
window = null;
}
if ((callback == null) && typeof options === 'function') {
// Shift.
callback = options;
options = null;
}
return [window, options, callback];
};
var checkAppInitialized = function() {
if (!app.isReady()) {
throw new Error('dialog module can only be used after app is ready');
}
};
module.exports = {
showOpenDialog: function() {
var args, callback, options, prop, properties, ref1, value, window, wrappedCallback;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
checkAppInitialized();
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
if (options == null) {
options = {
title: 'Open',
properties: ['openFile']
};
}
if (options.properties == null) {
options.properties = ['openFile'];
}
if (!Array.isArray(options.properties)) {
throw new TypeError('Properties need to be array');
}
properties = 0;
for (prop in fileDialogProperties) {
value = fileDialogProperties[prop];
if (indexOf.call(options.properties, prop) >= 0) {
properties |= value;
}
}
if (options.title == null) {
options.title = '';
}
if (options.defaultPath == null) {
options.defaultPath = '';
}
if (options.filters == null) {
options.filters = [];
}
wrappedCallback = typeof callback === 'function' ? function(success, result) {
return callback(success ? result : void 0);
} : null;
return binding.showOpenDialog(String(options.title), String(options.defaultPath), options.filters, properties, window, wrappedCallback);
},
showSaveDialog: function() {
var args, callback, options, ref1, window, wrappedCallback;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
checkAppInitialized();
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
if (options == null) {
options = {
title: 'Save'
};
}
if (options.title == null) {
options.title = '';
}
if (options.defaultPath == null) {
options.defaultPath = '';
}
if (options.filters == null) {
options.filters = [];
}
wrappedCallback = typeof callback === 'function' ? function(success, result) {
return callback(success ? result : void 0);
} : null;
return binding.showSaveDialog(String(options.title), String(options.defaultPath), options.filters, window, wrappedCallback);
},
showMessageBox: function() {
var args, callback, flags, i, j, len, messageBoxType, options, ref1, ref2, ref3, text, window;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
checkAppInitialized();
ref1 = parseArgs.apply(null, args), window = ref1[0], options = ref1[1], callback = ref1[2];
if (options == null) {
options = {
type: 'none'
};
}
if (options.type == null) {
options.type = 'none';
}
messageBoxType = messageBoxTypes.indexOf(options.type);
if (!(messageBoxType > -1)) {
throw new TypeError('Invalid message box type');
}
if (!Array.isArray(options.buttons)) {
throw new TypeError('Buttons need to be array');
}
if (options.title == null) {
options.title = '';
}
if (options.message == null) {
options.message = '';
}
if (options.detail == null) {
options.detail = '';
}
if (options.icon == null) {
options.icon = null;
}
if (options.defaultId == null) {
options.defaultId = -1;
}
// Choose a default button to get selected when dialog is cancelled.
if (options.cancelId == null) {
options.cancelId = 0;
ref2 = options.buttons;
for (i = j = 0, len = ref2.length; j < len; i = ++j) {
text = ref2[i];
if ((ref3 = text.toLowerCase()) === 'cancel' || ref3 === 'no') {
options.cancelId = i;
break;
}
}
}
flags = options.noLink ? messageBoxOptions.noLink : 0;
return binding.showMessageBox(messageBoxType, options.buttons, options.defaultId, options.cancelId, flags, options.title, options.message, options.detail, options.icon, window, callback);
},
showErrorBox: function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return binding.showErrorBox.apply(binding, args);
}
};
// Mark standard asynchronous functions.
var ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog'];
var j, len, api;
for (j = 0, len = ref1.length; j < len; j++) {
api = ref1[j];
v8Util.setHiddenValue(module.exports[api], 'asynchronous', true);
}

View file

@ -1,57 +0,0 @@
common = require '../../../../common/api/lib/exports/electron'
# Import common modules.
common.defineProperties exports
Object.defineProperties exports,
# Browser side modules, please sort with alphabet order.
app:
enumerable: true
get: -> require '../app'
autoUpdater:
enumerable: true
get: -> require '../auto-updater'
BrowserWindow:
enumerable: true
get: -> require '../browser-window'
contentTracing:
enumerable: true
get: -> require '../content-tracing'
dialog:
enumerable: true
get: -> require '../dialog'
ipcMain:
enumerable: true
get: -> require '../ipc-main'
globalShortcut:
enumerable: true
get: -> require '../global-shortcut'
Menu:
enumerable: true
get: -> require '../menu'
MenuItem:
enumerable: true
get: -> require '../menu-item'
powerMonitor:
enumerable: true
get: -> require '../power-monitor'
powerSaveBlocker:
enumerable: true
get: -> require '../power-save-blocker'
protocol:
enumerable: true
get: -> require '../protocol'
screen:
enumerable: true
get: -> require '../screen'
session:
enumerable: true
get: -> require '../session'
Tray:
enumerable: true
get: -> require '../tray'
# The internal modules, invisible unless you know their names.
NavigationController:
get: -> require '../navigation-controller'
webContents:
get: -> require '../web-contents'

View file

@ -0,0 +1,112 @@
const common = require('../../../../common/api/lib/exports/electron');
// Import common modules.
common.defineProperties(exports);
Object.defineProperties(exports, {
// Browser side modules, please sort with alphabet order.
app: {
enumerable: true,
get: function() {
return require('../app');
}
},
autoUpdater: {
enumerable: true,
get: function() {
return require('../auto-updater');
}
},
BrowserWindow: {
enumerable: true,
get: function() {
return require('../browser-window');
}
},
contentTracing: {
enumerable: true,
get: function() {
return require('../content-tracing');
}
},
dialog: {
enumerable: true,
get: function() {
return require('../dialog');
}
},
ipcMain: {
enumerable: true,
get: function() {
return require('../ipc-main');
}
},
globalShortcut: {
enumerable: true,
get: function() {
return require('../global-shortcut');
}
},
Menu: {
enumerable: true,
get: function() {
return require('../menu');
}
},
MenuItem: {
enumerable: true,
get: function() {
return require('../menu-item');
}
},
powerMonitor: {
enumerable: true,
get: function() {
return require('../power-monitor');
}
},
powerSaveBlocker: {
enumerable: true,
get: function() {
return require('../power-save-blocker');
}
},
protocol: {
enumerable: true,
get: function() {
return require('../protocol');
}
},
screen: {
enumerable: true,
get: function() {
return require('../screen');
}
},
session: {
enumerable: true,
get: function() {
return require('../session');
}
},
Tray: {
enumerable: true,
get: function() {
return require('../tray');
}
},
// The internal modules, invisible unless you know their names.
NavigationController: {
get: function() {
return require('../navigation-controller');
}
},
webContents: {
get: function() {
return require('../web-contents');
}
}
});

View file

@ -1,3 +0,0 @@
{globalShortcut} = process.atomBinding 'global_shortcut'
module.exports = globalShortcut

View file

@ -0,0 +1,5 @@
var globalShortcut;
globalShortcut = process.atomBinding('global_shortcut').globalShortcut;
module.exports = globalShortcut;

View file

@ -1,3 +0,0 @@
{EventEmitter} = require 'events'
module.exports = new EventEmitter

View file

@ -0,0 +1,3 @@
const EventEmitter = require('events').EventEmitter;
module.exports = new EventEmitter;

View file

@ -1,6 +0,0 @@
{deprecate, ipcMain} = require 'electron'
# This module is deprecated, we mirror everything from ipcMain.
deprecate.warn 'ipc module', 'require("electron").ipcMain'
module.exports = ipcMain

View file

@ -0,0 +1,7 @@
const deprecate = require('electron').deprecate;
const ipcMain = require('electron').ipcMain;
// This module is deprecated, we mirror everything from ipcMain.
deprecate.warn('ipc module', 'require("electron").ipcMain');
module.exports = ipcMain;

View file

@ -1,73 +0,0 @@
v8Util = process.atomBinding 'v8_util'
nextCommandId = 0
# Maps role to methods of webContents
rolesMap =
undo: 'undo'
redo: 'redo'
cut: 'cut'
copy: 'copy'
paste: 'paste'
selectall: 'selectAll'
minimize: 'minimize'
close: 'close'
# Maps methods that should be called directly on the BrowserWindow instance
methodInBrowserWindow =
minimize: true
close: true
class MenuItem
@types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']
constructor: (options) ->
{Menu} = require 'electron'
{click, @selector, @type, @role, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options
if @submenu? and @submenu.constructor isnt Menu
@submenu = Menu.buildFromTemplate @submenu
@type = 'submenu' if not @type? and @submenu?
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
@overrideReadOnlyProperty 'type', 'normal'
@overrideReadOnlyProperty 'role'
@overrideReadOnlyProperty 'accelerator'
@overrideReadOnlyProperty 'icon'
@overrideReadOnlyProperty 'submenu'
@overrideProperty 'label', ''
@overrideProperty 'sublabel', ''
@overrideProperty 'enabled', true
@overrideProperty 'visible', true
@overrideProperty 'checked', false
throw new Error("Unknown menu type #{@type}") if MenuItem.types.indexOf(@type) is -1
@commandId = ++nextCommandId
@click = (focusedWindow) =>
# Manually flip the checked flags when clicked.
@checked = !@checked if @type in ['checkbox', 'radio']
if @role and rolesMap[@role] and process.platform isnt 'darwin' and focusedWindow?
methodName = rolesMap[@role]
if methodInBrowserWindow[methodName]
focusedWindow[methodName]()
else
focusedWindow.webContents?[methodName]()
else if typeof click is 'function'
click this, focusedWindow
else if typeof @selector is 'string'
Menu.sendActionToFirstResponder @selector
overrideProperty: (name, defaultValue=null) ->
this[name] ?= defaultValue
overrideReadOnlyProperty: (name, defaultValue=null) ->
this[name] ?= defaultValue
Object.defineProperty this, name,
enumerable: true
writable: false
value: this[name]
module.exports = MenuItem

View file

@ -0,0 +1,104 @@
var MenuItem, methodInBrowserWindow, nextCommandId, rolesMap, v8Util;
v8Util = process.atomBinding('v8_util');
nextCommandId = 0;
// Maps role to methods of webContents
rolesMap = {
undo: 'undo',
redo: 'redo',
cut: 'cut',
copy: 'copy',
paste: 'paste',
selectall: 'selectAll',
minimize: 'minimize',
close: 'close'
};
// Maps methods that should be called directly on the BrowserWindow instance
methodInBrowserWindow = {
minimize: true,
close: true
};
MenuItem = (function() {
MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'];
function MenuItem(options) {
var click, ref;
const Menu = require('electron').Menu;
click = options.click, this.selector = options.selector, this.type = options.type, this.role = options.role, this.label = options.label, this.sublabel = options.sublabel, this.accelerator = options.accelerator, this.icon = options.icon, this.enabled = options.enabled, this.visible = options.visible, this.checked = options.checked, this.submenu = options.submenu;
if ((this.submenu != null) && this.submenu.constructor !== Menu) {
this.submenu = Menu.buildFromTemplate(this.submenu);
}
if ((this.type == null) && (this.submenu != null)) {
this.type = 'submenu';
}
if (this.type === 'submenu' && ((ref = this.submenu) != null ? ref.constructor : void 0) !== Menu) {
throw new Error('Invalid submenu');
}
this.overrideReadOnlyProperty('type', 'normal');
this.overrideReadOnlyProperty('role');
this.overrideReadOnlyProperty('accelerator');
this.overrideReadOnlyProperty('icon');
this.overrideReadOnlyProperty('submenu');
this.overrideProperty('label', '');
this.overrideProperty('sublabel', '');
this.overrideProperty('enabled', true);
this.overrideProperty('visible', true);
this.overrideProperty('checked', false);
if (MenuItem.types.indexOf(this.type) === -1) {
throw new Error("Unknown menu type " + this.type);
}
this.commandId = ++nextCommandId;
this.click = (function(_this) {
return function(focusedWindow) {
// Manually flip the checked flags when clicked.
var methodName, ref1, ref2;
if ((ref1 = _this.type) === 'checkbox' || ref1 === 'radio') {
_this.checked = !_this.checked;
}
if (_this.role && rolesMap[_this.role] && process.platform !== 'darwin' && (focusedWindow != null)) {
methodName = rolesMap[_this.role];
if (methodInBrowserWindow[methodName]) {
return focusedWindow[methodName]();
} else {
return (ref2 = focusedWindow.webContents) != null ? ref2[methodName]() : void 0;
}
} else if (typeof click === 'function') {
return click(_this, focusedWindow);
} else if (typeof _this.selector === 'string') {
return Menu.sendActionToFirstResponder(_this.selector);
}
};
})(this);
}
MenuItem.prototype.overrideProperty = function(name, defaultValue) {
if (defaultValue == null) {
defaultValue = null;
}
return this[name] != null ? this[name] : this[name] = defaultValue;
};
MenuItem.prototype.overrideReadOnlyProperty = function(name, defaultValue) {
if (defaultValue == null) {
defaultValue = null;
}
if (this[name] == null) {
this[name] = defaultValue;
}
return Object.defineProperty(this, name, {
enumerable: true,
writable: false,
value: this[name]
});
};
return MenuItem;
})();
module.exports = MenuItem;

View file

@ -1,178 +0,0 @@
{BrowserWindow, MenuItem} = require 'electron'
{EventEmitter} = require 'events'
v8Util = process.atomBinding 'v8_util'
bindings = process.atomBinding 'menu'
# Automatically generated radio menu item's group id.
nextGroupId = 0
# Search between seperators to find a radio menu item and return its group id,
# otherwise generate a group id.
generateGroupId = (items, pos) ->
if pos > 0
for i in [pos - 1..0]
item = items[i]
return item.groupId if item.type is 'radio'
break if item.type is 'separator'
else if pos < items.length
for i in [pos..items.length - 1]
item = items[i]
return item.groupId if item.type is 'radio'
break if item.type is 'separator'
++nextGroupId
# Returns the index of item according to |id|.
indexOfItemById = (items, id) ->
return i for item, i in items when item.id is id
-1
# Returns the index of where to insert the item according to |position|.
indexToInsertByPosition = (items, position) ->
return items.length unless position
[query, id] = position.split '='
insertIndex = indexOfItemById items, id
if insertIndex is -1 and query isnt 'endof'
console.warn "Item with id '#{id}' is not found"
return items.length
switch query
when 'after'
insertIndex++
when 'endof'
# If the |id| doesn't exist, then create a new group with the |id|.
if insertIndex is -1
items.push id: id, type: 'separator'
insertIndex = items.length - 1
# Find the end of the group.
insertIndex++
while insertIndex < items.length and items[insertIndex].type isnt 'separator'
insertIndex++
insertIndex
Menu = bindings.Menu
Menu::__proto__ = EventEmitter.prototype
Menu::_init = ->
@commandsMap = {}
@groupsMap = {}
@items = []
@delegate =
isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
isCommandIdEnabled: (commandId) => @commandsMap[commandId]?.enabled
isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible
getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
getIconForCommandId: (commandId) => @commandsMap[commandId]?.icon
executeCommand: (commandId) =>
@commandsMap[commandId]?.click BrowserWindow.getFocusedWindow()
menuWillShow: =>
# Make sure radio groups have at least one menu item seleted.
for id, group of @groupsMap
checked = false
for radioItem in group when radioItem.checked
checked = true
break
v8Util.setHiddenValue group[0], 'checked', true unless checked
Menu::popup = (window, x, y) ->
unless window?.constructor is BrowserWindow
# Shift.
y = x
x = window
window = BrowserWindow.getFocusedWindow()
if x? and y?
@_popupAt(window, x, y)
else
@_popup window
Menu::append = (item) ->
@insert @getItemCount(), item
Menu::insert = (pos, item) ->
throw new TypeError('Invalid item') unless item?.constructor is MenuItem
switch item.type
when 'normal' then @insertItem pos, item.commandId, item.label
when 'checkbox' then @insertCheckItem pos, item.commandId, item.label
when 'separator' then @insertSeparator pos
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
when 'radio'
# Grouping radio menu items.
item.overrideReadOnlyProperty 'groupId', generateGroupId(@items, pos)
@groupsMap[item.groupId] ?= []
@groupsMap[item.groupId].push item
# Setting a radio menu item should flip other items in the group.
v8Util.setHiddenValue item, 'checked', item.checked
Object.defineProperty item, 'checked',
enumerable: true
get: -> v8Util.getHiddenValue item, 'checked'
set: (val) =>
for otherItem in @groupsMap[item.groupId] when otherItem isnt item
v8Util.setHiddenValue otherItem, 'checked', false
v8Util.setHiddenValue item, 'checked', true
@insertRadioItem pos, item.commandId, item.label, item.groupId
@setSublabel pos, item.sublabel if item.sublabel?
@setIcon pos, item.icon if item.icon?
@setRole pos, item.role if item.role?
# Make menu accessable to items.
item.overrideReadOnlyProperty 'menu', this
# Remember the items.
@items.splice pos, 0, item
@commandsMap[item.commandId] = item
# Force menuWillShow to be called
Menu::_callMenuWillShow = ->
@delegate?.menuWillShow()
item.submenu._callMenuWillShow() for item in @items when item.submenu?
applicationMenu = null
Menu.setApplicationMenu = (menu) ->
throw new TypeError('Invalid menu') unless menu is null or menu.constructor is Menu
applicationMenu = menu # Keep a reference.
if process.platform is 'darwin'
return if menu is null
menu._callMenuWillShow()
bindings.setApplicationMenu menu
else
windows = BrowserWindow.getAllWindows()
w.setMenu menu for w in windows
Menu.getApplicationMenu = -> applicationMenu
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder
Menu.buildFromTemplate = (template) ->
throw new TypeError('Invalid template for Menu') unless Array.isArray template
positionedTemplate = []
insertIndex = 0
for item in template
if item.position
insertIndex = indexToInsertByPosition positionedTemplate, item.position
else
# If no |position| is specified, insert after last item.
insertIndex++
positionedTemplate.splice insertIndex, 0, item
menu = new Menu
for item in positionedTemplate
throw new TypeError('Invalid template for MenuItem') unless typeof item is 'object'
menuItem = new MenuItem(item)
menuItem[key] ?= value for key, value of item
menu.append menuItem
menu
module.exports = Menu

View file

@ -0,0 +1,332 @@
const BrowserWindow = require('electron').BrowserWindow;
const MenuItem = require('electron').MenuItem;
const EventEmitter = require('events').EventEmitter;
const v8Util = process.atomBinding('v8_util');
const bindings = process.atomBinding('menu');
// Automatically generated radio menu item's group id.
var nextGroupId = 0;
// Search between seperators to find a radio menu item and return its group id,
// otherwise generate a group id.
var generateGroupId = function(items, pos) {
var i, item, j, k, ref1, ref2, ref3;
if (pos > 0) {
for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
item = items[i];
if (item.type === 'radio') {
return item.groupId;
}
if (item.type === 'separator') {
break;
}
}
} else if (pos < items.length) {
for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
item = items[i];
if (item.type === 'radio') {
return item.groupId;
}
if (item.type === 'separator') {
break;
}
}
}
return ++nextGroupId;
};
// Returns the index of item according to |id|.
var indexOfItemById = function(items, id) {
var i, item, j, len;
for (i = j = 0, len = items.length; j < len; i = ++j) {
item = items[i];
if (item.id === id) {
return i;
}
}
return -1;
};
// Returns the index of where to insert the item according to |position|.
var indexToInsertByPosition = function(items, position) {
var id, insertIndex, query, ref1;
if (!position) {
return items.length;
}
ref1 = position.split('='), query = ref1[0], id = ref1[1];
insertIndex = indexOfItemById(items, id);
if (insertIndex === -1 && query !== 'endof') {
console.warn("Item with id '" + id + "' is not found");
return items.length;
}
switch (query) {
case 'after':
insertIndex++;
break;
case 'endof':
// If the |id| doesn't exist, then create a new group with the |id|.
if (insertIndex === -1) {
items.push({
id: id,
type: 'separator'
});
insertIndex = items.length - 1;
}
// Find the end of the group.
insertIndex++;
while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
insertIndex++;
}
}
return insertIndex;
};
const Menu = bindings.Menu;
Menu.prototype.__proto__ = EventEmitter.prototype;
Menu.prototype._init = function() {
this.commandsMap = {};
this.groupsMap = {};
this.items = [];
return this.delegate = {
isCommandIdChecked: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.checked : void 0;
};
})(this),
isCommandIdEnabled: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.enabled : void 0;
};
})(this),
isCommandIdVisible: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.visible : void 0;
};
})(this),
getAcceleratorForCommandId: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.accelerator : void 0;
};
})(this),
getIconForCommandId: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.icon : void 0;
};
})(this),
executeCommand: (function(_this) {
return function(commandId) {
var ref1;
return (ref1 = _this.commandsMap[commandId]) != null ? ref1.click(BrowserWindow.getFocusedWindow()) : void 0;
};
})(this),
menuWillShow: (function(_this) {
return function() {
// Make sure radio groups have at least one menu item seleted.
var checked, group, id, j, len, radioItem, ref1, results;
ref1 = _this.groupsMap;
results = [];
for (id in ref1) {
group = ref1[id];
checked = false;
for (j = 0, len = group.length; j < len; j++) {
radioItem = group[j];
if (!radioItem.checked) {
continue;
}
checked = true;
break;
}
if (!checked) {
results.push(v8Util.setHiddenValue(group[0], 'checked', true));
} else {
results.push(void 0);
}
}
return results;
};
})(this)
};
};
Menu.prototype.popup = function(window, x, y) {
if ((window != null ? window.constructor : void 0) !== BrowserWindow) {
// Shift.
y = x;
x = window;
window = BrowserWindow.getFocusedWindow();
}
if ((x != null) && (y != null)) {
return this._popupAt(window, x, y);
} else {
return this._popup(window);
}
};
Menu.prototype.append = function(item) {
return this.insert(this.getItemCount(), item);
};
Menu.prototype.insert = function(pos, item) {
var base, name;
if ((item != null ? item.constructor : void 0) !== MenuItem) {
throw new TypeError('Invalid item');
}
switch (item.type) {
case 'normal':
this.insertItem(pos, item.commandId, item.label);
break;
case 'checkbox':
this.insertCheckItem(pos, item.commandId, item.label);
break;
case 'separator':
this.insertSeparator(pos);
break;
case 'submenu':
this.insertSubMenu(pos, item.commandId, item.label, item.submenu);
break;
case 'radio':
// Grouping radio menu items.
item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos));
if ((base = this.groupsMap)[name = item.groupId] == null) {
base[name] = [];
}
this.groupsMap[item.groupId].push(item);
// Setting a radio menu item should flip other items in the group.
v8Util.setHiddenValue(item, 'checked', item.checked);
Object.defineProperty(item, 'checked', {
enumerable: true,
get: function() {
return v8Util.getHiddenValue(item, 'checked');
},
set: (function(_this) {
return function(val) {
var j, len, otherItem, ref1;
ref1 = _this.groupsMap[item.groupId];
for (j = 0, len = ref1.length; j < len; j++) {
otherItem = ref1[j];
if (otherItem !== item) {
v8Util.setHiddenValue(otherItem, 'checked', false);
}
}
return v8Util.setHiddenValue(item, 'checked', true);
};
})(this)
});
this.insertRadioItem(pos, item.commandId, item.label, item.groupId);
}
if (item.sublabel != null) {
this.setSublabel(pos, item.sublabel);
}
if (item.icon != null) {
this.setIcon(pos, item.icon);
}
if (item.role != null) {
this.setRole(pos, item.role);
}
// Make menu accessable to items.
item.overrideReadOnlyProperty('menu', this);
// Remember the items.
this.items.splice(pos, 0, item);
return this.commandsMap[item.commandId] = item;
};
// Force menuWillShow to be called
Menu.prototype._callMenuWillShow = function() {
var item, j, len, ref1, ref2, results;
if ((ref1 = this.delegate) != null) {
ref1.menuWillShow();
}
ref2 = this.items;
results = [];
for (j = 0, len = ref2.length; j < len; j++) {
item = ref2[j];
if (item.submenu != null) {
results.push(item.submenu._callMenuWillShow());
}
}
return results;
};
var applicationMenu = null;
Menu.setApplicationMenu = function(menu) {
var j, len, results, w, windows;
if (!(menu === null || menu.constructor === Menu)) {
throw new TypeError('Invalid menu');
}
// Keep a reference.
applicationMenu = menu;
if (process.platform === 'darwin') {
if (menu === null) {
return;
}
menu._callMenuWillShow();
return bindings.setApplicationMenu(menu);
} else {
windows = BrowserWindow.getAllWindows();
results = [];
for (j = 0, len = windows.length; j < len; j++) {
w = windows[j];
results.push(w.setMenu(menu));
}
return results;
}
};
Menu.getApplicationMenu = function() {
return applicationMenu;
};
Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder;
Menu.buildFromTemplate = function(template) {
var insertIndex, item, j, k, key, len, len1, menu, menuItem, positionedTemplate, value;
if (!Array.isArray(template)) {
throw new TypeError('Invalid template for Menu');
}
positionedTemplate = [];
insertIndex = 0;
for (j = 0, len = template.length; j < len; j++) {
item = template[j];
if (item.position) {
insertIndex = indexToInsertByPosition(positionedTemplate, item.position);
} else {
// If no |position| is specified, insert after last item.
insertIndex++;
}
positionedTemplate.splice(insertIndex, 0, item);
}
menu = new Menu;
for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
item = positionedTemplate[k];
if (typeof item !== 'object') {
throw new TypeError('Invalid template for MenuItem');
}
menuItem = new MenuItem(item);
for (key in item) {
value = item[key];
if (menuItem[key] == null) {
menuItem[key] = value;
}
}
menu.append(menuItem);
}
return menu;
};
module.exports = Menu;

View file

@ -1,122 +0,0 @@
{ipcMain} = require 'electron'
# The history operation in renderer is redirected to browser.
ipcMain.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
event.sender[method] args...
ipcMain.on 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', (event, method, args...) ->
event.returnValue = event.sender[method] args...
# JavaScript implementation of Chromium's NavigationController.
# Instead of relying on Chromium for history control, we compeletely do history
# control on user land, and only rely on WebContents.loadURL for navigation.
# This helps us avoid Chromium's various optimizations so we can ensure renderer
# process is restarted everytime.
class NavigationController
constructor: (@webContents) ->
@clearHistory()
# webContents may have already navigated to a page.
if @webContents._getURL()
@currentIndex++
@history.push @webContents._getURL()
@webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
if @inPageIndex > -1 and not inPage
# Navigated to a new page, clear in-page mark.
@inPageIndex = -1
else if @inPageIndex is -1 and inPage
# Started in-page navigations.
@inPageIndex = @currentIndex
if @pendingIndex >= 0 # Go to index.
@currentIndex = @pendingIndex
@pendingIndex = -1
@history[@currentIndex] = url
else if replaceEntry # Non-user initialized navigation.
@history[@currentIndex] = url
else # Normal navigation.
@history = @history.slice 0, @currentIndex + 1 # Clear history.
currentEntry = @history[@currentIndex]
if currentEntry?.url isnt url
@currentIndex++
@history.push url
loadURL: (url, options={}) ->
@pendingIndex = -1
@webContents._loadURL url, options
@webContents.emit 'load-url', url, options
getURL: ->
if @currentIndex is -1
''
else
@history[@currentIndex]
stop: ->
@pendingIndex = -1
@webContents._stop()
reload: ->
@pendingIndex = @currentIndex
@webContents._loadURL @getURL(), {}
reloadIgnoringCache: ->
@pendingIndex = @currentIndex
@webContents._loadURL @getURL(), {extraHeaders: "pragma: no-cache\n"}
canGoBack: ->
@getActiveIndex() > 0
canGoForward: ->
@getActiveIndex() < @history.length - 1
canGoToIndex: (index) ->
index >=0 and index < @history.length
canGoToOffset: (offset) ->
@canGoToIndex @currentIndex + offset
clearHistory: ->
@history = []
@currentIndex = -1
@pendingIndex = -1
@inPageIndex = -1
goBack: ->
return unless @canGoBack()
@pendingIndex = @getActiveIndex() - 1
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
@webContents._goBack()
else
@webContents._loadURL @history[@pendingIndex], {}
goForward: ->
return unless @canGoForward()
@pendingIndex = @getActiveIndex() + 1
if @inPageIndex > -1 and @pendingIndex >= @inPageIndex
@webContents._goForward()
else
@webContents._loadURL @history[@pendingIndex], {}
goToIndex: (index) ->
return unless @canGoToIndex index
@pendingIndex = index
@webContents._loadURL @history[@pendingIndex], {}
goToOffset: (offset) ->
return unless @canGoToOffset offset
pendingIndex = @currentIndex + offset
if @inPageIndex > -1 and pendingIndex >= @inPageIndex
@pendingIndex = pendingIndex
@webContents._goToOffset offset
else
@goToIndex pendingIndex
getActiveIndex: ->
if @pendingIndex is -1 then @currentIndex else @pendingIndex
length: ->
@history.length
module.exports = NavigationController

View file

@ -0,0 +1,188 @@
const ipcMain = require('electron').ipcMain;
var slice = [].slice;
// The history operation in renderer is redirected to browser.
ipcMain.on('ATOM_SHELL_NAVIGATION_CONTROLLER', function() {
var args, event, method, ref;
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return (ref = event.sender)[method].apply(ref, args);
});
ipcMain.on('ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', function() {
var args, event, method, ref;
event = arguments[0], method = arguments[1], args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
return event.returnValue = (ref = event.sender)[method].apply(ref, args);
});
// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
// control on user land, and only rely on WebContents.loadURL for navigation.
// This helps us avoid Chromium's various optimizations so we can ensure renderer
// process is restarted everytime.
var NavigationController = (function() {
function NavigationController(webContents) {
this.webContents = webContents;
this.clearHistory();
// webContents may have already navigated to a page.
if (this.webContents._getURL()) {
this.currentIndex++;
this.history.push(this.webContents._getURL());
}
this.webContents.on('navigation-entry-commited', (function(_this) {
return function(event, url, inPage, replaceEntry) {
var currentEntry;
if (_this.inPageIndex > -1 && !inPage) {
// Navigated to a new page, clear in-page mark.
_this.inPageIndex = -1;
} else if (_this.inPageIndex === -1 && inPage) {
// Started in-page navigations.
_this.inPageIndex = _this.currentIndex;
}
if (_this.pendingIndex >= 0) {
// Go to index.
_this.currentIndex = _this.pendingIndex;
_this.pendingIndex = -1;
return _this.history[_this.currentIndex] = url;
} else if (replaceEntry) {
// Non-user initialized navigation.
return _this.history[_this.currentIndex] = url;
} else {
// Normal navigation. Clear history.
_this.history = _this.history.slice(0, _this.currentIndex + 1);
currentEntry = _this.history[_this.currentIndex];
if ((currentEntry != null ? currentEntry.url : void 0) !== url) {
_this.currentIndex++;
return _this.history.push(url);
}
}
};
})(this));
}
NavigationController.prototype.loadURL = function(url, options) {
if (options == null) {
options = {};
}
this.pendingIndex = -1;
this.webContents._loadURL(url, options);
return this.webContents.emit('load-url', url, options);
};
NavigationController.prototype.getURL = function() {
if (this.currentIndex === -1) {
return '';
} else {
return this.history[this.currentIndex];
}
};
NavigationController.prototype.stop = function() {
this.pendingIndex = -1;
return this.webContents._stop();
};
NavigationController.prototype.reload = function() {
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {});
};
NavigationController.prototype.reloadIgnoringCache = function() {
this.pendingIndex = this.currentIndex;
return this.webContents._loadURL(this.getURL(), {
extraHeaders: "pragma: no-cache\n"
});
};
NavigationController.prototype.canGoBack = function() {
return this.getActiveIndex() > 0;
};
NavigationController.prototype.canGoForward = function() {
return this.getActiveIndex() < this.history.length - 1;
};
NavigationController.prototype.canGoToIndex = function(index) {
return index >= 0 && index < this.history.length;
};
NavigationController.prototype.canGoToOffset = function(offset) {
return this.canGoToIndex(this.currentIndex + offset);
};
NavigationController.prototype.clearHistory = function() {
this.history = [];
this.currentIndex = -1;
this.pendingIndex = -1;
return this.inPageIndex = -1;
};
NavigationController.prototype.goBack = function() {
if (!this.canGoBack()) {
return;
}
this.pendingIndex = this.getActiveIndex() - 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goBack();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
};
NavigationController.prototype.goForward = function() {
if (!this.canGoForward()) {
return;
}
this.pendingIndex = this.getActiveIndex() + 1;
if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
return this.webContents._goForward();
} else {
return this.webContents._loadURL(this.history[this.pendingIndex], {});
}
};
NavigationController.prototype.goToIndex = function(index) {
if (!this.canGoToIndex(index)) {
return;
}
this.pendingIndex = index;
return this.webContents._loadURL(this.history[this.pendingIndex], {});
};
NavigationController.prototype.goToOffset = function(offset) {
var pendingIndex;
if (!this.canGoToOffset(offset)) {
return;
}
pendingIndex = this.currentIndex + offset;
if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
this.pendingIndex = pendingIndex;
return this.webContents._goToOffset(offset);
} else {
return this.goToIndex(pendingIndex);
}
};
NavigationController.prototype.getActiveIndex = function() {
if (this.pendingIndex === -1) {
return this.currentIndex;
} else {
return this.pendingIndex;
}
};
NavigationController.prototype.length = function() {
return this.history.length;
};
return NavigationController;
})();
module.exports = NavigationController;

View file

@ -1,7 +0,0 @@
{EventEmitter} = require 'events'
{powerMonitor} = process.atomBinding 'power_monitor'
powerMonitor.__proto__ = EventEmitter.prototype
module.exports = powerMonitor

View file

@ -0,0 +1,6 @@
const EventEmitter = require('events').EventEmitter;
const powerMonitor = process.atomBinding('power_monitor').powerMonitor;
powerMonitor.__proto__ = EventEmitter.prototype;
module.exports = powerMonitor;

View file

@ -1,3 +0,0 @@
{powerSaveBlocker} = process.atomBinding 'power_save_blocker'
module.exports = powerSaveBlocker

View file

@ -0,0 +1,5 @@
var powerSaveBlocker;
powerSaveBlocker = process.atomBinding('power_save_blocker').powerSaveBlocker;
module.exports = powerSaveBlocker;

View file

@ -1,25 +0,0 @@
{app} = require 'electron'
throw new Error('Can not initialize protocol module before app is ready') unless app.isReady()
{protocol} = process.atomBinding 'protocol'
# Warn about removed APIs.
logAndThrow = (callback, message) ->
console.error message
if callback then callback(new Error(message)) else throw new Error(message)
protocol.registerProtocol = (scheme, handler, callback) ->
logAndThrow callback,
'registerProtocol API has been replaced by the
register[File/Http/Buffer/String]Protocol API family, please
switch to the new APIs.'
protocol.isHandledProtocol = (scheme, callback) ->
logAndThrow callback,
'isHandledProtocol API has been replaced by isProtocolHandled.'
protocol.interceptProtocol = (scheme, handler, callback) ->
logAndThrow callback,
'interceptProtocol API has been replaced by the
intercept[File/Http/Buffer/String]Protocol API family, please
switch to the new APIs.'
module.exports = protocol

View file

@ -0,0 +1,31 @@
const app = require('electron').app;
if (!app.isReady()) {
throw new Error('Can not initialize protocol module before app is ready');
}
const protocol = process.atomBinding('protocol').protocol;
// Warn about removed APIs.
var logAndThrow = function(callback, message) {
console.error(message);
if (callback) {
return callback(new Error(message));
} else {
throw new Error(message);
}
};
protocol.registerProtocol = function(scheme, handler, callback) {
return logAndThrow(callback, 'registerProtocol API has been replaced by the register[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
};
protocol.isHandledProtocol = function(scheme, callback) {
return logAndThrow(callback, 'isHandledProtocol API has been replaced by isProtocolHandled.');
};
protocol.interceptProtocol = function(scheme, handler, callback) {
return logAndThrow(callback, 'interceptProtocol API has been replaced by the intercept[File/Http/Buffer/String]Protocol API family, please switch to the new APIs.');
};
module.exports = protocol;

View file

@ -1,6 +0,0 @@
{EventEmitter} = require 'events'
{screen} = process.atomBinding 'screen'
screen.__proto__ = EventEmitter.prototype
module.exports = screen

View file

@ -0,0 +1,6 @@
const EventEmitter = require('events').EventEmitter;
const screen = process.atomBinding('screen').screen;
screen.__proto__ = EventEmitter.prototype;
module.exports = screen;

View file

@ -1,24 +0,0 @@
{EventEmitter} = require 'events'
bindings = process.atomBinding 'session'
PERSIST_PERFIX = 'persist:'
# Returns the Session from |partition| string.
exports.fromPartition = (partition='') ->
return exports.defaultSession if partition is ''
if partition.startsWith PERSIST_PERFIX
bindings.fromPartition partition.substr(PERSIST_PERFIX.length), false
else
bindings.fromPartition partition, true
# Returns the default session.
Object.defineProperty exports, 'defaultSession',
enumerable: true
get: -> bindings.fromPartition '', false
wrapSession = (session) ->
# session is an EventEmitter.
session.__proto__ = EventEmitter.prototype
bindings._setWrapSession wrapSession

View file

@ -0,0 +1,33 @@
const EventEmitter = require('events').EventEmitter;
const bindings = process.atomBinding('session');
const PERSIST_PERFIX = 'persist:';
// Returns the Session from |partition| string.
exports.fromPartition = function(partition) {
if (partition == null) {
partition = '';
}
if (partition === '') {
return exports.defaultSession;
}
if (partition.startsWith(PERSIST_PERFIX)) {
return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false);
} else {
return bindings.fromPartition(partition, true);
}
};
// Returns the default session.
Object.defineProperty(exports, 'defaultSession', {
enumerable: true,
get: function() {
return bindings.fromPartition('', false);
}
});
var wrapSession = function(session) {
// session is an EventEmitter.
return session.__proto__ = EventEmitter.prototype;
};
bindings._setWrapSession(wrapSession);

View file

@ -1,19 +0,0 @@
{deprecate} = require 'electron'
{EventEmitter} = require 'events'
{Tray} = process.atomBinding 'tray'
Tray::__proto__ = EventEmitter.prototype
Tray::_init = ->
# Deprecated.
deprecate.rename this, 'popContextMenu', 'popUpContextMenu'
deprecate.event this, 'clicked', 'click'
deprecate.event this, 'double-clicked', 'double-click'
deprecate.event this, 'right-clicked', 'right-click'
deprecate.event this, 'balloon-clicked', 'balloon-click'
Tray::setContextMenu = (menu) ->
@_setContextMenu menu
@menu = menu # Keep a strong reference of menu.
module.exports = Tray

View file

@ -0,0 +1,23 @@
const deprecate = require('electron').deprecate;
const EventEmitter = require('events').EventEmitter;
const Tray = process.atomBinding('tray').Tray;
Tray.prototype.__proto__ = EventEmitter.prototype;
Tray.prototype._init = function() {
// Deprecated.
deprecate.rename(this, 'popContextMenu', 'popUpContextMenu');
deprecate.event(this, 'clicked', 'click');
deprecate.event(this, 'double-clicked', 'double-click');
deprecate.event(this, 'right-clicked', 'right-click');
return deprecate.event(this, 'balloon-clicked', 'balloon-click');
};
Tray.prototype.setContextMenu = function(menu) {
this._setContextMenu(menu);
// Keep a strong reference of menu.
return this.menu = menu;
};
module.exports = Tray;

View file

@ -1,137 +0,0 @@
{EventEmitter} = require 'events'
{deprecate, ipcMain, session, NavigationController, Menu} = require 'electron'
binding = process.atomBinding 'web_contents'
nextId = 0
getNextId = -> ++nextId
PDFPageSize =
A5:
custom_display_name: "A5"
height_microns: 210000
name: "ISO_A5"
width_microns: 148000
A4:
custom_display_name: "A4"
height_microns: 297000
name: "ISO_A4"
is_default: "true"
width_microns: 210000
A3:
custom_display_name: "A3"
height_microns: 420000
name: "ISO_A3"
width_microns: 297000
Legal:
custom_display_name: "Legal"
height_microns: 355600
name: "NA_LEGAL"
width_microns: 215900
Letter:
custom_display_name: "Letter"
height_microns: 279400
name: "NA_LETTER"
width_microns: 215900
Tabloid:
height_microns: 431800
name: "NA_LEDGER"
width_microns: 279400
custom_display_name: "Tabloid"
wrapWebContents = (webContents) ->
# webContents is an EventEmitter.
webContents.__proto__ = EventEmitter.prototype
# WebContents::send(channel, args..)
webContents.send = (channel, args...) ->
@_send channel, [args...]
# Make sure webContents.executeJavaScript would run the code only when the
# web contents has been loaded.
webContents.executeJavaScript = (code, hasUserGesture=false) ->
if @getURL() and not @isLoading()
@_executeJavaScript code, hasUserGesture
else
webContents.once 'did-finish-load', @_executeJavaScript.bind(this, code, hasUserGesture)
# The navigation controller.
controller = new NavigationController(webContents)
for name, method of NavigationController.prototype when method instanceof Function
do (name, method) ->
webContents[name] = -> method.apply controller, arguments
# Dispatch IPC messages to the ipc module.
webContents.on 'ipc-message', (event, packed) ->
[channel, args...] = packed
ipcMain.emit channel, event, args...
webContents.on 'ipc-message-sync', (event, packed) ->
[channel, args...] = packed
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
ipcMain.emit channel, event, args...
# Handle context menu action request from pepper plugin.
webContents.on 'pepper-context-menu', (event, params) ->
menu = Menu.buildFromTemplate params.menu
menu.popup params.x, params.y
# This error occurs when host could not be found.
webContents.on 'did-fail-provisional-load', (args...) ->
# Calling loadURL during this event might cause crash, so delay the event
# until next tick.
setImmediate => @emit 'did-fail-load', args...
# Delays the page-title-updated event to next tick.
webContents.on '-page-title-updated', (args...) ->
setImmediate => @emit 'page-title-updated', args...
# Deprecated.
deprecate.rename webContents, 'loadUrl', 'loadURL'
deprecate.rename webContents, 'getUrl', 'getURL'
deprecate.event webContents, 'page-title-set', 'page-title-updated', (args...) ->
@emit 'page-title-set', args...
webContents.printToPDF = (options, callback) ->
printingSetting =
pageRage: []
mediaSize: {}
landscape: false
color: 2
headerFooterEnabled: false
marginsType: 0
isFirstRequest: false
requestID: getNextId()
previewModifiable: true
printToPDF: true
printWithCloudPrint: false
printWithPrivet: false
printWithExtension: false
deviceName: "Save as PDF"
generateDraftData: true
fitToPageEnabled: false
duplex: 0
copies: 1
collate: true
shouldPrintBackgrounds: false
shouldPrintSelectionOnly: false
if options.landscape
printingSetting.landscape = options.landscape
if options.marginsType
printingSetting.marginsType = options.marginsType
if options.printSelectionOnly
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
if options.printBackground
printingSetting.shouldPrintBackgrounds = options.printBackground
if options.pageSize and PDFPageSize[options.pageSize]
printingSetting.mediaSize = PDFPageSize[options.pageSize]
else
printingSetting.mediaSize = PDFPageSize['A4']
@_printToPDF printingSetting, callback
binding._setWrapWebContents wrapWebContents
module.exports.create = (options={}) ->
binding.create(options)

View file

@ -0,0 +1,226 @@
'use strict';
const EventEmitter = require('events').EventEmitter;
const deprecate = require('electron').deprecate;
const ipcMain = require('electron').ipcMain;
const session = require('electron').session;
const NavigationController = require('electron').NavigationController;
const Menu = require('electron').Menu;
const binding = process.atomBinding('web_contents');
let slice = [].slice;
let nextId = 0;
let getNextId = function() {
return ++nextId;
};
let PDFPageSize = {
A5: {
custom_display_name: "A5",
height_microns: 210000,
name: "ISO_A5",
width_microns: 148000
},
A4: {
custom_display_name: "A4",
height_microns: 297000,
name: "ISO_A4",
is_default: "true",
width_microns: 210000
},
A3: {
custom_display_name: "A3",
height_microns: 420000,
name: "ISO_A3",
width_microns: 297000
},
Legal: {
custom_display_name: "Legal",
height_microns: 355600,
name: "NA_LEGAL",
width_microns: 215900
},
Letter: {
custom_display_name: "Letter",
height_microns: 279400,
name: "NA_LETTER",
width_microns: 215900
},
Tabloid: {
height_microns: 431800,
name: "NA_LEDGER",
width_microns: 279400,
custom_display_name: "Tabloid"
}
};
// Following methods are mapped to webFrame.
const webFrameMethods = [
'executeJavaScript',
'insertText',
'setZoomFactor',
'setZoomLevel',
'setZoomLevelLimits',
];
let wrapWebContents = function(webContents) {
// webContents is an EventEmitter.
var controller, method, name, ref1;
webContents.__proto__ = EventEmitter.prototype;
// Every remote callback from renderer process would add a listenter to the
// render-view-deleted event, so ignore the listenters warning.
webContents.setMaxListeners(0);
// WebContents::send(channel, args..)
webContents.send = function() {
var args, channel;
channel = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return this._send(channel, slice.call(args));
};
// The navigation controller.
controller = new NavigationController(webContents);
ref1 = NavigationController.prototype;
for (name in ref1) {
method = ref1[name];
if (method instanceof Function) {
(function(name, method) {
return webContents[name] = function() {
return method.apply(controller, arguments);
};
})(name, method);
}
}
// Mapping webFrame methods.
for (let method of webFrameMethods) {
webContents[method] = function() {
let args = Array.prototype.slice.call(arguments);
this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args);
};
}
// Make sure webContents.executeJavaScript would run the code only when the
// webContents has been loaded.
const executeJavaScript = webContents.executeJavaScript;
webContents.executeJavaScript = function(code, hasUserGesture) {
if (this.getURL() && !this.isLoading())
return executeJavaScript.call(this, code, hasUserGesture);
else
return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture));
};
// Dispatch IPC messages to the ipc module.
webContents.on('ipc-message', function(event, packed) {
var args, channel;
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
});
webContents.on('ipc-message-sync', function(event, packed) {
var args, channel;
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
Object.defineProperty(event, 'returnValue', {
set: function(value) {
return event.sendReply(JSON.stringify(value));
}
});
return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
});
// Handle context menu action request from pepper plugin.
webContents.on('pepper-context-menu', function(event, params) {
var menu;
menu = Menu.buildFromTemplate(params.menu);
return menu.popup(params.x, params.y);
});
// This error occurs when host could not be found.
webContents.on('did-fail-provisional-load', function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
// Calling loadURL during this event might cause crash, so delay the event
// until next tick.
return setImmediate((function(_this) {
return function() {
return _this.emit.apply(_this, ['did-fail-load'].concat(slice.call(args)));
};
})(this));
});
// Delays the page-title-updated event to next tick.
webContents.on('-page-title-updated', function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return setImmediate((function(_this) {
return function() {
return _this.emit.apply(_this, ['page-title-updated'].concat(slice.call(args)));
};
})(this));
});
// Deprecated.
deprecate.rename(webContents, 'loadUrl', 'loadURL');
deprecate.rename(webContents, 'getUrl', 'getURL');
deprecate.event(webContents, 'page-title-set', 'page-title-updated', function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return this.emit.apply(this, ['page-title-set'].concat(slice.call(args)));
});
return webContents.printToPDF = function(options, callback) {
var printingSetting;
printingSetting = {
pageRage: [],
mediaSize: {},
landscape: false,
color: 2,
headerFooterEnabled: false,
marginsType: 0,
isFirstRequest: false,
requestID: getNextId(),
previewModifiable: true,
printToPDF: true,
printWithCloudPrint: false,
printWithPrivet: false,
printWithExtension: false,
deviceName: "Save as PDF",
generateDraftData: true,
fitToPageEnabled: false,
duplex: 0,
copies: 1,
collate: true,
shouldPrintBackgrounds: false,
shouldPrintSelectionOnly: false
};
if (options.landscape) {
printingSetting.landscape = options.landscape;
}
if (options.marginsType) {
printingSetting.marginsType = options.marginsType;
}
if (options.printSelectionOnly) {
printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
}
if (options.printBackground) {
printingSetting.shouldPrintBackgrounds = options.printBackground;
}
if (options.pageSize && PDFPageSize[options.pageSize]) {
printingSetting.mediaSize = PDFPageSize[options.pageSize];
} else {
printingSetting.mediaSize = PDFPageSize['A4'];
}
return this._printToPDF(printingSetting, callback);
};
};
binding._setWrapWebContents(wrapWebContents);
module.exports.create = function(options) {
if (options == null) {
options = {};
}
return binding.create(options);
};

View file

@ -21,6 +21,7 @@
#if defined(USE_X11) #if defined(USE_X11)
#include "chrome/browser/ui/libgtk2ui/gtk2_util.h" #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
#include "ui/events/devices/x11/touch_factory_x11.h"
#endif #endif
namespace atom { namespace atom {
@ -116,6 +117,10 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
node_bindings_->PrepareMessageLoop(); node_bindings_->PrepareMessageLoop();
node_bindings_->RunMessageLoop(); node_bindings_->RunMessageLoop();
#if defined(USE_X11)
ui::TouchFactory::SetTouchDeviceListFromCommandLine();
#endif
// Start idle gc. // Start idle gc.
gc_timer_.Start( gc_timer_.Start(
FROM_HERE, base::TimeDelta::FromMinutes(1), FROM_HERE, base::TimeDelta::FromMinutes(1),

View file

@ -1,96 +0,0 @@
electron = require 'electron'
fs = require 'fs'
path = require 'path'
url = require 'url'
# Mapping between hostname and file path.
hostPathMap = {}
hostPathMapNextKey = 0
getHostForPath = (path) ->
key = "extension-#{++hostPathMapNextKey}"
hostPathMap[key] = path
key
getPathForHost = (host) ->
hostPathMap[host]
# Cache extensionInfo.
extensionInfoMap = {}
getExtensionInfoFromPath = (srcDirectory) ->
manifest = JSON.parse fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
unless extensionInfoMap[manifest.name]?
# We can not use 'file://' directly because all resources in the extension
# will be treated as relative to the root in Chrome.
page = url.format
protocol: 'chrome-extension'
slashes: true
hostname: getHostForPath srcDirectory
pathname: manifest.devtools_page
extensionInfoMap[manifest.name] =
startPage: page
name: manifest.name
srcDirectory: srcDirectory
exposeExperimentalAPIs: true
extensionInfoMap[manifest.name]
# The loaded extensions cache and its persistent path.
loadedExtensions = null
loadedExtensionsPath = null
# Persistent loaded extensions.
{app} = electron
app.on 'will-quit', ->
try
loadedExtensions = Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key].srcDirectory
try
fs.mkdirSync path.dirname(loadedExtensionsPath)
catch e
fs.writeFileSync loadedExtensionsPath, JSON.stringify(loadedExtensions)
catch e
# We can not use protocol or BrowserWindow until app is ready.
app.once 'ready', ->
{protocol, BrowserWindow} = electron
# Load persistented extensions.
loadedExtensionsPath = path.join app.getPath('userData'), 'DevTools Extensions'
try
loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)
loadedExtensions = [] unless Array.isArray loadedExtensions
# Preheat the extensionInfo cache.
getExtensionInfoFromPath srcDirectory for srcDirectory in loadedExtensions
catch e
# The chrome-extension: can map a extension URL request to real file path.
chromeExtensionHandler = (request, callback) ->
parsed = url.parse request.url
return callback() unless parsed.hostname and parsed.path?
return callback() unless /extension-\d+/.test parsed.hostname
directory = getPathForHost parsed.hostname
return callback() unless directory?
callback path.join(directory, parsed.path)
protocol.registerFileProtocol 'chrome-extension', chromeExtensionHandler, (error) ->
console.error 'Unable to register chrome-extension protocol' if error
BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) ->
@devToolsWebContents?.executeJavaScript "DevToolsAPI.addExtensions(#{JSON.stringify(extensionInfoArray)});"
BrowserWindow.addDevToolsExtension = (srcDirectory) ->
extensionInfo = getExtensionInfoFromPath srcDirectory
if extensionInfo
window._loadDevToolsExtensions [extensionInfo] for window in BrowserWindow.getAllWindows()
extensionInfo.name
BrowserWindow.removeDevToolsExtension = (name) ->
delete extensionInfoMap[name]
# Load persistented extensions when devtools is opened.
init = BrowserWindow::_init
BrowserWindow::_init = ->
init.call this
@on 'devtools-opened', ->
@_loadDevToolsExtensions Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key]

View file

@ -0,0 +1,142 @@
const electron = require('electron');
const app = electron.app;
const fs = require('fs');
const path = require('path');
const url = require('url');
// Mapping between hostname and file path.
var hostPathMap = {};
var hostPathMapNextKey = 0;
var getHostForPath = function(path) {
var key;
key = "extension-" + (++hostPathMapNextKey);
hostPathMap[key] = path;
return key;
};
var getPathForHost = function(host) {
return hostPathMap[host];
};
// Cache extensionInfo.
var extensionInfoMap = {};
var getExtensionInfoFromPath = function(srcDirectory) {
var manifest, page;
manifest = JSON.parse(fs.readFileSync(path.join(srcDirectory, 'manifest.json')));
if (extensionInfoMap[manifest.name] == null) {
// We can not use 'file://' directly because all resources in the extension
// will be treated as relative to the root in Chrome.
page = url.format({
protocol: 'chrome-extension',
slashes: true,
hostname: getHostForPath(srcDirectory),
pathname: manifest.devtools_page
});
extensionInfoMap[manifest.name] = {
startPage: page,
name: manifest.name,
srcDirectory: srcDirectory,
exposeExperimentalAPIs: true
};
return extensionInfoMap[manifest.name];
}
};
// The loaded extensions cache and its persistent path.
var loadedExtensions = null;
var loadedExtensionsPath = null;
app.on('will-quit', function() {
var e, error1, error2;
try {
loadedExtensions = Object.keys(extensionInfoMap).map(function(key) {
return extensionInfoMap[key].srcDirectory;
});
try {
fs.mkdirSync(path.dirname(loadedExtensionsPath));
} catch (error1) {
e = error1;
}
return fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions));
} catch (error2) {
e = error2;
}
});
// We can not use protocol or BrowserWindow until app is ready.
app.once('ready', function() {
var BrowserWindow, chromeExtensionHandler, e, error1, i, init, len, protocol, srcDirectory;
protocol = electron.protocol, BrowserWindow = electron.BrowserWindow;
// Load persistented extensions.
loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions');
try {
loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath));
if (!Array.isArray(loadedExtensions)) {
loadedExtensions = [];
}
// Preheat the extensionInfo cache.
for (i = 0, len = loadedExtensions.length; i < len; i++) {
srcDirectory = loadedExtensions[i];
getExtensionInfoFromPath(srcDirectory);
}
} catch (error1) {
e = error1;
}
// The chrome-extension: can map a extension URL request to real file path.
chromeExtensionHandler = function(request, callback) {
var directory, parsed;
parsed = url.parse(request.url);
if (!(parsed.hostname && (parsed.path != null))) {
return callback();
}
if (!/extension-\d+/.test(parsed.hostname)) {
return callback();
}
directory = getPathForHost(parsed.hostname);
if (directory == null) {
return callback();
}
return callback(path.join(directory, parsed.path));
};
protocol.registerFileProtocol('chrome-extension', chromeExtensionHandler, function(error) {
if (error) {
return console.error('Unable to register chrome-extension protocol');
}
});
BrowserWindow.prototype._loadDevToolsExtensions = function(extensionInfoArray) {
var ref;
return (ref = this.devToolsWebContents) != null ? ref.executeJavaScript("DevToolsAPI.addExtensions(" + (JSON.stringify(extensionInfoArray)) + ");") : void 0;
};
BrowserWindow.addDevToolsExtension = function(srcDirectory) {
var extensionInfo, j, len1, ref, window;
extensionInfo = getExtensionInfoFromPath(srcDirectory);
if (extensionInfo) {
ref = BrowserWindow.getAllWindows();
for (j = 0, len1 = ref.length; j < len1; j++) {
window = ref[j];
window._loadDevToolsExtensions([extensionInfo]);
}
return extensionInfo.name;
}
};
BrowserWindow.removeDevToolsExtension = function(name) {
return delete extensionInfoMap[name];
};
// Load persistented extensions when devtools is opened.
init = BrowserWindow.prototype._init;
return BrowserWindow.prototype._init = function() {
init.call(this);
return this.on('devtools-opened', function() {
return this._loadDevToolsExtensions(Object.keys(extensionInfoMap).map(function(key) {
return extensionInfoMap[key];
}));
});
};
});

View file

@ -1,37 +0,0 @@
{ipcMain} = require 'electron'
{desktopCapturer} = process.atomBinding 'desktop_capturer'
deepEqual = (opt1, opt2) ->
return JSON.stringify(opt1) is JSON.stringify(opt2)
# A queue for holding all requests from renderer process.
requestsQueue = []
ipcMain.on 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, id) ->
request = id: id, options: {captureWindow, captureScreen, thumbnailSize}, webContents: event.sender
requestsQueue.push request
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize if requestsQueue.length is 1
# If the WebContents is destroyed before receiving result, just remove the
# reference from requestsQueue to make the module not send the result to it.
event.sender.once 'destroyed', ->
request.webContents = null
desktopCapturer.emit = (event, name, sources) ->
# Receiving sources result from main process, now send them back to renderer.
handledRequest = requestsQueue.shift 0
result = ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() } for source in sources)
handledRequest.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{handledRequest.id}", result
# Check the queue to see whether there is other same request. If has, handle
# it for reducing redunplicated `desktopCaptuer.startHandling` calls.
unhandledRequestsQueue = []
for request in requestsQueue
if deepEqual handledRequest.options, request.options
request.webContents?.send "ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_#{request.id}", errorMessage, result
else
unhandledRequestsQueue.push request
requestsQueue = unhandledRequestsQueue
# If the requestsQueue is not empty, start a new request handling.
if requestsQueue.length > 0
{captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize

View file

@ -0,0 +1,75 @@
const ipcMain = require('electron').ipcMain;
const desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer;
var deepEqual = function(opt1, opt2) {
return JSON.stringify(opt1) === JSON.stringify(opt2);
};
// A queue for holding all requests from renderer process.
var requestsQueue = [];
ipcMain.on('ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function(event, captureWindow, captureScreen, thumbnailSize, id) {
var request;
request = {
id: id,
options: {
captureWindow: captureWindow,
captureScreen: captureScreen,
thumbnailSize: thumbnailSize
},
webContents: event.sender
};
requestsQueue.push(request);
if (requestsQueue.length === 1) {
desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize);
}
// If the WebContents is destroyed before receiving result, just remove the
// reference from requestsQueue to make the module not send the result to it.
return event.sender.once('destroyed', function() {
return request.webContents = null;
});
});
desktopCapturer.emit = function(event, name, sources) {
// Receiving sources result from main process, now send them back to renderer.
var captureScreen, captureWindow, handledRequest, i, len, ref, ref1, ref2, request, result, source, thumbnailSize, unhandledRequestsQueue;
handledRequest = requestsQueue.shift(0);
result = (function() {
var i, len, results;
results = [];
for (i = 0, len = sources.length; i < len; i++) {
source = sources[i];
results.push({
id: source.id,
name: source.name,
thumbnail: source.thumbnail.toDataUrl()
});
}
return results;
})();
if ((ref = handledRequest.webContents) != null) {
ref.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + handledRequest.id, result);
}
// Check the queue to see whether there is other same request. If has, handle
// it for reducing redunplicated `desktopCaptuer.startHandling` calls.
unhandledRequestsQueue = [];
for (i = 0, len = requestsQueue.length; i < len; i++) {
request = requestsQueue[i];
if (deepEqual(handledRequest.options, request.options)) {
if ((ref1 = request.webContents) != null) {
ref1.send("ATOM_RENDERER_DESKTOP_CAPTURER_RESULT_" + request.id, errorMessage, result);
}
} else {
unhandledRequestsQueue.push(request);
}
}
requestsQueue = unhandledRequestsQueue;
// If the requestsQueue is not empty, start a new request handling.
if (requestsQueue.length > 0) {
ref2 = requestsQueue[0].options, captureWindow = ref2.captureWindow, captureScreen = ref2.captureScreen, thumbnailSize = ref2.thumbnailSize;
return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize);
}
};

View file

@ -1,174 +0,0 @@
{ipcMain, webContents} = require 'electron'
webViewManager = null # Doesn't exist in early initialization.
supportedWebViewEvents = [
'load-commit'
'did-finish-load'
'did-fail-load'
'did-frame-finish-load'
'did-start-loading'
'did-stop-loading'
'did-get-response-details'
'did-get-redirect-request'
'dom-ready'
'console-message'
'devtools-opened'
'devtools-closed'
'devtools-focused'
'new-window'
'will-navigate'
'did-navigate'
'did-navigate-in-page'
'close'
'crashed'
'gpu-crashed'
'plugin-crashed'
'destroyed'
'page-title-updated'
'page-favicon-updated'
'enter-html-full-screen'
'leave-html-full-screen'
'media-started-playing'
'media-paused'
'found-in-page'
'did-change-theme-color'
]
nextInstanceId = 0
guestInstances = {}
embedderElementsMap = {}
reverseEmbedderElementsMap = {}
# Moves the last element of array to the first one.
moveLastToFirst = (list) ->
list.unshift list.pop()
# Generate guestInstanceId.
getNextInstanceId = (webContents) ->
++nextInstanceId
# Create a new guest instance.
createGuest = (embedder, params) ->
webViewManager ?= process.atomBinding 'web_view_manager'
id = getNextInstanceId embedder
guest = webContents.create {isGuest: true, partition: params.partition, embedder}
guestInstances[id] = {guest, embedder}
# Destroy guest when the embedder is gone or navigated.
destroyEvents = ['destroyed', 'crashed', 'did-navigate']
destroy = ->
destroyGuest embedder, id if guestInstances[id]?
for event in destroyEvents
embedder.once event, destroy
# Users might also listen to the crashed event, so We must ensure the guest
# is destroyed before users' listener gets called. It is done by moving our
# listener to the first one in queue.
listeners = embedder._events[event]
moveLastToFirst listeners if Array.isArray listeners
guest.once 'destroyed', ->
embedder.removeListener event, destroy for event in destroyEvents
# Init guest web view after attached.
guest.once 'did-attach', ->
params = @attachParams
delete @attachParams
@viewInstanceId = params.instanceId
@setSize
normal:
width: params.elementWidth, height: params.elementHeight
enableAutoSize: params.autosize
min:
width: params.minwidth, height: params.minheight
max:
width: params.maxwidth, height: params.maxheight
if params.src
opts = {}
opts.httpReferrer = params.httpreferrer if params.httpreferrer
opts.userAgent = params.useragent if params.useragent
@loadURL params.src, opts
if params.allowtransparency?
@setAllowTransparency params.allowtransparency
guest.allowPopups = params.allowpopups
# Dispatch events to embedder.
for event in supportedWebViewEvents
do (event) ->
guest.on event, (_, args...) ->
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{guest.viewInstanceId}", event, args...
# Dispatch guest's IPC messages to embedder.
guest.on 'ipc-message-host', (_, packed) ->
[channel, args...] = packed
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{guest.viewInstanceId}", channel, args...
# Autosize.
guest.on 'size-changed', (_, args...) ->
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{guest.viewInstanceId}", args...
id
# Attach the guest to an element of embedder.
attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
guest = guestInstances[guestInstanceId].guest
# Destroy the old guest when attaching.
key = "#{embedder.getId()}-#{elementInstanceId}"
oldGuestInstanceId = embedderElementsMap[key]
if oldGuestInstanceId?
# Reattachment to the same guest is not currently supported.
return unless oldGuestInstanceId != guestInstanceId
return unless guestInstances[oldGuestInstanceId]?
destroyGuest embedder, oldGuestInstanceId
webPreferences =
guestInstanceId: guestInstanceId
nodeIntegration: params.nodeintegration ? false
plugins: params.plugins
webSecurity: !params.disablewebsecurity
webPreferences.preloadURL = params.preload if params.preload
webViewManager.addGuest guestInstanceId, elementInstanceId, embedder, guest, webPreferences
guest.attachParams = params
embedderElementsMap[key] = guestInstanceId
reverseEmbedderElementsMap[guestInstanceId] = key
# Destroy an existing guest instance.
destroyGuest = (embedder, id) ->
webViewManager.removeGuest embedder, id
guestInstances[id].guest.destroy()
delete guestInstances[id]
key = reverseEmbedderElementsMap[id]
if key?
delete reverseEmbedderElementsMap[id]
delete embedderElementsMap[key]
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', (event, params, requestId) ->
event.sender.send "ATOM_SHELL_RESPONSE_#{requestId}", createGuest(event.sender, params)
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', (event, elementInstanceId, guestInstanceId, params) ->
attachGuest event.sender, elementInstanceId, guestInstanceId, params
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', (event, id) ->
destroyGuest event.sender, id
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
guestInstances[id]?.guest.setSize params
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
guestInstances[id]?.guest.setAllowTransparency allowtransparency
# Returns WebContents from its guest id.
exports.getGuest = (id) ->
guestInstances[id]?.guest
# Returns the embedder of the guest.
exports.getEmbedder = (id) ->
guestInstances[id]?.embedder

View file

@ -0,0 +1,217 @@
const ipcMain = require('electron').ipcMain;
const webContents = require('electron').webContents;
var slice = [].slice;
// Doesn't exist in early initialization.
var webViewManager = null;
var supportedWebViewEvents = ['load-commit', 'did-finish-load', 'did-fail-load', 'did-frame-finish-load', 'did-start-loading', 'did-stop-loading', 'did-get-response-details', 'did-get-redirect-request', 'dom-ready', 'console-message', 'devtools-opened', 'devtools-closed', 'devtools-focused', 'new-window', 'will-navigate', 'did-navigate', 'did-navigate-in-page', 'close', 'crashed', 'gpu-crashed', 'plugin-crashed', 'destroyed', 'page-title-updated', 'page-favicon-updated', 'enter-html-full-screen', 'leave-html-full-screen', 'media-started-playing', 'media-paused', 'found-in-page', 'did-change-theme-color'];
var nextInstanceId = 0;
var guestInstances = {};
var embedderElementsMap = {};
var reverseEmbedderElementsMap = {};
// Moves the last element of array to the first one.
var moveLastToFirst = function(list) {
return list.unshift(list.pop());
};
// Generate guestInstanceId.
var getNextInstanceId = function(webContents) {
return ++nextInstanceId;
};
// Create a new guest instance.
var createGuest = function(embedder, params) {
var destroy, destroyEvents, event, fn, guest, i, id, j, len, len1, listeners;
if (webViewManager == null) {
webViewManager = process.atomBinding('web_view_manager');
}
id = getNextInstanceId(embedder);
guest = webContents.create({
isGuest: true,
partition: params.partition,
embedder: embedder
});
guestInstances[id] = {
guest: guest,
embedder: embedder
};
// Destroy guest when the embedder is gone or navigated.
destroyEvents = ['will-destroy', 'crashed', 'did-navigate'];
destroy = function() {
if (guestInstances[id] != null) {
return destroyGuest(embedder, id);
}
};
for (i = 0, len = destroyEvents.length; i < len; i++) {
event = destroyEvents[i];
embedder.once(event, destroy);
// Users might also listen to the crashed event, so We must ensure the guest
// is destroyed before users' listener gets called. It is done by moving our
// listener to the first one in queue.
listeners = embedder._events[event];
if (Array.isArray(listeners)) {
moveLastToFirst(listeners);
}
}
guest.once('destroyed', function() {
var j, len1, results;
results = [];
for (j = 0, len1 = destroyEvents.length; j < len1; j++) {
event = destroyEvents[j];
results.push(embedder.removeListener(event, destroy));
}
return results;
});
// Init guest web view after attached.
guest.once('did-attach', function() {
var opts;
params = this.attachParams;
delete this.attachParams;
this.viewInstanceId = params.instanceId;
this.setSize({
normal: {
width: params.elementWidth,
height: params.elementHeight
},
enableAutoSize: params.autosize,
min: {
width: params.minwidth,
height: params.minheight
},
max: {
width: params.maxwidth,
height: params.maxheight
}
});
if (params.src) {
opts = {};
if (params.httpreferrer) {
opts.httpReferrer = params.httpreferrer;
}
if (params.useragent) {
opts.userAgent = params.useragent;
}
this.loadURL(params.src, opts);
}
if (params.allowtransparency != null) {
this.setAllowTransparency(params.allowtransparency);
}
return guest.allowPopups = params.allowpopups;
});
// Dispatch events to embedder.
fn = function(event) {
return guest.on(event, function() {
var _, args;
_ = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-" + guest.viewInstanceId, event].concat(slice.call(args)));
});
};
for (j = 0, len1 = supportedWebViewEvents.length; j < len1; j++) {
event = supportedWebViewEvents[j];
fn(event);
}
// Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host', function(_, packed) {
var args, channel;
channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-" + guest.viewInstanceId, channel].concat(slice.call(args)));
});
// Autosize.
guest.on('size-changed', function() {
var _, args;
_ = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return embedder.send.apply(embedder, ["ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-" + guest.viewInstanceId].concat(slice.call(args)));
});
return id;
};
// Attach the guest to an element of embedder.
var attachGuest = function(embedder, elementInstanceId, guestInstanceId, params) {
var guest, key, oldGuestInstanceId, ref1, webPreferences;
guest = guestInstances[guestInstanceId].guest;
// Destroy the old guest when attaching.
key = (embedder.getId()) + "-" + elementInstanceId;
oldGuestInstanceId = embedderElementsMap[key];
if (oldGuestInstanceId != null) {
// Reattachment to the same guest is not currently supported.
if (oldGuestInstanceId === guestInstanceId) {
return;
}
if (guestInstances[oldGuestInstanceId] == null) {
return;
}
destroyGuest(embedder, oldGuestInstanceId);
}
webPreferences = {
guestInstanceId: guestInstanceId,
nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
plugins: params.plugins,
webSecurity: !params.disablewebsecurity
};
if (params.preload) {
webPreferences.preloadURL = params.preload;
}
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences);
guest.attachParams = params;
embedderElementsMap[key] = guestInstanceId;
return reverseEmbedderElementsMap[guestInstanceId] = key;
};
// Destroy an existing guest instance.
var destroyGuest = function(embedder, id) {
var key;
webViewManager.removeGuest(embedder, id);
guestInstances[id].guest.destroy();
delete guestInstances[id];
key = reverseEmbedderElementsMap[id];
if (key != null) {
delete reverseEmbedderElementsMap[id];
return delete embedderElementsMap[key];
}
};
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_CREATE_GUEST', function(event, params, requestId) {
return event.sender.send("ATOM_SHELL_RESPONSE_" + requestId, createGuest(event.sender, params));
});
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_ATTACH_GUEST', function(event, elementInstanceId, guestInstanceId, params) {
return attachGuest(event.sender, elementInstanceId, guestInstanceId, params);
});
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_DESTROY_GUEST', function(event, id) {
return destroyGuest(event.sender, id);
});
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', function(event, id, params) {
var ref1;
return (ref1 = guestInstances[id]) != null ? ref1.guest.setSize(params) : void 0;
});
ipcMain.on('ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', function(event, id, allowtransparency) {
var ref1;
return (ref1 = guestInstances[id]) != null ? ref1.guest.setAllowTransparency(allowtransparency) : void 0;
});
// Returns WebContents from its guest id.
exports.getGuest = function(id) {
var ref1;
return (ref1 = guestInstances[id]) != null ? ref1.guest : void 0;
};
// Returns the embedder of the guest.
exports.getEmbedder = function(id) {
var ref1;
return (ref1 = guestInstances[id]) != null ? ref1.embedder : void 0;
};

View file

@ -1,86 +0,0 @@
{ipcMain, BrowserWindow} = require 'electron'
v8Util = process.atomBinding 'v8_util'
frameToGuest = {}
# Copy attribute of |parent| to |child| if it is not defined in |child|.
mergeOptions = (child, parent) ->
for own key, value of parent when key not of child
if typeof value is 'object'
child[key] = mergeOptions {}, value
else
child[key] = value
child
# Merge |options| with the |embedder|'s window's options.
mergeBrowserWindowOptions = (embedder, options) ->
if embedder.browserWindowOptions?
# Inherit the original options if it is a BrowserWindow.
mergeOptions options, embedder.browserWindowOptions
else
# Or only inherit web-preferences if it is a webview.
options.webPreferences ?= {}
mergeOptions options.webPreferences, embedder.getWebPreferences()
options
# Create a new guest created by |embedder| with |options|.
createGuest = (embedder, url, frameName, options) ->
guest = frameToGuest[frameName]
if frameName and guest?
guest.loadURL url
return guest.id
# Remember the embedder window's id.
options.webPreferences ?= {}
options.webPreferences.openerId = BrowserWindow.fromWebContents(embedder)?.id
guest = new BrowserWindow(options)
guest.loadURL url
# When |embedder| is destroyed we should also destroy attached guest, and if
# guest is closed by user then we should prevent |embedder| from double
# closing guest.
guestId = guest.id
closedByEmbedder = ->
guest.removeListener 'closed', closedByUser
guest.destroy()
closedByUser = ->
embedder.send "ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_#{guestId}"
embedder.removeListener 'render-view-deleted', closedByEmbedder
embedder.once 'render-view-deleted', closedByEmbedder
guest.once 'closed', closedByUser
if frameName
frameToGuest[frameName] = guest
guest.frameName = frameName
guest.once 'closed', ->
delete frameToGuest[frameName]
guest.id
# Routed window.open messages.
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) ->
[url, frameName, options] = args
options = mergeBrowserWindowOptions event.sender, options
event.sender.emit 'new-window', event, url, frameName, 'new-window', options
if (event.sender.isGuest() and not event.sender.allowPopups) or event.defaultPrevented
event.returnValue = null
else
event.returnValue = createGuest event.sender, url, frameName, options
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', (event, guestId) ->
BrowserWindow.fromId(guestId)?.destroy()
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestId, method, args...) ->
BrowserWindow.fromId(guestId)?[method] args...
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestId, message, targetOrigin, sourceOrigin) ->
sourceId = BrowserWindow.fromWebContents(event.sender)?.id
return unless sourceId?
guestContents = BrowserWindow.fromId(guestId)?.webContents
if guestContents?.getURL().indexOf(targetOrigin) is 0 or targetOrigin is '*'
guestContents?.send 'ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestId, method, args...) ->
BrowserWindow.fromId(guestId)?.webContents?[method] args...

View file

@ -0,0 +1,126 @@
const ipcMain = require('electron').ipcMain;
const BrowserWindow = require('electron').BrowserWindow;
const v8Util = process.atomBinding('v8_util');
var hasProp = {}.hasOwnProperty;
var slice = [].slice;
var frameToGuest = {};
// Copy attribute of |parent| to |child| if it is not defined in |child|.
var mergeOptions = function(child, parent) {
var key, value;
for (key in parent) {
if (!hasProp.call(parent, key)) continue;
value = parent[key];
if (!(key in child)) {
if (typeof value === 'object') {
child[key] = mergeOptions({}, value);
} else {
child[key] = value;
}
}
}
return child;
};
// Merge |options| with the |embedder|'s window's options.
var mergeBrowserWindowOptions = function(embedder, options) {
if (embedder.browserWindowOptions != null) {
// Inherit the original options if it is a BrowserWindow.
mergeOptions(options, embedder.browserWindowOptions);
} else {
// Or only inherit web-preferences if it is a webview.
if (options.webPreferences == null) {
options.webPreferences = {};
}
mergeOptions(options.webPreferences, embedder.getWebPreferences());
}
return options;
};
// Create a new guest created by |embedder| with |options|.
var createGuest = function(embedder, url, frameName, options) {
var closedByEmbedder, closedByUser, guest, guestId, ref1;
guest = frameToGuest[frameName];
if (frameName && (guest != null)) {
guest.loadURL(url);
return guest.id;
}
// Remember the embedder window's id.
if (options.webPreferences == null) {
options.webPreferences = {};
}
options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0;
guest = new BrowserWindow(options);
guest.loadURL(url);
// When |embedder| is destroyed we should also destroy attached guest, and if
// guest is closed by user then we should prevent |embedder| from double
// closing guest.
guestId = guest.id;
closedByEmbedder = function() {
guest.removeListener('closed', closedByUser);
return guest.destroy();
};
closedByUser = function() {
embedder.send("ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_" + guestId);
return embedder.removeListener('render-view-deleted', closedByEmbedder);
};
embedder.once('render-view-deleted', closedByEmbedder);
guest.once('closed', closedByUser);
if (frameName) {
frameToGuest[frameName] = guest;
guest.frameName = frameName;
guest.once('closed', function() {
return delete frameToGuest[frameName];
});
}
return guest.id;
};
// Routed window.open messages.
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function() {
var args, event, frameName, options, url;
event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
url = args[0], frameName = args[1], options = args[2];
options = mergeBrowserWindowOptions(event.sender, options);
event.sender.emit('new-window', event, url, frameName, 'new-window', options);
if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
return event.returnValue = null;
} else {
return event.returnValue = createGuest(event.sender, url, frameName, options);
}
});
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function(event, guestId) {
var ref1;
return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0;
});
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function() {
var args, event, guestId, method, ref1;
event = arguments[0], guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
return (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0;
});
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function(event, guestId, message, targetOrigin, sourceOrigin) {
var guestContents, ref1, ref2, sourceId;
sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0;
if (sourceId == null) {
return;
}
guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0;
if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') {
return guestContents != null ? guestContents.send('ATOM_SHELL_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0;
}
});
ipcMain.on('ATOM_SHELL_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function() {
var args, event, guestId, method, ref1, ref2;
event = arguments[0], guestId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
return (ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0;
});

View file

@ -1,118 +0,0 @@
fs = require 'fs'
path = require 'path'
util = require 'util'
Module = require 'module'
# We modified the original process.argv to let node.js load the atom.js,
# we need to restore it here.
process.argv.splice 1, 1
# Clear search paths.
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')
# Import common settings.
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
globalPaths = Module.globalPaths
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
# Expose public APIs.
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib', 'exports')
if process.platform is 'win32'
# Redirect node's console to use our own implementations, since node can not
# handle console output when running as GUI program.
consoleLog = (args...) ->
process.log util.format(args...) + "\n"
streamWrite = (chunk, encoding, callback) ->
chunk = chunk.toString(encoding) if Buffer.isBuffer chunk
process.log chunk
callback() if callback
true
console.log = console.error = console.warn = consoleLog
process.stdout.write = process.stderr.write = streamWrite
# Always returns EOF for stdin stream.
Readable = require('stream').Readable
stdin = new Readable
stdin.push null
process.__defineGetter__ 'stdin', -> stdin
# Don't quit on fatal error.
process.on 'uncaughtException', (error) ->
# Do nothing if the user has a custom uncaught exception handler.
if process.listeners('uncaughtException').length > 1
return
# Show error in GUI.
{dialog} = require 'electron'
stack = error.stack ? "#{error.name}: #{error.message}"
message = "Uncaught Exception:\n#{stack}"
dialog.showErrorBox 'A JavaScript error occurred in the main process', message
# Emit 'exit' event on quit.
{app} = require 'electron'
app.on 'quit', (event, exitCode) ->
process.emit 'exit', exitCode
# Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit
# Load the RPC server.
require './rpc-server'
# Load the guest view manager.
require './guest-view-manager'
require './guest-window-manager'
# Now we try to load app's package.json.
packageJson = null
searchPaths = [ 'app', 'app.asar', 'default_app' ]
for packagePath in searchPaths
try
packagePath = path.join process.resourcesPath, packagePath
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')))
break
catch e
continue
unless packageJson?
process.nextTick -> process.exit 1
throw new Error("Unable to find a valid app")
# Set application's version.
app.setVersion packageJson.version if packageJson.version?
# Set application's name.
if packageJson.productName?
app.setName packageJson.productName
else if packageJson.name?
app.setName packageJson.name
# Set application's desktop name.
if packageJson.desktopName?
app.setDesktopName packageJson.desktopName
else
app.setDesktopName "#{app.getName()}.desktop"
# Chrome 42 disables NPAPI plugins by default, reenable them here
app.commandLine.appendSwitch 'enable-npapi'
# Set the user path according to application's name.
app.setPath 'userData', path.join(app.getPath('appData'), app.getName())
app.setPath 'userCache', path.join(app.getPath('cache'), app.getName())
app.setAppPath packagePath
# Load the chrome extension support.
require './chrome-extension'
# Load internal desktop-capturer module.
require './desktop-capturer'
# Set main startup script of the app.
mainStartupScript = packageJson.main or 'index.js'
# Finally load app's main.js and transfer control to C++.
Module._load path.join(packagePath, mainStartupScript), Module, true

152
atom/browser/lib/init.js Normal file
View file

@ -0,0 +1,152 @@
const fs = require('fs');
const path = require('path');
const util = require('util');
const Module = require('module');
var slice = [].slice;
// We modified the original process.argv to let node.js load the atom.js,
// we need to restore it here.
process.argv.splice(1, 1);
// Clear search paths.
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths'));
// Import common settings.
require(path.resolve(__dirname, '..', '..', 'common', 'lib', 'init'));
var globalPaths = Module.globalPaths;
if (!process.env.ELECTRON_HIDE_INTERNAL_MODULES) {
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib'));
}
// Expose public APIs.
globalPaths.push(path.resolve(__dirname, '..', 'api', 'lib', 'exports'));
if (process.platform === 'win32') {
// Redirect node's console to use our own implementations, since node can not
// handle console output when running as GUI program.
var consoleLog = function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
return process.log(util.format.apply(util, args) + "\n");
};
var streamWrite = function(chunk, encoding, callback) {
if (Buffer.isBuffer(chunk)) {
chunk = chunk.toString(encoding);
}
process.log(chunk);
if (callback) {
callback();
}
return true;
};
console.log = console.error = console.warn = consoleLog;
process.stdout.write = process.stderr.write = streamWrite;
// Always returns EOF for stdin stream.
var Readable = require('stream').Readable;
var stdin = new Readable;
stdin.push(null);
process.__defineGetter__('stdin', function() {
return stdin;
});
}
// Don't quit on fatal error.
process.on('uncaughtException', function(error) {
// Do nothing if the user has a custom uncaught exception handler.
var dialog, message, ref, stack;
if (process.listeners('uncaughtException').length > 1) {
return;
}
// Show error in GUI.
dialog = require('electron').dialog;
stack = (ref = error.stack) != null ? ref : error.name + ": " + error.message;
message = "Uncaught Exception:\n" + stack;
return dialog.showErrorBox('A JavaScript error occurred in the main process', message);
});
// Emit 'exit' event on quit.
var app = require('electron').app;
app.on('quit', function(event, exitCode) {
return process.emit('exit', exitCode);
});
// Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit;
// Load the RPC server.
require('./rpc-server');
// Load the guest view manager.
require('./guest-view-manager');
require('./guest-window-manager');
// Now we try to load app's package.json.
var packageJson = null;
var searchPaths = ['app', 'app.asar', 'default_app'];
var i, len, packagePath;
for (i = 0, len = searchPaths.length; i < len; i++) {
packagePath = searchPaths[i];
try {
packagePath = path.join(process.resourcesPath, packagePath);
packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json')));
break;
} catch (error) {
continue;
}
}
if (packageJson == null) {
process.nextTick(function() {
return process.exit(1);
});
throw new Error("Unable to find a valid app");
}
// Set application's version.
if (packageJson.version != null) {
app.setVersion(packageJson.version);
}
// Set application's name.
if (packageJson.productName != null) {
app.setName(packageJson.productName);
} else if (packageJson.name != null) {
app.setName(packageJson.name);
}
// Set application's desktop name.
if (packageJson.desktopName != null) {
app.setDesktopName(packageJson.desktopName);
} else {
app.setDesktopName((app.getName()) + ".desktop");
}
// Chrome 42 disables NPAPI plugins by default, reenable them here
app.commandLine.appendSwitch('enable-npapi');
// Set the user path according to application's name.
app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()));
app.setAppPath(packagePath);
// Load the chrome extension support.
require('./chrome-extension');
// Load internal desktop-capturer module.
require('./desktop-capturer');
// Set main startup script of the app.
var mainStartupScript = packageJson.main || 'index.js';
// Finally load app's main.js and transfer control to C++.
Module._load(path.join(packagePath, mainStartupScript), Module, true);

View file

@ -1,67 +0,0 @@
{EventEmitter} = require 'events'
v8Util = process.atomBinding 'v8_util'
class ObjectsRegistry extends EventEmitter
constructor: ->
@setMaxListeners Number.MAX_VALUE
@nextId = 0
# Stores all objects by ref-counting.
# (id) => {object, count}
@storage = {}
# Stores the IDs of objects referenced by WebContents.
# (webContentsId) => {(id) => (count)}
@owners = {}
# Register a new object, the object would be kept referenced until you release
# it explicitly.
add: (webContentsId, obj) ->
id = @saveToStorage obj
# Remember the owner.
@owners[webContentsId] ?= {}
@owners[webContentsId][id] ?= 0
@owners[webContentsId][id]++
# Returns object's id
id
# Get an object according to its ID.
get: (id) ->
@storage[id]?.object
# Dereference an object according to its ID.
remove: (webContentsId, id) ->
@dereference id, 1
# Also reduce the count in owner.
pointer = @owners[webContentsId]
return unless pointer?
--pointer[id]
delete pointer[id] if pointer[id] is 0
# Clear all references to objects refrenced by the WebContents.
clear: (webContentsId) ->
@emit "clear-#{webContentsId}"
return unless @owners[webContentsId]?
@dereference id, count for id, count of @owners[webContentsId]
delete @owners[webContentsId]
# Private: Saves the object into storage and assigns an ID for it.
saveToStorage: (object) ->
id = v8Util.getHiddenValue object, 'atomId'
unless id
id = ++@nextId
@storage[id] = {count: 0, object}
v8Util.setHiddenValue object, 'atomId', id
++@storage[id].count
id
# Private: Dereference the object from store.
dereference: (id, count) ->
pointer = @storage[id]
return unless pointer?
pointer.count -= count
if pointer.count is 0
v8Util.deleteHiddenValue pointer.object, 'atomId'
delete @storage[id]
module.exports = new ObjectsRegistry

View file

@ -0,0 +1,115 @@
const EventEmitter = require('events').EventEmitter;
const v8Util = process.atomBinding('v8_util');
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
var hasProp = {}.hasOwnProperty;
var ObjectsRegistry = (function(superClass) {
extend(ObjectsRegistry, superClass);
function ObjectsRegistry() {
this.setMaxListeners(Number.MAX_VALUE);
this.nextId = 0;
// Stores all objects by ref-counting.
// (id) => {object, count}
this.storage = {};
// Stores the IDs of objects referenced by WebContents.
// (webContentsId) => {(id) => (count)}
this.owners = {};
}
// Register a new object, the object would be kept referenced until you release
// it explicitly.
ObjectsRegistry.prototype.add = function(webContentsId, obj) {
var base, base1, id;
id = this.saveToStorage(obj);
// Remember the owner.
if ((base = this.owners)[webContentsId] == null) {
base[webContentsId] = {};
}
if ((base1 = this.owners[webContentsId])[id] == null) {
base1[id] = 0;
}
this.owners[webContentsId][id]++;
// Returns object's id
return id;
};
// Get an object according to its ID.
ObjectsRegistry.prototype.get = function(id) {
var ref;
return (ref = this.storage[id]) != null ? ref.object : void 0;
};
// Dereference an object according to its ID.
ObjectsRegistry.prototype.remove = function(webContentsId, id) {
var pointer;
this.dereference(id, 1);
// Also reduce the count in owner.
pointer = this.owners[webContentsId];
if (pointer == null) {
return;
}
--pointer[id];
if (pointer[id] === 0) {
return delete pointer[id];
}
};
// Clear all references to objects refrenced by the WebContents.
ObjectsRegistry.prototype.clear = function(webContentsId) {
var count, id, ref;
this.emit("clear-" + webContentsId);
if (this.owners[webContentsId] == null) {
return;
}
ref = this.owners[webContentsId];
for (id in ref) {
count = ref[id];
this.dereference(id, count);
}
return delete this.owners[webContentsId];
};
// Private: Saves the object into storage and assigns an ID for it.
ObjectsRegistry.prototype.saveToStorage = function(object) {
var id;
id = v8Util.getHiddenValue(object, 'atomId');
if (!id) {
id = ++this.nextId;
this.storage[id] = {
count: 0,
object: object
};
v8Util.setHiddenValue(object, 'atomId', id);
}
++this.storage[id].count;
return id;
};
// Private: Dereference the object from store.
ObjectsRegistry.prototype.dereference = function(id, count) {
var pointer;
pointer = this.storage[id];
if (pointer == null) {
return;
}
pointer.count -= count;
if (pointer.count === 0) {
v8Util.deleteHiddenValue(pointer.object, 'atomId');
return delete this.storage[id];
}
};
return ObjectsRegistry;
})(EventEmitter);
module.exports = new ObjectsRegistry;

View file

@ -1,231 +0,0 @@
path = require 'path'
electron = require 'electron'
{ipcMain} = electron
objectsRegistry = require './objects-registry'
v8Util = process.atomBinding 'v8_util'
{IDWeakMap} = process.atomBinding 'id_weak_map'
# Convert a real value into meta data.
valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta = type: typeof value
meta.type = 'buffer' if Buffer.isBuffer value
meta.type = 'value' if value is null
meta.type = 'array' if Array.isArray value
meta.type = 'error' if value instanceof Error
meta.type = 'date' if value instanceof Date
meta.type = 'promise' if value?.constructor.name is 'Promise'
# Treat simple objects as value.
if optimizeSimpleObject and meta.type is 'object' and v8Util.getHiddenValue value, 'simple'
meta.type = 'value'
# Treat the arguments object as array.
meta.type = 'array' if meta.type is 'object' and value.callee? and value.length?
if meta.type is 'array'
meta.members = []
meta.members.push valueToMeta(sender, el) for el in value
else if meta.type is 'object' or meta.type is 'function'
meta.name = value.constructor.name
# Reference the original value if it's an object, because when it's
# passed to renderer we would assume the renderer keeps a reference of
# it.
meta.id = objectsRegistry.add sender.getId(), value
meta.members = ({name, type: typeof field} for name, field of value)
else if meta.type is 'buffer'
meta.value = Array::slice.call value, 0
else if meta.type is 'promise'
meta.then = valueToMeta sender, value.then.bind(value)
else if meta.type is 'error'
meta.members = plainObjectToMeta value
# Error.name is not part of own properties.
meta.members.push {name: 'name', value: value.name}
else if meta.type is 'date'
meta.value = value.getTime()
else
meta.type = 'value'
meta.value = value
meta
# Convert object to meta by value.
plainObjectToMeta = (obj) ->
Object.getOwnPropertyNames(obj).map (name) -> {name, value: obj[name]}
# Convert Error into meta data.
exceptionToMeta = (error) ->
type: 'exception', message: error.message, stack: (error.stack || error)
# Convert array of meta data from renderer into array of real values.
unwrapArgs = (sender, args) ->
metaToValue = (meta) ->
switch meta.type
when 'value' then meta.value
when 'remote-object' then objectsRegistry.get meta.id
when 'array' then unwrapArgs sender, meta.value
when 'buffer' then new Buffer(meta.value)
when 'date' then new Date(meta.value)
when 'promise' then Promise.resolve(then: metaToValue(meta.then))
when 'object'
ret = v8Util.createObjectWithName meta.name
for member in meta.members
ret[member.name] = metaToValue(member.value)
ret
when 'function-with-return-value'
returnValue = metaToValue meta.value
-> returnValue
when 'function'
# Cache the callbacks in renderer.
unless sender.callbacks
sender.callbacks = new IDWeakMap
sender.on 'render-view-deleted', ->
sender.callbacks.clear()
return sender.callbacks.get meta.id if sender.callbacks.has meta.id
rendererReleased = false
objectsRegistry.once "clear-#{sender.getId()}", ->
rendererReleased = true
ret = ->
if rendererReleased
throw new Error("Attempting to call a function in a renderer window
that has been closed or released. Function provided here: #{meta.location}.")
sender.send 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, arguments)
v8Util.setDestructor ret, ->
return if rendererReleased
sender.callbacks.remove meta.id
sender.send 'ATOM_RENDERER_RELEASE_CALLBACK', meta.id
sender.callbacks.set meta.id, ret
ret
else throw new TypeError("Unknown type: #{meta.type}")
args.map metaToValue
# Call a function and send reply asynchronously if it's a an asynchronous
# style function and the caller didn't pass a callback.
callFunction = (event, func, caller, args) ->
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
funcPassedCallback = typeof args[args.length - 1] is 'function'
try
if funcMarkedAsync and not funcPassedCallback
args.push (ret) ->
event.returnValue = valueToMeta event.sender, ret, true
func.apply caller, args
else
ret = func.apply caller, args
event.returnValue = valueToMeta event.sender, ret, true
catch e
# Catch functions thrown further down in function invocation and wrap
# them with the function name so it's easier to trace things like
# `Error processing argument -1.`
funcName = func.name ? "anonymous"
throw new Error("Could not call remote function `#{funcName}`.
Check that the function signature is correct.
Underlying error: #{e.message}")
# Send by BrowserWindow when its render view is deleted.
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (id) ->
objectsRegistry.clear id
ipcMain.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
try
event.returnValue = valueToMeta event.sender, process.mainModule.require(module)
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_GET_BUILTIN', (event, module) ->
try
event.returnValue = valueToMeta event.sender, electron[module]
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
try
event.returnValue = valueToMeta event.sender, global[name]
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
try
event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow()
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
event.returnValue = valueToMeta event.sender, event.sender
ipcMain.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
try
args = unwrapArgs event.sender, args
constructor = objectsRegistry.get id
# Call new with array of arguments.
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
try
args = unwrapArgs event.sender, args
func = objectsRegistry.get id
callFunction event, func, global, args
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
try
args = unwrapArgs event.sender, args
constructor = objectsRegistry.get(id)[method]
# Call new with array of arguments.
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
try
args = unwrapArgs event.sender, args
obj = objectsRegistry.get id
callFunction event, obj[method], obj, args
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
try
obj = objectsRegistry.get id
obj[name] = value
event.returnValue = null
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
try
obj = objectsRegistry.get id
event.returnValue = valueToMeta event.sender, obj[name]
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
objectsRegistry.remove event.sender.getId(), id
ipcMain.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
try
guestViewManager = require './guest-view-manager'
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
catch e
event.returnValue = exceptionToMeta e
ipcMain.on 'ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', (event, guestInstanceId, method, args...) ->
try
guestViewManager = require './guest-view-manager'
guest = guestViewManager.getGuest(guestInstanceId)
guest[method].apply(guest, args)
catch e
event.returnValue = exceptionToMeta e

View file

@ -0,0 +1,363 @@
'use strict';
const path = require('path');
const electron = require('electron');
const ipcMain = electron.ipcMain;
const objectsRegistry = require('./objects-registry');
const v8Util = process.atomBinding('v8_util');
const IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap;
var slice = [].slice;
// Convert a real value into meta data.
var valueToMeta = function(sender, value, optimizeSimpleObject) {
var el, field, i, len, meta, name;
if (optimizeSimpleObject == null) {
optimizeSimpleObject = false;
}
meta = {
type: typeof value
};
if (Buffer.isBuffer(value)) {
meta.type = 'buffer';
}
if (value === null) {
meta.type = 'value';
}
if (Array.isArray(value)) {
meta.type = 'array';
}
if (value instanceof Error) {
meta.type = 'error';
}
if (value instanceof Date) {
meta.type = 'date';
}
if ((value != null ? value.constructor.name : void 0) === 'Promise') {
meta.type = 'promise';
}
// Treat simple objects as value.
if (optimizeSimpleObject && meta.type === 'object' && v8Util.getHiddenValue(value, 'simple')) {
meta.type = 'value';
}
// Treat the arguments object as array.
if (meta.type === 'object' && (value.hasOwnProperty('callee')) && (value.length != null)) {
meta.type = 'array';
}
if (meta.type === 'array') {
meta.members = [];
for (i = 0, len = value.length; i < len; i++) {
el = value[i];
meta.members.push(valueToMeta(sender, el));
}
} else if (meta.type === 'object' || meta.type === 'function') {
meta.name = value.constructor.name;
// Reference the original value if it's an object, because when it's
// passed to renderer we would assume the renderer keeps a reference of
// it.
meta.id = objectsRegistry.add(sender.getId(), value);
meta.members = (function() {
var results;
results = [];
for (name in value) {
field = value[name];
results.push({
name: name,
type: typeof field
});
}
return results;
})();
} else if (meta.type === 'buffer') {
meta.value = Array.prototype.slice.call(value, 0);
} else if (meta.type === 'promise') {
meta.then = valueToMeta(sender, value.then.bind(value));
} else if (meta.type === 'error') {
meta.members = plainObjectToMeta(value);
// Error.name is not part of own properties.
meta.members.push({
name: 'name',
value: value.name
});
} else if (meta.type === 'date') {
meta.value = value.getTime();
} else {
meta.type = 'value';
meta.value = value;
}
return meta;
};
// Convert object to meta by value.
var plainObjectToMeta = function(obj) {
return Object.getOwnPropertyNames(obj).map(function(name) {
return {
name: name,
value: obj[name]
};
});
};
// Convert Error into meta data.
var exceptionToMeta = function(error) {
return {
type: 'exception',
message: error.message,
stack: error.stack || error
};
};
// Convert array of meta data from renderer into array of real values.
var unwrapArgs = function(sender, args) {
var metaToValue;
metaToValue = function(meta) {
var i, len, member, ref, rendererReleased, returnValue;
switch (meta.type) {
case 'value':
return meta.value;
case 'remote-object':
return objectsRegistry.get(meta.id);
case 'array':
return unwrapArgs(sender, meta.value);
case 'buffer':
return new Buffer(meta.value);
case 'date':
return new Date(meta.value);
case 'promise':
return Promise.resolve({
then: metaToValue(meta.then)
});
case 'object':
let ret = v8Util.createObjectWithName(meta.name);
ref = meta.members;
for (i = 0, len = ref.length; i < len; i++) {
member = ref[i];
ret[member.name] = metaToValue(member.value);
}
return ret;
case 'function-with-return-value':
returnValue = metaToValue(meta.value);
return function() {
return returnValue;
};
case 'function':
// Cache the callbacks in renderer.
if (!sender.callbacks) {
sender.callbacks = new IDWeakMap;
sender.on('render-view-deleted', function() {
return this.callbacks.clear();
});
}
if (sender.callbacks.has(meta.id))
return sender.callbacks.get(meta.id);
// Prevent the callback from being called when its page is gone.
rendererReleased = false;
sender.once('render-view-deleted', function() {
rendererReleased = true;
});
let callIntoRenderer = function(...args) {
if (rendererReleased)
throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`);
sender.send('ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args));
};
v8Util.setDestructor(callIntoRenderer, function() {
if (!rendererReleased)
sender.send('ATOM_RENDERER_RELEASE_CALLBACK', meta.id);
});
sender.callbacks.set(meta.id, callIntoRenderer);
return callIntoRenderer;
default:
throw new TypeError("Unknown type: " + meta.type);
}
};
return args.map(metaToValue);
};
// Call a function and send reply asynchronously if it's a an asynchronous
// style function and the caller didn't pass a callback.
var callFunction = function(event, func, caller, args) {
var e, error1, funcMarkedAsync, funcName, funcPassedCallback, ref, ret;
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous');
funcPassedCallback = typeof args[args.length - 1] === 'function';
try {
if (funcMarkedAsync && !funcPassedCallback) {
args.push(function(ret) {
return event.returnValue = valueToMeta(event.sender, ret, true);
});
return func.apply(caller, args);
} else {
ret = func.apply(caller, args);
return event.returnValue = valueToMeta(event.sender, ret, true);
}
} catch (error1) {
e = error1;
// Catch functions thrown further down in function invocation and wrap
// them with the function name so it's easier to trace things like
// `Error processing argument -1.`
funcName = (ref = func.name) != null ? ref : "anonymous";
throw new Error("Could not call remote function `" + funcName + "`. Check that the function signature is correct. Underlying error: " + e.message);
}
};
// Send by BrowserWindow when its render view is deleted.
process.on('ATOM_BROWSER_RELEASE_RENDER_VIEW', function(id) {
return objectsRegistry.clear(id);
});
ipcMain.on('ATOM_BROWSER_REQUIRE', function(event, module) {
var e, error1;
try {
return event.returnValue = valueToMeta(event.sender, process.mainModule.require(module));
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_GET_BUILTIN', function(event, module) {
var e, error1;
try {
return event.returnValue = valueToMeta(event.sender, electron[module]);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_GLOBAL', function(event, name) {
var e, error1;
try {
return event.returnValue = valueToMeta(event.sender, global[name]);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_CURRENT_WINDOW', function(event) {
var e, error1;
try {
return event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow());
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_CURRENT_WEB_CONTENTS', function(event) {
return event.returnValue = valueToMeta(event.sender, event.sender);
});
ipcMain.on('ATOM_BROWSER_CONSTRUCTOR', function(event, id, args) {
var constructor, e, error1, obj;
try {
args = unwrapArgs(event.sender, args);
constructor = objectsRegistry.get(id);
// Call new with array of arguments.
// http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
return event.returnValue = valueToMeta(event.sender, obj);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_FUNCTION_CALL', function(event, id, args) {
var e, error1, func;
try {
args = unwrapArgs(event.sender, args);
func = objectsRegistry.get(id);
return callFunction(event, func, global, args);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_MEMBER_CONSTRUCTOR', function(event, id, method, args) {
var constructor, e, error1, obj;
try {
args = unwrapArgs(event.sender, args);
constructor = objectsRegistry.get(id)[method];
// Call new with array of arguments.
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
return event.returnValue = valueToMeta(event.sender, obj);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_MEMBER_CALL', function(event, id, method, args) {
var e, error1, obj;
try {
args = unwrapArgs(event.sender, args);
obj = objectsRegistry.get(id);
return callFunction(event, obj[method], obj, args);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_MEMBER_SET', function(event, id, name, value) {
var e, error1, obj;
try {
obj = objectsRegistry.get(id);
obj[name] = value;
return event.returnValue = null;
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_MEMBER_GET', function(event, id, name) {
var e, error1, obj;
try {
obj = objectsRegistry.get(id);
return event.returnValue = valueToMeta(event.sender, obj[name]);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_DEREFERENCE', function(event, id) {
return objectsRegistry.remove(event.sender.getId(), id);
});
ipcMain.on('ATOM_BROWSER_GUEST_WEB_CONTENTS', function(event, guestInstanceId) {
var e, error1, guestViewManager;
try {
guestViewManager = require('./guest-view-manager');
return event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId));
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});
ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function() {
var args, e, error1, event, guest, guestInstanceId, guestViewManager, method;
event = arguments[0], guestInstanceId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
try {
guestViewManager = require('./guest-view-manager');
guest = guestViewManager.getGuest(guestInstanceId);
return guest[method].apply(guest, args);
} catch (error1) {
e = error1;
return event.returnValue = exceptionToMeta(e);
}
});

View file

@ -143,6 +143,7 @@ class NativeWindow : public base::SupportsUserData,
virtual void SetMenu(ui::MenuModel* menu); virtual void SetMenu(ui::MenuModel* menu);
virtual bool HasModalDialog(); virtual bool HasModalDialog();
virtual gfx::NativeWindow GetNativeWindow() = 0; virtual gfx::NativeWindow GetNativeWindow() = 0;
virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
// Taskbar/Dock APIs. // Taskbar/Dock APIs.
virtual void SetProgressBar(double progress) = 0; virtual void SetProgressBar(double progress) = 0;

View file

@ -65,6 +65,7 @@ class NativeWindowMac : public NativeWindow {
void SetIgnoreMouseEvents(bool ignore) override; void SetIgnoreMouseEvents(bool ignore) override;
bool HasModalDialog() override; bool HasModalDialog() override;
gfx::NativeWindow GetNativeWindow() override; gfx::NativeWindow GetNativeWindow() override;
gfx::AcceleratedWidget GetAcceleratedWidget() override;
void SetProgressBar(double progress) override; void SetProgressBar(double progress) override;
void SetOverlayIcon(const gfx::Image& overlay, void SetOverlayIcon(const gfx::Image& overlay,
const std::string& description) override; const std::string& description) override;

View file

@ -452,6 +452,11 @@ NativeWindowMac::NativeWindowMac(
set_force_using_draggable_region(true); set_force_using_draggable_region(true);
} }
bool movable;
if (options.Get(options::kMovable, &movable)) {
[window_ setMovable:movable];
}
// On OS X the initial window size doesn't include window frame. // On OS X the initial window size doesn't include window frame.
bool use_content_size = false; bool use_content_size = false;
options.Get(options::kUseContentSize, &use_content_size); options.Get(options::kUseContentSize, &use_content_size);
@ -731,6 +736,10 @@ gfx::NativeWindow NativeWindowMac::GetNativeWindow() {
return window_; return window_;
} }
gfx::AcceleratedWidget NativeWindowMac::GetAcceleratedWidget() {
return inspectable_web_contents()->GetView()->GetNativeView();
}
void NativeWindowMac::SetProgressBar(double progress) { void NativeWindowMac::SetProgressBar(double progress) {
NSDockTile* dock_tile = [NSApp dockTile]; NSDockTile* dock_tile = [NSApp dockTile];

View file

@ -90,7 +90,7 @@ class NativeWindowViews : public NativeWindow,
void SetVisibleOnAllWorkspaces(bool visible) override; void SetVisibleOnAllWorkspaces(bool visible) override;
bool IsVisibleOnAllWorkspaces() override; bool IsVisibleOnAllWorkspaces() override;
gfx::AcceleratedWidget GetAcceleratedWidget(); gfx::AcceleratedWidget GetAcceleratedWidget() override;
views::Widget* widget() const { return window_.get(); } views::Widget* widget() const { return window_.get(); }

View file

@ -17,9 +17,9 @@
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>atom.icns</string> <string>atom.icns</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>0.36.2</string> <string>0.36.4</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.36.2</string> <string>0.36.4</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string> <string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View file

@ -56,8 +56,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,36,2,0 FILEVERSION 0,36,4,0
PRODUCTVERSION 0,36,2,0 PRODUCTVERSION 0,36,4,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -74,12 +74,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "GitHub, Inc." VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron" VALUE "FileDescription", "Electron"
VALUE "FileVersion", "0.36.2" VALUE "FileVersion", "0.36.4"
VALUE "InternalName", "electron.exe" VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe" VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron" VALUE "ProductName", "Electron"
VALUE "ProductVersion", "0.36.2" VALUE "ProductVersion", "0.36.4"
VALUE "SquirrelAwareVersion", "1" VALUE "SquirrelAwareVersion", "1"
END END
END END

View file

@ -38,6 +38,7 @@ int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int cancel_id, int cancel_id,
int default_id,
int options, int options,
const std::string& title, const std::string& title,
const std::string& message, const std::string& message,
@ -47,6 +48,7 @@ int ShowMessageBox(NativeWindow* parent_window,
void ShowMessageBox(NativeWindow* parent_window, void ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,

View file

@ -29,6 +29,7 @@ class GtkMessageBox {
GtkMessageBox(NativeWindow* parent_window, GtkMessageBox(NativeWindow* parent_window,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
const std::string& title, const std::string& title,
const std::string& message, const std::string& message,
@ -60,9 +61,10 @@ class GtkMessageBox {
// Add buttons. // Add buttons.
for (size_t i = 0; i < buttons.size(); ++i) { for (size_t i = 0; i < buttons.size(); ++i) {
gtk_dialog_add_button(GTK_DIALOG(dialog_), GtkWidget* button = gtk_dialog_add_button(
TranslateToStock(i, buttons[i]), GTK_DIALOG(dialog_), TranslateToStock(i, buttons[i]), i);
i); if (static_cast<int>(i) == default_id)
gtk_widget_grab_focus(button);
} }
// Parent window. // Parent window.
@ -161,19 +163,21 @@ void GtkMessageBox::OnResponseDialog(GtkWidget* widget, int response) {
int ShowMessageBox(NativeWindow* parent, int ShowMessageBox(NativeWindow* parent,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
const std::string& message, const std::string& message,
const std::string& detail, const std::string& detail,
const gfx::ImageSkia& icon) { const gfx::ImageSkia& icon) {
return GtkMessageBox(parent, type, buttons, cancel_id, title, message, detail, return GtkMessageBox(parent, type, buttons, default_id, cancel_id,
icon).RunSynchronous(); title, message, detail, icon).RunSynchronous();
} }
void ShowMessageBox(NativeWindow* parent, void ShowMessageBox(NativeWindow* parent,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -181,13 +185,13 @@ void ShowMessageBox(NativeWindow* parent,
const std::string& detail, const std::string& detail,
const gfx::ImageSkia& icon, const gfx::ImageSkia& icon,
const MessageBoxCallback& callback) { const MessageBoxCallback& callback) {
(new GtkMessageBox(parent, type, buttons, cancel_id, title, message, detail, (new GtkMessageBox(parent, type, buttons, default_id, cancel_id,
icon))->RunAsynchronous(callback); title, message, detail, icon))->RunAsynchronous(callback);
} }
void ShowErrorBox(const base::string16& title, const base::string16& content) { void ShowErrorBox(const base::string16& title, const base::string16& content) {
if (Browser::Get()->is_ready()) { if (Browser::Get()->is_ready()) {
GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, { "OK" }, 0, "Error", GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, { "OK" }, -1, 0, "Error",
base::UTF16ToUTF8(title).c_str(), base::UTF16ToUTF8(title).c_str(),
base::UTF16ToUTF8(content).c_str(), base::UTF16ToUTF8(content).c_str(),
gfx::ImageSkia()).RunSynchronous(); gfx::ImageSkia()).RunSynchronous();

View file

@ -54,6 +54,7 @@ namespace {
NSAlert* CreateNSAlert(NativeWindow* parent_window, NSAlert* CreateNSAlert(NativeWindow* parent_window,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
const std::string& title, const std::string& title,
const std::string& message, const std::string& message,
const std::string& detail) { const std::string& detail) {
@ -82,6 +83,15 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
[button setTag:i]; [button setTag:i];
} }
NSArray* ns_buttons = [alert buttons];
if (default_id >= 0 && default_id < static_cast<int>([ns_buttons count])) {
// Focus the button at default_id if the user opted to do so.
// The first button added gets set as the default selected.
// So remove that default, and make the requested button the default.
[[ns_buttons objectAtIndex:0] setKeyEquivalent:@""];
[[ns_buttons objectAtIndex:default_id] setKeyEquivalent:@"\r"];
}
return alert; return alert;
} }
@ -94,6 +104,7 @@ void SetReturnCode(int* ret_code, int result) {
int ShowMessageBox(NativeWindow* parent_window, int ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -101,7 +112,8 @@ int ShowMessageBox(NativeWindow* parent_window,
const std::string& detail, const std::string& detail,
const gfx::ImageSkia& icon) { const gfx::ImageSkia& icon) {
NSAlert* alert = CreateNSAlert( NSAlert* alert = CreateNSAlert(
parent_window, type, buttons, title, message, detail); parent_window, type, buttons, default_id, title, message,
detail);
// Use runModal for synchronous alert without parent, since we don't have a // Use runModal for synchronous alert without parent, since we don't have a
// window to wait for. // window to wait for.
@ -127,6 +139,7 @@ int ShowMessageBox(NativeWindow* parent_window,
void ShowMessageBox(NativeWindow* parent_window, void ShowMessageBox(NativeWindow* parent_window,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -135,7 +148,8 @@ void ShowMessageBox(NativeWindow* parent_window,
const gfx::ImageSkia& icon, const gfx::ImageSkia& icon,
const MessageBoxCallback& callback) { const MessageBoxCallback& callback) {
NSAlert* alert = CreateNSAlert( NSAlert* alert = CreateNSAlert(
parent_window, type, buttons, title, message, detail); parent_window, type, buttons, default_id, title, message,
detail);
ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback
andAlert:alert andAlert:alert
callEndModal:false]; callEndModal:false];

View file

@ -72,6 +72,7 @@ void MapToCommonID(const std::vector<base::string16>& buttons,
int ShowMessageBoxUTF16(HWND parent, int ShowMessageBoxUTF16(HWND parent,
MessageBoxType type, MessageBoxType type,
const std::vector<base::string16>& buttons, const std::vector<base::string16>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const base::string16& title, const base::string16& title,
@ -88,6 +89,9 @@ int ShowMessageBoxUTF16(HWND parent,
config.hInstance = GetModuleHandle(NULL); config.hInstance = GetModuleHandle(NULL);
config.dwFlags = flags; config.dwFlags = flags;
if (default_id > 0)
config.nDefaultButton = kIDStart + default_id;
// TaskDialogIndirect doesn't allow empty name, if we set empty title it // TaskDialogIndirect doesn't allow empty name, if we set empty title it
// will show "electron.exe" in title. // will show "electron.exe" in title.
base::string16 app_name = base::UTF8ToUTF16(Browser::Get()->GetName()); base::string16 app_name = base::UTF8ToUTF16(Browser::Get()->GetName());
@ -156,6 +160,7 @@ void RunMessageBoxInNewThread(base::Thread* thread,
NativeWindow* parent, NativeWindow* parent,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -163,8 +168,8 @@ void RunMessageBoxInNewThread(base::Thread* thread,
const std::string& detail, const std::string& detail,
const gfx::ImageSkia& icon, const gfx::ImageSkia& icon,
const MessageBoxCallback& callback) { const MessageBoxCallback& callback) {
int result = ShowMessageBox(parent, type, buttons, cancel_id, options, title, int result = ShowMessageBox(parent, type, buttons, default_id,
message, detail, icon); cancel_id, options, title, message, detail, icon);
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result)); content::BrowserThread::UI, FROM_HERE, base::Bind(callback, result));
content::BrowserThread::DeleteSoon( content::BrowserThread::DeleteSoon(
@ -176,6 +181,7 @@ void RunMessageBoxInNewThread(base::Thread* thread,
int ShowMessageBox(NativeWindow* parent, int ShowMessageBox(NativeWindow* parent,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -194,6 +200,7 @@ int ShowMessageBox(NativeWindow* parent,
return ShowMessageBoxUTF16(hwnd_parent, return ShowMessageBoxUTF16(hwnd_parent,
type, type,
utf16_buttons, utf16_buttons,
default_id,
cancel_id, cancel_id,
options, options,
base::UTF8ToUTF16(title), base::UTF8ToUTF16(title),
@ -205,6 +212,7 @@ int ShowMessageBox(NativeWindow* parent,
void ShowMessageBox(NativeWindow* parent, void ShowMessageBox(NativeWindow* parent,
MessageBoxType type, MessageBoxType type,
const std::vector<std::string>& buttons, const std::vector<std::string>& buttons,
int default_id,
int cancel_id, int cancel_id,
int options, int options,
const std::string& title, const std::string& title,
@ -224,13 +232,13 @@ void ShowMessageBox(NativeWindow* parent,
unretained->message_loop()->PostTask( unretained->message_loop()->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained), base::Bind(&RunMessageBoxInNewThread, base::Unretained(unretained),
parent, type, buttons, cancel_id, options, title, message, parent, type, buttons, default_id, cancel_id, options, title,
detail, icon, callback)); message, detail, icon, callback));
} }
void ShowErrorBox(const base::string16& title, const base::string16& content) { void ShowErrorBox(const base::string16& title, const base::string16& content) {
ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, 0, 0, L"Error", title, ShowMessageBoxUTF16(NULL, MESSAGE_BOX_TYPE_ERROR, {}, -1, 0, 0, L"Error",
content, gfx::ImageSkia()); title, content, gfx::ImageSkia());
} }
} // namespace atom } // namespace atom

View file

@ -30,20 +30,10 @@ IPC_SYNC_MESSAGE_ROUTED2_1(AtomViewHostMsg_Message_Sync,
base::ListValue /* arguments */, base::ListValue /* arguments */,
base::string16 /* result (in JSON) */) base::string16 /* result (in JSON) */)
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_ZoomLevelChanged,
double /* level */)
IPC_MESSAGE_ROUTED1(AtomViewMsg_SetZoomLevel,
double /* level */)
IPC_MESSAGE_ROUTED2(AtomViewMsg_Message, IPC_MESSAGE_ROUTED2(AtomViewMsg_Message,
base::string16 /* channel */, base::string16 /* channel */,
base::ListValue /* arguments */) base::ListValue /* arguments */)
IPC_MESSAGE_ROUTED2(AtomViewMsg_ExecuteJavaScript,
base::string16 /* code */,
bool /* has user gesture */)
// Sent by the renderer when the draggable regions are updated. // Sent by the renderer when the draggable regions are updated.
IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions, IPC_MESSAGE_ROUTED1(AtomViewHostMsg_UpdateDraggableRegions,
std::vector<atom::DraggableRegion> /* regions */) std::vector<atom::DraggableRegion> /* regions */)

View file

@ -1,43 +0,0 @@
v8Util = process.atomBinding 'v8_util'
module.exports =
class CallbacksRegistry
constructor: ->
@nextId = 0
@callbacks = {}
add: (callback) ->
# The callback is already added.
id = v8Util.getHiddenValue callback, 'callbackId'
return id if id?
id = ++@nextId
# Capture the location of the function and put it in the ID string,
# so that release errors can be tracked down easily.
regexp = /at (.*)/gi
stackString = (new Error).stack
while (match = regexp.exec(stackString)) isnt null
[x, location] = match
continue if location.indexOf('(native)') isnt -1
continue if location.indexOf('atom.asar') isnt -1
[x, filenameAndLine] = /([^/^\)]*)\)?$/gi.exec(location)
break
@callbacks[id] = callback
v8Util.setHiddenValue callback, 'callbackId', id
v8Util.setHiddenValue callback, 'location', filenameAndLine
id
get: (id) ->
@callbacks[id] ? ->
call: (id, args...) ->
@get(id).call global, args...
apply: (id, args...) ->
@get(id).apply global, args...
remove: (id) ->
delete @callbacks[id]

View file

@ -0,0 +1,65 @@
var CallbacksRegistry, v8Util,
slice = [].slice;
v8Util = process.atomBinding('v8_util');
module.exports = CallbacksRegistry = (function() {
function CallbacksRegistry() {
this.nextId = 0;
this.callbacks = {};
}
CallbacksRegistry.prototype.add = function(callback) {
// The callback is already added.
var filenameAndLine, id, location, match, ref, regexp, stackString, x;
id = v8Util.getHiddenValue(callback, 'callbackId');
if (id != null) {
return id;
}
id = ++this.nextId;
// Capture the location of the function and put it in the ID string,
// so that release errors can be tracked down easily.
regexp = /at (.*)/gi;
stackString = (new Error).stack;
while ((match = regexp.exec(stackString)) !== null) {
x = match[0], location = match[1];
if (location.indexOf('(native)') !== -1) {
continue;
}
if (location.indexOf('atom.asar') !== -1) {
continue;
}
ref = /([^\/^\)]*)\)?$/gi.exec(location), x = ref[0], filenameAndLine = ref[1];
break;
}
this.callbacks[id] = callback;
v8Util.setHiddenValue(callback, 'callbackId', id);
v8Util.setHiddenValue(callback, 'location', filenameAndLine);
return id;
};
CallbacksRegistry.prototype.get = function(id) {
var ref;
return (ref = this.callbacks[id]) != null ? ref : function() {};
};
CallbacksRegistry.prototype.call = function() {
var args, id, ref;
id = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return (ref = this.get(id)).call.apply(ref, [global].concat(slice.call(args)));
};
CallbacksRegistry.prototype.apply = function() {
var args, id, ref;
id = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return (ref = this.get(id)).apply.apply(ref, [global].concat(slice.call(args)));
};
CallbacksRegistry.prototype.remove = function(id) {
return delete this.callbacks[id];
};
return CallbacksRegistry;
})();

View file

@ -1,5 +0,0 @@
if process.platform is 'linux' and process.type is 'renderer'
# On Linux we could not access clipboard in renderer process.
module.exports = require('electron').remote.clipboard
else
module.exports = process.atomBinding 'clipboard'

View file

@ -0,0 +1,6 @@
if (process.platform === 'linux' && process.type === 'renderer') {
// On Linux we could not access clipboard in renderer process.
module.exports = require('electron').remote.clipboard;
} else {
module.exports = process.atomBinding('clipboard');
}

View file

@ -1,69 +0,0 @@
fs = require 'fs'
os = require 'os'
path = require 'path'
{spawn} = require 'child_process'
electron = require 'electron'
binding = process.atomBinding 'crash_reporter'
class CrashReporter
start: (options={}) ->
{@productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra} = options
# Deprecated.
{deprecate} = electron
if options.submitUrl
submitURL ?= options.submitUrl
deprecate.warn 'submitUrl', 'submitURL'
{app} = if process.type is 'browser' then electron else electron.remote
@productName ?= app.getName()
autoSubmit ?= true
ignoreSystemCrashHandler ?= false
extra ?= {}
extra._productName ?= @productName
extra._companyName ?= companyName
extra._version ?= app.getVersion()
unless companyName?
deprecate.log('companyName is now a required option to crashReporter.start')
return
unless submitURL?
deprecate.log('submitURL is now a required option to crashReporter.start')
return
start = => binding.start @productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra
if process.platform is 'win32'
args = [
"--reporter-url=#{submitURL}"
"--application-name=#{@productName}"
"--v=1"
]
env = ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1
spawn process.execPath, args, {env, detached: true}
start()
getLastCrashReport: ->
reports = this.getUploadedReports()
if reports.length > 0 then reports[0] else null
getUploadedReports: ->
tmpdir =
if process.platform is 'win32'
os.tmpdir()
else
'/tmp'
log =
if process.platform is 'darwin'
path.join tmpdir, "#{@productName} Crashes"
else
path.join tmpdir, "#{@productName} Crashes", 'uploads.log'
binding._getUploadedReports log
crashRepoter = new CrashReporter
module.exports = crashRepoter

View file

@ -0,0 +1,95 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const spawn = require('child_process').spawn;
const electron = require('electron');
const binding = process.atomBinding('crash_reporter');
var CrashReporter = (function() {
function CrashReporter() {}
CrashReporter.prototype.start = function(options) {
var app, args, autoSubmit, companyName, deprecate, env, extra, ignoreSystemCrashHandler, start, submitURL;
if (options == null) {
options = {};
}
this.productName = options.productName, companyName = options.companyName, submitURL = options.submitURL, autoSubmit = options.autoSubmit, ignoreSystemCrashHandler = options.ignoreSystemCrashHandler, extra = options.extra;
// Deprecated.
deprecate = electron.deprecate;
if (options.submitUrl) {
if (submitURL == null) {
submitURL = options.submitUrl;
}
deprecate.warn('submitUrl', 'submitURL');
}
app = (process.type === 'browser' ? electron : electron.remote).app;
if (this.productName == null) {
this.productName = app.getName();
}
if (autoSubmit == null) {
autoSubmit = true;
}
if (ignoreSystemCrashHandler == null) {
ignoreSystemCrashHandler = false;
}
if (extra == null) {
extra = {};
}
if (extra._productName == null) {
extra._productName = this.productName;
}
if (extra._companyName == null) {
extra._companyName = companyName;
}
if (extra._version == null) {
extra._version = app.getVersion();
}
if (companyName == null) {
deprecate.log('companyName is now a required option to crashReporter.start');
return;
}
if (submitURL == null) {
deprecate.log('submitURL is now a required option to crashReporter.start');
return;
}
start = (function(_this) {
return function() {
return binding.start(_this.productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra);
};
})(this);
if (process.platform === 'win32') {
args = ["--reporter-url=" + submitURL, "--application-name=" + this.productName, "--v=1"];
env = {
ATOM_SHELL_INTERNAL_CRASH_SERVICE: 1
};
spawn(process.execPath, args, {
env: env,
detached: true
});
}
return start();
};
CrashReporter.prototype.getLastCrashReport = function() {
var reports;
reports = this.getUploadedReports();
if (reports.length > 0) {
return reports[0];
} else {
return null;
}
};
CrashReporter.prototype.getUploadedReports = function() {
var log, tmpdir;
tmpdir = process.platform === 'win32' ? os.tmpdir() : '/tmp';
log = process.platform === 'darwin' ? path.join(tmpdir, this.productName + " Crashes") : path.join(tmpdir, this.productName + " Crashes", 'uploads.log');
return binding._getUploadedReports(log);
};
return CrashReporter;
})();
module.exports = new CrashReporter;

View file

@ -1,68 +0,0 @@
# Deprecate a method.
deprecate = (oldName, newName, fn) ->
warned = false
->
unless warned or process.noDeprecation
warned = true
deprecate.warn oldName, newName
fn.apply this, arguments
# The method is renamed.
deprecate.rename = (object, oldName, newName) ->
warned = false
newMethod = ->
unless warned or process.noDeprecation
warned = true
deprecate.warn oldName, newName
this[newName].apply this, arguments
if typeof object is 'function'
object.prototype[oldName] = newMethod
else
object[oldName] = newMethod
# Forward the method to member.
deprecate.member = (object, method, member) ->
warned = false
object.prototype[method] = ->
unless warned or process.noDeprecation
warned = true
deprecate.warn method, "#{member}.#{method}"
this[member][method].apply this[member], arguments
# Deprecate a property.
deprecate.property = (object, property, method) ->
Object.defineProperty object, property,
get: ->
warned = false
unless warned or process.noDeprecation
warned = true
deprecate.warn "#{property} property", "#{method} method"
this[method]()
# Deprecate an event.
deprecate.event = (emitter, oldName, newName, fn) ->
warned = false
emitter.on newName, (args...) ->
if @listenerCount(oldName) > 0 # there is listeners for old API.
unless warned or process.noDeprecation
warned = true
deprecate.warn "'#{oldName}' event", "'#{newName}' event"
if fn?
fn.apply this, arguments
else
@emit oldName, args...
# Print deprecation warning.
deprecate.warn = (oldName, newName) ->
deprecate.log "#{oldName} is deprecated. Use #{newName} instead."
# Print deprecation message.
deprecate.log = (message) ->
if process.throwDeprecation
throw new Error(message)
else if process.traceDeprecation
console.trace message
else
console.warn "(electron) #{message}"
module.exports = deprecate

View file

@ -0,0 +1,102 @@
// Deprecate a method.
var deprecate,
slice = [].slice;
deprecate = function(oldName, newName, fn) {
var warned;
warned = false;
return function() {
if (!(warned || process.noDeprecation)) {
warned = true;
deprecate.warn(oldName, newName);
}
return fn.apply(this, arguments);
};
};
// The method is renamed.
deprecate.rename = function(object, oldName, newName) {
var newMethod, warned;
warned = false;
newMethod = function() {
if (!(warned || process.noDeprecation)) {
warned = true;
deprecate.warn(oldName, newName);
}
return this[newName].apply(this, arguments);
};
if (typeof object === 'function') {
return object.prototype[oldName] = newMethod;
} else {
return object[oldName] = newMethod;
}
};
// Forward the method to member.
deprecate.member = function(object, method, member) {
var warned;
warned = false;
return object.prototype[method] = function() {
if (!(warned || process.noDeprecation)) {
warned = true;
deprecate.warn(method, member + "." + method);
}
return this[member][method].apply(this[member], arguments);
};
};
// Deprecate a property.
deprecate.property = function(object, property, method) {
return Object.defineProperty(object, property, {
get: function() {
var warned;
warned = false;
if (!(warned || process.noDeprecation)) {
warned = true;
deprecate.warn(property + " property", method + " method");
}
return this[method]();
}
});
};
// Deprecate an event.
deprecate.event = function(emitter, oldName, newName, fn) {
var warned;
warned = false;
return emitter.on(newName, function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
// there is listeners for old API.
if (this.listenerCount(oldName) > 0) {
if (!(warned || process.noDeprecation)) {
warned = true;
deprecate.warn("'" + oldName + "' event", "'" + newName + "' event");
}
if (fn != null) {
return fn.apply(this, arguments);
} else {
return this.emit.apply(this, [oldName].concat(slice.call(args)));
}
}
});
};
// Print deprecation warning.
deprecate.warn = function(oldName, newName) {
return deprecate.log(oldName + " is deprecated. Use " + newName + " instead.");
};
// Print deprecation message.
deprecate.log = function(message) {
if (process.throwDeprecation) {
throw new Error(message);
} else if (process.traceDeprecation) {
return console.trace(message);
} else {
return console.warn("(electron) " + message);
}
};
module.exports = deprecate;

View file

@ -1,29 +0,0 @@
# Do not expose the internal modules to `require`.
exports.hideInternalModules = ->
{globalPaths} = require 'module'
if globalPaths.length is 3
# Remove the "common/api/lib" and "browser-or-renderer/api/lib".
globalPaths.splice 0, 2
# Attaches properties to |exports|.
exports.defineProperties = (exports) ->
Object.defineProperties exports,
# Common modules, please sort with alphabet order.
clipboard:
# Must be enumerable, otherwise it woulde be invisible to remote module.
enumerable: true
get: -> require '../clipboard'
crashReporter:
enumerable: true
get: -> require '../crash-reporter'
nativeImage:
enumerable: true
get: -> require '../native-image'
shell:
enumerable: true
get: -> require '../shell'
# The internal modules, invisible unless you know their names.
CallbacksRegistry:
get: -> require '../callbacks-registry'
deprecate:
get: -> require '../deprecate'

View file

@ -0,0 +1,55 @@
// Do not expose the internal modules to `require`.
exports.hideInternalModules = function() {
var globalPaths = require('module').globalPaths;
if (globalPaths.length === 3) {
// Remove the "common/api/lib" and "browser-or-renderer/api/lib".
return globalPaths.splice(0, 2);
}
};
// Attaches properties to |exports|.
exports.defineProperties = function(exports) {
return Object.defineProperties(exports, {
// Common modules, please sort with alphabet order.
clipboard: {
// Must be enumerable, otherwise it woulde be invisible to remote module.
enumerable: true,
get: function() {
return require('../clipboard');
}
},
crashReporter: {
enumerable: true,
get: function() {
return require('../crash-reporter');
}
},
nativeImage: {
enumerable: true,
get: function() {
return require('../native-image');
}
},
shell: {
enumerable: true,
get: function() {
return require('../shell');
}
},
// The internal modules, invisible unless you know their names.
CallbacksRegistry: {
get: function() {
return require('../callbacks-registry');
}
},
deprecate: {
get: function() {
return require('../deprecate');
}
}
});
};

View file

@ -1,7 +0,0 @@
{deprecate} = require 'electron'
nativeImage = process.atomBinding 'native_image'
# Deprecated.
deprecate.rename nativeImage, 'createFromDataUrl', 'createFromDataURL'
module.exports = nativeImage

View file

@ -0,0 +1,7 @@
const deprecate = require('electron').deprecate;
const nativeImage = process.atomBinding('native_image');
// Deprecated.
deprecate.rename(nativeImage, 'createFromDataUrl', 'createFromDataURL');
module.exports = nativeImage;

View file

@ -1 +0,0 @@
module.exports = process.atomBinding 'shell'

View file

@ -0,0 +1 @@
module.exports = process.atomBinding('shell');

View file

@ -129,6 +129,9 @@ Archive::Archive(const base::FilePath& path)
} }
Archive::~Archive() { Archive::~Archive() {
#if defined(OS_WIN)
_close(fd_);
#endif
} }
bool Archive::Init() { bool Archive::Init() {

View file

@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 0 #define ATOM_MAJOR_VERSION 0
#define ATOM_MINOR_VERSION 36 #define ATOM_MINOR_VERSION 36
#define ATOM_PATCH_VERSION 2 #define ATOM_PATCH_VERSION 4
#define ATOM_VERSION_IS_RELEASE 1 #define ATOM_VERSION_IS_RELEASE 1

View file

@ -1,386 +0,0 @@
asar = process.binding 'atom_common_asar'
child_process = require 'child_process'
path = require 'path'
util = require 'util'
# Cache asar archive objects.
cachedArchives = {}
getOrCreateArchive = (p) ->
archive = cachedArchives[p]
return archive if archive?
archive = asar.createArchive p
return false unless archive
cachedArchives[p] = archive
# Clean cache on quit.
process.on 'exit', ->
archive.destroy() for own p, archive of cachedArchives
# Separate asar package's path from full path.
splitPath = (p) ->
return [false] if process.noAsar # shortcut to disable asar.
return [false] if typeof p isnt 'string'
return [true, p, ''] if p.substr(-5) is '.asar'
p = path.normalize p
index = p.lastIndexOf ".asar#{path.sep}"
return [false] if index is -1
[true, p.substr(0, index + 5), p.substr(index + 6)]
# Convert asar archive's Stats object to fs's Stats object.
nextInode = 0
uid = if process.getuid? then process.getuid() else 0
gid = if process.getgid? then process.getgid() else 0
fakeTime = new Date()
asarStatsToFsStats = (stats) ->
{
dev: 1,
ino: ++nextInode,
mode: 33188,
nlink: 1,
uid: uid,
gid: gid,
rdev: 0,
atime: stats.atime || fakeTime,
birthtime: stats.birthtime || fakeTime,
mtime: stats.mtime || fakeTime,
ctime: stats.ctime || fakeTime,
size: stats.size,
isFile: -> stats.isFile
isDirectory: -> stats.isDirectory
isSymbolicLink: -> stats.isLink
isBlockDevice: -> false
isCharacterDevice: -> false
isFIFO: -> false
isSocket: -> false
}
# Create a ENOENT error.
notFoundError = (asarPath, filePath, callback) ->
error = new Error("ENOENT, #{filePath} not found in #{asarPath}")
error.code = "ENOENT"
error.errno = -2
unless typeof callback is 'function'
throw error
process.nextTick -> callback error
# Create a ENOTDIR error.
notDirError = (callback) ->
error = new Error('ENOTDIR, not a directory')
error.code = 'ENOTDIR'
error.errno = -20
unless typeof callback is 'function'
throw error
process.nextTick -> callback error
# Create invalid archive error.
invalidArchiveError = (asarPath, callback) ->
error = new Error("Invalid package #{asarPath}")
unless typeof callback is 'function'
throw error
process.nextTick -> callback error
# Override APIs that rely on passing file path instead of content to C++.
overrideAPISync = (module, name, arg = 0) ->
old = module[name]
module[name] = ->
p = arguments[arg]
[isAsar, asarPath, filePath] = splitPath p
return old.apply this, arguments unless isAsar
archive = getOrCreateArchive asarPath
invalidArchiveError asarPath unless archive
newPath = archive.copyFileOut filePath
notFoundError asarPath, filePath unless newPath
arguments[arg] = newPath
old.apply this, arguments
overrideAPI = (module, name, arg = 0) ->
old = module[name]
module[name] = ->
p = arguments[arg]
[isAsar, asarPath, filePath] = splitPath p
return old.apply this, arguments unless isAsar
callback = arguments[arguments.length - 1]
return overrideAPISync module, name, arg unless typeof callback is 'function'
archive = getOrCreateArchive asarPath
return invalidArchiveError asarPath, callback unless archive
newPath = archive.copyFileOut filePath
return notFoundError asarPath, filePath, callback unless newPath
arguments[arg] = newPath
old.apply this, arguments
# Override fs APIs.
exports.wrapFsWithAsar = (fs) ->
lstatSync = fs.lstatSync
fs.lstatSync = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return lstatSync p unless isAsar
archive = getOrCreateArchive asarPath
invalidArchiveError asarPath unless archive
stats = archive.stat filePath
notFoundError asarPath, filePath unless stats
asarStatsToFsStats stats
lstat = fs.lstat
fs.lstat = (p, callback) ->
[isAsar, asarPath, filePath] = splitPath p
return lstat p, callback unless isAsar
archive = getOrCreateArchive asarPath
return invalidArchiveError asarPath, callback unless archive
stats = getOrCreateArchive(asarPath).stat filePath
return notFoundError asarPath, filePath, callback unless stats
process.nextTick -> callback null, asarStatsToFsStats stats
statSync = fs.statSync
fs.statSync = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return statSync p unless isAsar
# Do not distinguish links for now.
fs.lstatSync p
stat = fs.stat
fs.stat = (p, callback) ->
[isAsar, asarPath, filePath] = splitPath p
return stat p, callback unless isAsar
# Do not distinguish links for now.
process.nextTick -> fs.lstat p, callback
statSyncNoException = fs.statSyncNoException
fs.statSyncNoException = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return statSyncNoException p unless isAsar
archive = getOrCreateArchive asarPath
return false unless archive
stats = archive.stat filePath
return false unless stats
asarStatsToFsStats stats
realpathSync = fs.realpathSync
fs.realpathSync = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return realpathSync.apply this, arguments unless isAsar
archive = getOrCreateArchive asarPath
invalidArchiveError asarPath unless archive
real = archive.realpath filePath
notFoundError asarPath, filePath if real is false
path.join realpathSync(asarPath), real
realpath = fs.realpath
fs.realpath = (p, cache, callback) ->
[isAsar, asarPath, filePath] = splitPath p
return realpath.apply this, arguments unless isAsar
if typeof cache is 'function'
callback = cache
cache = undefined
archive = getOrCreateArchive asarPath
return invalidArchiveError asarPath, callback unless archive
real = archive.realpath filePath
if real is false
return notFoundError asarPath, filePath, callback
realpath asarPath, (err, p) ->
return callback err if err
callback null, path.join(p, real)
exists = fs.exists
fs.exists = (p, callback) ->
[isAsar, asarPath, filePath] = splitPath p
return exists p, callback unless isAsar
archive = getOrCreateArchive asarPath
return invalidArchiveError asarPath, callback unless archive
process.nextTick -> callback archive.stat(filePath) isnt false
existsSync = fs.existsSync
fs.existsSync = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return existsSync p unless isAsar
archive = getOrCreateArchive asarPath
return false unless archive
archive.stat(filePath) isnt false
open = fs.open
readFile = fs.readFile
fs.readFile = (p, options, callback) ->
[isAsar, asarPath, filePath] = splitPath p
return readFile.apply this, arguments unless isAsar
if typeof options is 'function'
callback = options
options = undefined
archive = getOrCreateArchive asarPath
return invalidArchiveError asarPath, callback unless archive
info = archive.getFileInfo filePath
return notFoundError asarPath, filePath, callback unless info
if info.size is 0
return process.nextTick -> callback null, new Buffer(0)
if info.unpacked
realPath = archive.copyFileOut filePath
return fs.readFile realPath, options, callback
if not options
options = encoding: null
else if util.isString options
options = encoding: options
else if not util.isObject options
throw new TypeError('Bad arguments')
encoding = options.encoding
buffer = new Buffer(info.size)
fd = archive.getFd()
return notFoundError asarPath, filePath, callback unless fd >= 0
fs.read fd, buffer, 0, info.size, info.offset, (error) ->
callback error, if encoding then buffer.toString encoding else buffer
openSync = fs.openSync
readFileSync = fs.readFileSync
fs.readFileSync = (p, opts) ->
options = opts # this allows v8 to optimize this function
[isAsar, asarPath, filePath] = splitPath p
return readFileSync.apply this, arguments unless isAsar
archive = getOrCreateArchive asarPath
invalidArchiveError asarPath unless archive
info = archive.getFileInfo filePath
notFoundError asarPath, filePath unless info
if info.size is 0
return if options then '' else new Buffer(0)
if info.unpacked
realPath = archive.copyFileOut filePath
return fs.readFileSync realPath, options
if not options
options = encoding: null
else if util.isString options
options = encoding: options
else if not util.isObject options
throw new TypeError('Bad arguments')
encoding = options.encoding
buffer = new Buffer(info.size)
fd = archive.getFd()
notFoundError asarPath, filePath unless fd >= 0
fs.readSync fd, buffer, 0, info.size, info.offset
if encoding then buffer.toString encoding else buffer
readdir = fs.readdir
fs.readdir = (p, callback) ->
[isAsar, asarPath, filePath] = splitPath p
return readdir.apply this, arguments unless isAsar
archive = getOrCreateArchive asarPath
return invalidArchiveError asarPath, callback unless archive
files = archive.readdir filePath
return notFoundError asarPath, filePath, callback unless files
process.nextTick -> callback null, files
readdirSync = fs.readdirSync
fs.readdirSync = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return readdirSync.apply this, arguments unless isAsar
archive = getOrCreateArchive asarPath
invalidArchiveError asarPath unless archive
files = archive.readdir filePath
notFoundError asarPath, filePath unless files
files
internalModuleReadFile = process.binding('fs').internalModuleReadFile
process.binding('fs').internalModuleReadFile = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return internalModuleReadFile p unless isAsar
archive = getOrCreateArchive asarPath
return undefined unless archive
info = archive.getFileInfo filePath
return undefined unless info
return '' if info.size is 0
if info.unpacked
realPath = archive.copyFileOut filePath
return fs.readFileSync realPath, encoding: 'utf8'
buffer = new Buffer(info.size)
fd = archive.getFd()
return undefined unless fd >= 0
fs.readSync fd, buffer, 0, info.size, info.offset
buffer.toString 'utf8'
internalModuleStat = process.binding('fs').internalModuleStat
process.binding('fs').internalModuleStat = (p) ->
[isAsar, asarPath, filePath] = splitPath p
return internalModuleStat p unless isAsar
archive = getOrCreateArchive asarPath
return -34 unless archive # -ENOENT
stats = archive.stat filePath
return -34 unless stats # -ENOENT
if stats.isDirectory then return 1 else return 0
# Calling mkdir for directory inside asar archive should throw ENOTDIR
# error, but on Windows it throws ENOENT.
# This is to work around the recursive looping bug of mkdirp since it is
# widely used.
if process.platform is 'win32'
mkdir = fs.mkdir
fs.mkdir = (p, mode, callback) ->
callback = mode if typeof mode is 'function'
[isAsar, asarPath, filePath] = splitPath p
return notDirError callback if isAsar and filePath.length
mkdir p, mode, callback
mkdirSync = fs.mkdirSync
fs.mkdirSync = (p, mode) ->
[isAsar, asarPath, filePath] = splitPath p
notDirError() if isAsar and filePath.length
mkdirSync p, mode
overrideAPI fs, 'open'
overrideAPI child_process, 'execFile'
overrideAPISync process, 'dlopen', 1
overrideAPISync require('module')._extensions, '.node', 1
overrideAPISync fs, 'openSync'
overrideAPISync child_process, 'execFileSync'

587
atom/common/lib/asar.js Normal file
View file

@ -0,0 +1,587 @@
(function () {
const asar = process.binding('atom_common_asar');
const child_process = require('child_process');
const path = require('path');
const util = require('util');
var hasProp = {}.hasOwnProperty;
// Cache asar archive objects.
var cachedArchives = {};
var getOrCreateArchive = function(p) {
var archive;
archive = cachedArchives[p];
if (archive != null) {
return archive;
}
archive = asar.createArchive(p);
if (!archive) {
return false;
}
return cachedArchives[p] = archive;
};
// Clean cache on quit.
process.on('exit', function() {
var archive, p, results;
results = [];
for (p in cachedArchives) {
if (!hasProp.call(cachedArchives, p)) continue;
archive = cachedArchives[p];
results.push(archive.destroy());
}
return results;
});
// Separate asar package's path from full path.
var splitPath = function(p) {
var index;
// shortcut to disable asar.
if (process.noAsar) {
return [false];
}
if (typeof p !== 'string') {
return [false];
}
if (p.substr(-5) === '.asar') {
return [true, p, ''];
}
p = path.normalize(p);
index = p.lastIndexOf(".asar" + path.sep);
if (index === -1) {
return [false];
}
return [true, p.substr(0, index + 5), p.substr(index + 6)];
};
// Convert asar archive's Stats object to fs's Stats object.
var nextInode = 0;
var uid = process.getuid != null ? process.getuid() : 0;
var gid = process.getgid != null ? process.getgid() : 0;
var fakeTime = new Date();
var asarStatsToFsStats = function(stats) {
return {
dev: 1,
ino: ++nextInode,
mode: 33188,
nlink: 1,
uid: uid,
gid: gid,
rdev: 0,
atime: stats.atime || fakeTime,
birthtime: stats.birthtime || fakeTime,
mtime: stats.mtime || fakeTime,
ctime: stats.ctime || fakeTime,
size: stats.size,
isFile: function() {
return stats.isFile;
},
isDirectory: function() {
return stats.isDirectory;
},
isSymbolicLink: function() {
return stats.isLink;
},
isBlockDevice: function() {
return false;
},
isCharacterDevice: function() {
return false;
},
isFIFO: function() {
return false;
},
isSocket: function() {
return false;
}
};
};
// Create a ENOENT error.
var notFoundError = function(asarPath, filePath, callback) {
var error;
error = new Error("ENOENT, " + filePath + " not found in " + asarPath);
error.code = "ENOENT";
error.errno = -2;
if (typeof callback !== 'function') {
throw error;
}
return process.nextTick(function() {
return callback(error);
});
};
// Create a ENOTDIR error.
var notDirError = function(callback) {
var error;
error = new Error('ENOTDIR, not a directory');
error.code = 'ENOTDIR';
error.errno = -20;
if (typeof callback !== 'function') {
throw error;
}
return process.nextTick(function() {
return callback(error);
});
};
// Create invalid archive error.
var invalidArchiveError = function(asarPath, callback) {
var error;
error = new Error("Invalid package " + asarPath);
if (typeof callback !== 'function') {
throw error;
}
return process.nextTick(function() {
return callback(error);
});
};
// Override APIs that rely on passing file path instead of content to C++.
var overrideAPISync = function(module, name, arg) {
var old;
if (arg == null) {
arg = 0;
}
old = module[name];
return module[name] = function() {
var archive, asarPath, filePath, isAsar, newPath, p, ref;
p = arguments[arg];
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return old.apply(this, arguments);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
invalidArchiveError(asarPath);
}
newPath = archive.copyFileOut(filePath);
if (!newPath) {
notFoundError(asarPath, filePath);
}
arguments[arg] = newPath;
return old.apply(this, arguments);
};
};
var overrideAPI = function(module, name, arg) {
var old;
if (arg == null) {
arg = 0;
}
old = module[name];
return module[name] = function() {
var archive, asarPath, callback, filePath, isAsar, newPath, p, ref;
p = arguments[arg];
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return old.apply(this, arguments);
}
callback = arguments[arguments.length - 1];
if (typeof callback !== 'function') {
return overrideAPISync(module, name, arg);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return invalidArchiveError(asarPath, callback);
}
newPath = archive.copyFileOut(filePath);
if (!newPath) {
return notFoundError(asarPath, filePath, callback);
}
arguments[arg] = newPath;
return old.apply(this, arguments);
};
};
// Override fs APIs.
exports.wrapFsWithAsar = function(fs) {
var exists, existsSync, internalModuleReadFile, internalModuleStat, lstat, lstatSync, mkdir, mkdirSync, open, openSync, readFile, readFileSync, readdir, readdirSync, realpath, realpathSync, stat, statSync, statSyncNoException;
lstatSync = fs.lstatSync;
fs.lstatSync = function(p) {
var archive, asarPath, filePath, isAsar, ref, stats;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return lstatSync(p);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
invalidArchiveError(asarPath);
}
stats = archive.stat(filePath);
if (!stats) {
notFoundError(asarPath, filePath);
}
return asarStatsToFsStats(stats);
};
lstat = fs.lstat;
fs.lstat = function(p, callback) {
var archive, asarPath, filePath, isAsar, ref, stats;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return lstat(p, callback);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return invalidArchiveError(asarPath, callback);
}
stats = getOrCreateArchive(asarPath).stat(filePath);
if (!stats) {
return notFoundError(asarPath, filePath, callback);
}
return process.nextTick(function() {
return callback(null, asarStatsToFsStats(stats));
});
};
statSync = fs.statSync;
fs.statSync = function(p) {
var asarPath, filePath, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return statSync(p);
}
// Do not distinguish links for now.
return fs.lstatSync(p);
};
stat = fs.stat;
fs.stat = function(p, callback) {
var asarPath, filePath, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return stat(p, callback);
}
// Do not distinguish links for now.
return process.nextTick(function() {
return fs.lstat(p, callback);
});
};
statSyncNoException = fs.statSyncNoException;
fs.statSyncNoException = function(p) {
var archive, asarPath, filePath, isAsar, ref, stats;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return statSyncNoException(p);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return false;
}
stats = archive.stat(filePath);
if (!stats) {
return false;
}
return asarStatsToFsStats(stats);
};
realpathSync = fs.realpathSync;
fs.realpathSync = function(p) {
var archive, asarPath, filePath, isAsar, real, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return realpathSync.apply(this, arguments);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
invalidArchiveError(asarPath);
}
real = archive.realpath(filePath);
if (real === false) {
notFoundError(asarPath, filePath);
}
return path.join(realpathSync(asarPath), real);
};
realpath = fs.realpath;
fs.realpath = function(p, cache, callback) {
var archive, asarPath, filePath, isAsar, real, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return realpath.apply(this, arguments);
}
if (typeof cache === 'function') {
callback = cache;
cache = void 0;
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return invalidArchiveError(asarPath, callback);
}
real = archive.realpath(filePath);
if (real === false) {
return notFoundError(asarPath, filePath, callback);
}
return realpath(asarPath, function(err, p) {
if (err) {
return callback(err);
}
return callback(null, path.join(p, real));
});
};
exists = fs.exists;
fs.exists = function(p, callback) {
var archive, asarPath, filePath, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return exists(p, callback);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return invalidArchiveError(asarPath, callback);
}
return process.nextTick(function() {
return callback(archive.stat(filePath) !== false);
});
};
existsSync = fs.existsSync;
fs.existsSync = function(p) {
var archive, asarPath, filePath, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return existsSync(p);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return false;
}
return archive.stat(filePath) !== false;
};
open = fs.open;
readFile = fs.readFile;
fs.readFile = function(p, options, callback) {
var archive, asarPath, buffer, encoding, fd, filePath, info, isAsar, realPath, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return readFile.apply(this, arguments);
}
if (typeof options === 'function') {
callback = options;
options = void 0;
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return invalidArchiveError(asarPath, callback);
}
info = archive.getFileInfo(filePath);
if (!info) {
return notFoundError(asarPath, filePath, callback);
}
if (info.size === 0) {
return process.nextTick(function() {
return callback(null, new Buffer(0));
});
}
if (info.unpacked) {
realPath = archive.copyFileOut(filePath);
return fs.readFile(realPath, options, callback);
}
if (!options) {
options = {
encoding: null
};
} else if (util.isString(options)) {
options = {
encoding: options
};
} else if (!util.isObject(options)) {
throw new TypeError('Bad arguments');
}
encoding = options.encoding;
buffer = new Buffer(info.size);
fd = archive.getFd();
if (!(fd >= 0)) {
return notFoundError(asarPath, filePath, callback);
}
return fs.read(fd, buffer, 0, info.size, info.offset, function(error) {
return callback(error, encoding ? buffer.toString(encoding) : buffer);
});
};
openSync = fs.openSync;
readFileSync = fs.readFileSync;
fs.readFileSync = function(p, opts) {
// this allows v8 to optimize this function
var archive, asarPath, buffer, encoding, fd, filePath, info, isAsar, options, realPath, ref;
options = opts;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return readFileSync.apply(this, arguments);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
invalidArchiveError(asarPath);
}
info = archive.getFileInfo(filePath);
if (!info) {
notFoundError(asarPath, filePath);
}
if (info.size === 0) {
if (options) {
return '';
} else {
return new Buffer(0);
}
}
if (info.unpacked) {
realPath = archive.copyFileOut(filePath);
return fs.readFileSync(realPath, options);
}
if (!options) {
options = {
encoding: null
};
} else if (util.isString(options)) {
options = {
encoding: options
};
} else if (!util.isObject(options)) {
throw new TypeError('Bad arguments');
}
encoding = options.encoding;
buffer = new Buffer(info.size);
fd = archive.getFd();
if (!(fd >= 0)) {
notFoundError(asarPath, filePath);
}
fs.readSync(fd, buffer, 0, info.size, info.offset);
if (encoding) {
return buffer.toString(encoding);
} else {
return buffer;
}
};
readdir = fs.readdir;
fs.readdir = function(p, callback) {
var archive, asarPath, filePath, files, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return readdir.apply(this, arguments);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return invalidArchiveError(asarPath, callback);
}
files = archive.readdir(filePath);
if (!files) {
return notFoundError(asarPath, filePath, callback);
}
return process.nextTick(function() {
return callback(null, files);
});
};
readdirSync = fs.readdirSync;
fs.readdirSync = function(p) {
var archive, asarPath, filePath, files, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return readdirSync.apply(this, arguments);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
invalidArchiveError(asarPath);
}
files = archive.readdir(filePath);
if (!files) {
notFoundError(asarPath, filePath);
}
return files;
};
internalModuleReadFile = process.binding('fs').internalModuleReadFile;
process.binding('fs').internalModuleReadFile = function(p) {
var archive, asarPath, buffer, fd, filePath, info, isAsar, realPath, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return internalModuleReadFile(p);
}
archive = getOrCreateArchive(asarPath);
if (!archive) {
return void 0;
}
info = archive.getFileInfo(filePath);
if (!info) {
return void 0;
}
if (info.size === 0) {
return '';
}
if (info.unpacked) {
realPath = archive.copyFileOut(filePath);
return fs.readFileSync(realPath, {
encoding: 'utf8'
});
}
buffer = new Buffer(info.size);
fd = archive.getFd();
if (!(fd >= 0)) {
return void 0;
}
fs.readSync(fd, buffer, 0, info.size, info.offset);
return buffer.toString('utf8');
};
internalModuleStat = process.binding('fs').internalModuleStat;
process.binding('fs').internalModuleStat = function(p) {
var archive, asarPath, filePath, isAsar, ref, stats;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (!isAsar) {
return internalModuleStat(p);
}
archive = getOrCreateArchive(asarPath);
// -ENOENT
if (!archive) {
return -34;
}
stats = archive.stat(filePath);
// -ENOENT
if (!stats) {
return -34;
}
if (stats.isDirectory) {
return 1;
} else {
return 0;
}
};
// Calling mkdir for directory inside asar archive should throw ENOTDIR
// error, but on Windows it throws ENOENT.
// This is to work around the recursive looping bug of mkdirp since it is
// widely used.
if (process.platform === 'win32') {
mkdir = fs.mkdir;
fs.mkdir = function(p, mode, callback) {
var asarPath, filePath, isAsar, ref;
if (typeof mode === 'function') {
callback = mode;
}
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (isAsar && filePath.length) {
return notDirError(callback);
}
return mkdir(p, mode, callback);
};
mkdirSync = fs.mkdirSync;
fs.mkdirSync = function(p, mode) {
var asarPath, filePath, isAsar, ref;
ref = splitPath(p), isAsar = ref[0], asarPath = ref[1], filePath = ref[2];
if (isAsar && filePath.length) {
notDirError();
}
return mkdirSync(p, mode);
};
}
overrideAPI(fs, 'open');
overrideAPI(child_process, 'execFile');
overrideAPISync(process, 'dlopen', 1);
overrideAPISync(require('module')._extensions, '.node', 1);
overrideAPISync(fs, 'openSync');
return overrideAPISync(child_process, 'execFileSync');
};
})()

View file

@ -1,22 +0,0 @@
return (process, require, asarSource) ->
{createArchive} = process.binding 'atom_common_asar'
# Make asar.coffee accessible via "require".
process.binding('natives').ATOM_SHELL_ASAR = asarSource
# Monkey-patch the fs module.
require('ATOM_SHELL_ASAR').wrapFsWithAsar require('fs')
# Make graceful-fs work with asar.
source = process.binding 'natives'
source['original-fs'] = source.fs
source['fs'] = """
var src = '(function (exports, require, module, __filename, __dirname) { ' +
process.binding('natives')['original-fs'] +
' });';
var vm = require('vm');
var fn = vm.runInThisContext(src, { filename: 'fs.js' });
fn(exports, require, module);
var asar = require('ATOM_SHELL_ASAR');
asar.wrapFsWithAsar(exports);
"""

View file

@ -0,0 +1,17 @@
(function () {
return function(process, require, asarSource) {
var createArchive, source;
createArchive = process.binding('atom_common_asar').createArchive;
// Make asar.coffee accessible via "require".
process.binding('natives').ATOM_SHELL_ASAR = asarSource;
// Monkey-patch the fs module.
require('ATOM_SHELL_ASAR').wrapFsWithAsar(require('fs'));
// Make graceful-fs work with asar.
source = process.binding('natives');
source['original-fs'] = source.fs;
return source['fs'] = "var src = '(function (exports, require, module, __filename, __dirname) { ' +\n process.binding('natives')['original-fs'] +\n ' });';\nvar vm = require('vm');\nvar fn = vm.runInThisContext(src, { filename: 'fs.js' });\nfn(exports, require, module);\nvar asar = require('ATOM_SHELL_ASAR');\nasar.wrapFsWithAsar(exports);";
};
})()

View file

@ -1,36 +0,0 @@
fs = require 'fs'
path = require 'path'
timers = require 'timers'
Module = require 'module'
process.atomBinding = (name) ->
try
process.binding "atom_#{process.type}_#{name}"
catch e
process.binding "atom_common_#{name}" if /No such module/.test e.message
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
# Add common/api/lib to module search paths.
Module.globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
# setImmediate and process.nextTick makes use of uv_check and uv_prepare to
# run the callbacks, however since we only run uv loop on requests, the
# callbacks wouldn't be called until something else activated the uv loop,
# which would delay the callbacks for arbitrary long time. So we should
# initiatively activate the uv loop once setImmediate and process.nextTick is
# called.
wrapWithActivateUvLoop = (func) ->
->
process.activateUvLoop()
func.apply this, arguments
process.nextTick = wrapWithActivateUvLoop process.nextTick
global.setImmediate = wrapWithActivateUvLoop timers.setImmediate
global.clearImmediate = timers.clearImmediate
if process.type is 'browser'
# setTimeout needs to update the polling timeout of the event loop, when
# called under Chromium's event loop the node's event loop won't get a chance
# to update the timeout, so we have to force the node's event loop to
# recalculate the timeout in browser process.
global.setTimeout = wrapWithActivateUvLoop timers.setTimeout
global.setInterval = wrapWithActivateUvLoop timers.setInterval

Some files were not shown because too many files have changed in this diff Show more