Merge remote-tracking branch 'refs/remotes/atom/master'

This commit is contained in:
Plusb Preco 2015-10-29 14:23:21 +09:00
commit 72e8660245
83 changed files with 3193 additions and 481 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
.DS_Store
/.idea/
/build/
/dist/
/external_binaries/

View file

@ -4,7 +4,7 @@
'product_name%': 'Electron',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '0.34.0',
'version%': '0.34.1',
},
'includes': [
'filenames.gypi',

View file

@ -13,11 +13,13 @@
#include "atom/browser/api/atom_api_menu.h"
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/atom_browser_context.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/browser.h"
#include "atom/browser/api/atom_api_web_contents.h"
#include "atom/browser/login_handler.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/content_converter.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/node_includes.h"
#include "atom/common/options_switches.h"
@ -111,12 +113,27 @@ int GetPathConstant(const std::string& name) {
return -1;
}
bool NotificationCallbackWrapper(
const ProcessSingleton::NotificationCallback& callback,
const base::CommandLine::StringVector& cmd,
const base::FilePath& cwd) {
// Make sure the callback is called after app gets ready.
if (Browser::Get()->is_ready()) {
callback.Run(cmd, cwd);
} else {
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
base::ThreadTaskRunnerHandle::Get());
task_runner->PostTask(
FROM_HERE, base::Bind(base::IgnoreResult(callback), cmd, cwd));
}
// ProcessSingleton needs to know whether current process is quiting.
return !Browser::Get()->is_shutting_down();
}
void OnClientCertificateSelected(
v8::Isolate* isolate,
std::shared_ptr<content::ClientCertificateDelegate> delegate,
mate::Arguments* args) {
v8::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
mate::Dictionary cert_data;
if (!(args->Length() == 1 && args->GetNext(&cert_data))) {
args->ThrowError();
@ -130,10 +147,18 @@ void OnClientCertificateSelected(
net::X509Certificate::CreateCertificateListFromBytes(
encoded_data.data(), encoded_data.size(),
net::X509Certificate::FORMAT_AUTO);
delegate->ContinueWithCertificate(certs[0].get());
}
void PassLoginInformation(scoped_refptr<LoginHandler> login_handler,
mate::Arguments* args) {
base::string16 username, password;
if (args->GetNext(&username) && args->GetNext(&password))
login_handler->Login(username, password);
else
login_handler->CancelAuth();
}
} // namespace
App::App() {
@ -160,6 +185,11 @@ void App::OnWindowAllClosed() {
void App::OnQuit() {
Emit("quit");
if (process_singleton_.get()) {
process_singleton_->Cleanup();
process_singleton_.reset();
}
}
void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
@ -211,6 +241,31 @@ void App::OnSelectCertificate(
cert_request_info->client_certs[0].get());
}
void App::OnLogin(LoginHandler* login_handler) {
// Convert the args explicitly since they will be passed for twice.
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto web_contents =
WebContents::CreateFrom(isolate(), login_handler->GetWebContents());
auto request = mate::ConvertToV8(isolate(), login_handler->request());
auto auth_info = mate::ConvertToV8(isolate(), login_handler->auth_info());
auto callback = mate::ConvertToV8(
isolate(),
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
bool prevent_default =
Emit("login", web_contents, request, auth_info, callback);
// Also pass it to WebContents.
if (!prevent_default)
prevent_default =
web_contents->Emit("login", request, auth_info, callback);
// Default behavior is to always cancel the auth.
if (!prevent_default)
login_handler->CancelAuth();
}
void App::OnGpuProcessCrashed(base::TerminationStatus exit_code) {
Emit("gpu-process-crashed");
}
@ -268,6 +323,28 @@ v8::Local<v8::Value> App::DefaultSession(v8::Isolate* isolate) {
return v8::Local<v8::Value>::New(isolate, default_session_);
}
bool App::MakeSingleInstance(
const ProcessSingleton::NotificationCallback& callback) {
if (process_singleton_.get())
return false;
base::FilePath user_dir;
PathService::Get(brightray::DIR_USER_DATA, &user_dir);
process_singleton_.reset(new ProcessSingleton(
user_dir, base::Bind(NotificationCallbackWrapper, callback)));
switch (process_singleton_->NotifyOtherProcessOrCreate()) {
case ProcessSingleton::NotifyResult::LOCK_ERROR:
case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
process_singleton_.reset();
return true;
case ProcessSingleton::NotifyResult::PROCESS_NONE:
default: // Shouldn't be needed, but VS warns if it is not there.
return false;
}
}
mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
auto browser = base::Unretained(Browser::Get());
@ -294,6 +371,7 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
.SetMethod("allowNTLMCredentialsForAllDomains",
&App::AllowNTLMCredentialsForAllDomains)
.SetMethod("getLocale", &App::GetLocale)
.SetMethod("makeSingleInstance", &App::MakeSingleInstance)
.SetProperty("defaultSession", &App::DefaultSession);
}

View file

@ -9,6 +9,8 @@
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/browser_observer.h"
#include "atom/common/native_mate_converters/callback.h"
#include "chrome/browser/process_singleton.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "native_mate/handle.h"
@ -48,6 +50,7 @@ class App : public mate::EventEmitter,
content::WebContents* web_contents,
net::SSLCertRequestInfo* cert_request_info,
scoped_ptr<content::ClientCertificateDelegate> delegate) override;
void OnLogin(LoginHandler* login_handler) override;
// content::GpuDataManagerObserver:
void OnGpuProcessCrashed(base::TerminationStatus exit_code) override;
@ -66,12 +69,15 @@ class App : public mate::EventEmitter,
void SetDesktopName(const std::string& desktop_name);
void SetAppUserModelId(const std::string& app_id);
void AllowNTLMCredentialsForAllDomains(bool should_allow);
bool MakeSingleInstance(
const ProcessSingleton::NotificationCallback& callback);
std::string GetLocale();
v8::Local<v8::Value> DefaultSession(v8::Isolate* isolate);
v8::Global<v8::Value> default_session_;
scoped_ptr<ProcessSingleton> process_singleton_;
DISALLOW_COPY_AND_ASSIGN(App);
};

View file

@ -5,12 +5,31 @@
#include "atom/browser/api/atom_api_auto_updater.h"
#include "base/time/time.h"
#include "atom/browser/auto_updater.h"
#include "atom/browser/browser.h"
#include "atom/browser/native_window.h"
#include "atom/browser/window_list.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/node_includes.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
namespace mate {
template<>
struct Converter<base::Time> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::Time& val) {
v8::MaybeLocal<v8::Value> date = v8::Date::New(
isolate->GetCurrentContext(), val.ToJsTime());
if (date.IsEmpty())
return v8::Null(isolate);
else
return date.ToLocalChecked();
}
};
} // namespace mate
namespace atom {
namespace api {
@ -20,11 +39,18 @@ AutoUpdater::AutoUpdater() {
}
AutoUpdater::~AutoUpdater() {
auto_updater::AutoUpdater::SetDelegate(NULL);
auto_updater::AutoUpdater::SetDelegate(nullptr);
}
void AutoUpdater::OnError(const std::string& error) {
Emit("error", error);
void AutoUpdater::OnError(const std::string& message) {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
auto error = v8::Exception::Error(mate::StringToV8(isolate(), message));
EmitCustomEvent(
"error",
error->ToObject(isolate()->GetCurrentContext()).ToLocalChecked(),
// Message is also emitted to keep compatibility with old code.
message);
}
void AutoUpdater::OnCheckingForUpdate() {
@ -42,11 +68,14 @@ void AutoUpdater::OnUpdateNotAvailable() {
void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url,
const base::Closure& quit_and_install) {
quit_and_install_ = quit_and_install;
Emit("update-downloaded-raw", release_notes, release_name,
release_date.ToJsTime(), update_url);
const std::string& url) {
Emit("update-downloaded", release_notes, release_name, release_date, url,
// Keep compatibility with old APIs.
base::Bind(&AutoUpdater::QuitAndInstall, base::Unretained(this)));
}
void AutoUpdater::OnWindowAllClosed() {
QuitAndInstall();
}
mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
@ -54,14 +83,21 @@ mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
return mate::ObjectTemplateBuilder(isolate)
.SetMethod("setFeedUrl", &auto_updater::AutoUpdater::SetFeedURL)
.SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates)
.SetMethod("_quitAndInstall", &AutoUpdater::QuitAndInstall);
.SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall);
}
void AutoUpdater::QuitAndInstall() {
if (quit_and_install_.is_null())
Browser::Get()->Shutdown();
else
quit_and_install_.Run();
// If we don't have any window then quitAndInstall immediately.
WindowList* window_list = WindowList::GetInstance();
if (window_list->size() == 0) {
auto_updater::AutoUpdater::QuitAndInstall();
return;
}
// Otherwise do the restart after all windows have been closed.
window_list->AddObserver(this);
for (NativeWindow* window : *window_list)
window->Close();
}
// static

View file

@ -7,9 +7,9 @@
#include <string>
#include "base/callback.h"
#include "atom/browser/api/event_emitter.h"
#include "atom/browser/auto_updater_delegate.h"
#include "atom/browser/auto_updater.h"
#include "atom/browser/window_list_observer.h"
#include "native_mate/handle.h"
namespace atom {
@ -17,7 +17,8 @@ namespace atom {
namespace api {
class AutoUpdater : public mate::EventEmitter,
public auto_updater::AutoUpdaterDelegate {
public auto_updater::Delegate,
public WindowListObserver {
public:
static mate::Handle<AutoUpdater> Create(v8::Isolate* isolate);
@ -25,17 +26,18 @@ class AutoUpdater : public mate::EventEmitter,
AutoUpdater();
virtual ~AutoUpdater();
// AutoUpdaterDelegate implementations.
// Delegate implementations.
void OnError(const std::string& error) override;
void OnCheckingForUpdate() override;
void OnUpdateAvailable() override;
void OnUpdateNotAvailable() override;
void OnUpdateDownloaded(
const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url,
const base::Closure& quit_and_install) override;
void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url) override;
// WindowListObserver:
void OnWindowAllClosed() override;
// mate::Wrappable implementations:
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
@ -44,8 +46,6 @@ class AutoUpdater : public mate::EventEmitter,
private:
void QuitAndInstall();
base::Closure quit_and_install_;
DISALLOW_COPY_AND_ASSIGN(AutoUpdater);
};

View file

@ -12,27 +12,12 @@
#include "atom/browser/net/url_request_fetch_job.h"
#include "atom/browser/net/url_request_string_job.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/content_converter.h"
#include "atom/common/node_includes.h"
#include "native_mate/dictionary.h"
using content::BrowserThread;
namespace mate {
template<>
struct Converter<const net::URLRequest*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::URLRequest* val) {
return mate::ObjectTemplateBuilder(isolate)
.SetValue("method", val->method())
.SetValue("url", val->url().spec())
.SetValue("referrer", val->referrer())
.Build()->NewInstance();
}
};
} // namespace mate
namespace atom {
namespace api {

View file

@ -105,6 +105,24 @@ struct Converter<ClearStorageDataOptions> {
}
};
template<>
struct Converter<net::ProxyConfig> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
net::ProxyConfig* out) {
std::string proxy;
if (!ConvertFromV8(isolate, val, &proxy))
return false;
auto pac_url = GURL(proxy);
if (pac_url.is_valid()) {
out->set_pac_url(pac_url);
} else {
out->proxy_rules().ParseFromString(proxy);
}
return true;
}
};
} // namespace mate
namespace atom {
@ -209,12 +227,12 @@ void ClearHttpCacheInIO(
}
void SetProxyInIO(net::URLRequestContextGetter* getter,
const std::string& proxy,
const net::ProxyConfig& config,
const base::Closure& callback) {
net::ProxyConfig config;
config.proxy_rules().ParseFromString(proxy);
auto proxy_service = getter->GetURLRequestContext()->proxy_service();
proxy_service->ResetConfigService(new net::ProxyConfigServiceFixed(config));
// Refetches and applies the new pac script if provided.
proxy_service->ForceReloadProxyConfig();
RunCallbackInUI(callback);
}
@ -287,11 +305,11 @@ void Session::ClearStorageData(mate::Arguments* args) {
base::Time(), base::Time::Max(), callback);
}
void Session::SetProxy(const std::string& proxy,
void Session::SetProxy(const net::ProxyConfig& config,
const base::Closure& callback) {
auto getter = browser_context_->GetRequestContext();
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&SetProxyInIO, base::Unretained(getter), proxy, callback));
base::Bind(&SetProxyInIO, base::Unretained(getter), config, callback));
}
void Session::SetDownloadPath(const base::FilePath& path) {

View file

@ -23,6 +23,10 @@ class Arguments;
class Dictionary;
}
namespace net {
class ProxyConfig;
}
namespace atom {
class AtomBrowserContext;
@ -64,7 +68,7 @@ class Session: public mate::TrackableObject<Session>,
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
void ClearCache(const net::CompletionCallback& callback);
void ClearStorageData(mate::Arguments* args);
void SetProxy(const std::string& proxy, const base::Closure& callback);
void SetProxy(const net::ProxyConfig& config, const base::Closure& callback);
void SetDownloadPath(const base::FilePath& path);
void EnableNetworkEmulation(const mate::Dictionary& options);
void DisableNetworkEmulation();

View file

@ -618,6 +618,10 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
if (options.Get("userAgent", &user_agent))
SetUserAgent(user_agent);
std::string extra_headers;
if (options.Get("extraHeaders", &extra_headers))
params.extra_headers = extra_headers;
params.transition_type = ui::PAGE_TRANSITION_TYPED;
params.should_clear_history_list = true;
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;

View file

@ -60,6 +60,16 @@ void OnCapturePageDone(
callback.Run(gfx::Image::CreateFrom1xBitmap(bitmap));
}
#if defined(OS_WIN)
v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size);
if (buffer.IsEmpty())
return v8::Null(isolate);
else
return buffer.ToLocalChecked();
}
#endif
} // namespace
@ -189,6 +199,16 @@ void Window::OnExecuteWindowsCommand(const std::string& command_name) {
Emit("app-command", command_name);
}
#if defined(OS_WIN)
void Window::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {
if (IsWindowMessageHooked(message)) {
messages_callback_map_[message].Run(
ToBuffer(isolate(), static_cast<void*>(&w_param), sizeof(WPARAM)),
ToBuffer(isolate(), static_cast<void*>(&l_param), sizeof(LPARAM)));
}
}
#endif
// static
mate::Wrappable* Window::New(v8::Isolate* isolate,
const mate::Dictionary& options) {
@ -385,6 +405,10 @@ bool Window::IsKiosk() {
return window_->IsKiosk();
}
void Window::SetBackgroundColor(const std::string& color_name) {
window_->SetBackgroundColor(color_name);
}
void Window::FocusOnWebView() {
window_->FocusOnWebView();
}
@ -488,6 +512,29 @@ bool Window::IsMenuBarVisible() {
return window_->IsMenuBarVisible();
}
#if defined(OS_WIN)
bool Window::HookWindowMessage(UINT message,
const MessageCallback& callback) {
messages_callback_map_[message] = callback;
return true;
}
void Window::UnhookWindowMessage(UINT message) {
if (!ContainsKey(messages_callback_map_, message))
return;
messages_callback_map_.erase(message);
}
bool Window::IsWindowMessageHooked(UINT message) {
return ContainsKey(messages_callback_map_, message);
}
void Window::UnhookAllWindowMessages() {
messages_callback_map_.clear();
}
#endif
#if defined(OS_MACOSX)
void Window::ShowDefinitionForSelection() {
window_->ShowDefinitionForSelection();
@ -564,6 +611,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setSkipTaskbar", &Window::SetSkipTaskbar)
.SetMethod("setKiosk", &Window::SetKiosk)
.SetMethod("isKiosk", &Window::IsKiosk)
.SetMethod("setBackgroundColor", &Window::SetBackgroundColor)
.SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename)
.SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename)
.SetMethod("setDocumentEdited", &Window::SetDocumentEdited)
@ -585,6 +633,12 @@ void Window::BuildPrototype(v8::Isolate* isolate,
&Window::SetVisibleOnAllWorkspaces)
.SetMethod("isVisibleOnAllWorkspaces",
&Window::IsVisibleOnAllWorkspaces)
#if defined(OS_WIN)
.SetMethod("hookWindowMessage", &Window::HookWindowMessage)
.SetMethod("isWindowMessageHooked", &Window::IsWindowMessageHooked)
.SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage)
.SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages)
#endif
#if defined(OS_MACOSX)
.SetMethod("showDefinitionForSelection",
&Window::ShowDefinitionForSelection)

View file

@ -5,6 +5,7 @@
#ifndef ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#define ATOM_BROWSER_API_ATOM_API_WINDOW_H_
#include <map>
#include <string>
#include <vector>
@ -75,6 +76,10 @@ class Window : public mate::TrackableObject<Window>,
void OnRendererResponsive() override;
void OnExecuteWindowsCommand(const std::string& command_name) override;
#if defined(OS_WIN)
void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) override;
#endif
// mate::Wrappable:
bool IsDestroyed() const override;
@ -122,6 +127,7 @@ class Window : public mate::TrackableObject<Window>,
void SetSkipTaskbar(bool skip);
void SetKiosk(bool kiosk);
bool IsKiosk();
void SetBackgroundColor(const std::string& color_name);
void FocusOnWebView();
void BlurWebView();
bool IsWebViewFocused();
@ -142,6 +148,16 @@ class Window : public mate::TrackableObject<Window>,
bool IsMenuBarVisible();
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
#if defined(OS_WIN)
typedef base::Callback<void(v8::Local<v8::Value>,
v8::Local<v8::Value>)> MessageCallback;
bool HookWindowMessage(UINT message, const MessageCallback& callback);
bool IsWindowMessageHooked(UINT message);
void UnhookWindowMessage(UINT message);
void UnhookAllWindowMessages();
#endif
#if defined(OS_MACOSX)
void ShowDefinitionForSelection();
#endif
@ -152,6 +168,11 @@ class Window : public mate::TrackableObject<Window>,
int32_t ID() const;
v8::Local<v8::Value> WebContents(v8::Isolate* isolate);
#if defined(OS_WIN)
typedef std::map<UINT, MessageCallback> MessageCallbackMap;
MessageCallbackMap messages_callback_map_;
#endif
v8::Global<v8::Value> web_contents_;
v8::Global<v8::Value> menu_;

View file

@ -1,24 +1,7 @@
autoUpdater = process.atomBinding('auto_updater').autoUpdater
EventEmitter = require('events').EventEmitter
autoUpdater.__proto__ = EventEmitter.prototype
autoUpdater.on 'update-downloaded-raw', (args...) ->
args[3] = new Date(args[3]) # releaseDate
@emit 'update-downloaded', args..., => @quitAndInstall()
autoUpdater.quitAndInstall = ->
# If we don't have any window then quitAndInstall immediately.
BrowserWindow = require 'browser-window'
windows = BrowserWindow.getAllWindows()
if windows.length is 0
@_quitAndInstall()
return
# Do the restart after all windows have been closed.
app = require 'app'
app.removeAllListeners 'window-all-closed'
app.once 'window-all-closed', @_quitAndInstall.bind(this)
win.close() for win in windows
module.exports = autoUpdater
switch process.platform
when 'win32'
module.exports = require './auto-updater/auto-updater-win'
when 'darwin'
module.exports = require './auto-updater/auto-updater-mac'
else
throw new Error('auto-updater is not implemented on this platform')

View file

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

View file

@ -0,0 +1,42 @@
app = require 'app'
url = require 'url'
{EventEmitter} = require 'events'
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,67 @@
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

@ -62,17 +62,15 @@ void AtomBrowserMainParts::PreEarlyInitialization() {
void AtomBrowserMainParts::PostEarlyInitialization() {
brightray::BrowserMainParts::PostEarlyInitialization();
{
// Temporary set the bridge_task_runner_ as current thread's task runner,
// so we can fool gin::PerIsolateData to use it as its task runner, instead
// of getting current message loop's task runner, which is null for now.
bridge_task_runner_ = new BridgeTaskRunner;
base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
// Temporary set the bridge_task_runner_ as current thread's task runner,
// so we can fool gin::PerIsolateData to use it as its task runner, instead
// of getting current message loop's task runner, which is null for now.
bridge_task_runner_ = new BridgeTaskRunner;
base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
// The ProxyResolverV8 has setup a complete V8 environment, in order to
// avoid conflicts we only initialize our V8 environment after that.
js_env_.reset(new JavascriptEnvironment);
}
// The ProxyResolverV8 has setup a complete V8 environment, in order to
// avoid conflicts we only initialize our V8 environment after that.
js_env_.reset(new JavascriptEnvironment);
node_bindings_->Initialize();
@ -107,6 +105,7 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
1000));
brightray::BrowserMainParts::PreMainMessageLoopRun();
BridgeTaskRunner::MessageLoopIsReady();
#if defined(USE_X11)
libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());

View file

@ -4,6 +4,7 @@
#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
#include "atom/browser/login_handler.h"
#include "atom/common/platform_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/escape.h"
@ -29,4 +30,11 @@ bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
return true;
}
content::ResourceDispatcherHostLoginDelegate*
AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
net::AuthChallengeInfo* auth_info,
net::URLRequest* request) {
return new LoginHandler(auth_info, request);
}
} // namespace atom

View file

@ -21,6 +21,9 @@ class AtomResourceDispatcherHostDelegate
bool is_main_frame,
ui::PageTransition transition,
bool has_user_gesture) override;
content::ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
net::AuthChallengeInfo* auth_info,
net::URLRequest* request) override;
};
} // namespace atom

View file

@ -6,22 +6,25 @@
namespace auto_updater {
AutoUpdaterDelegate* AutoUpdater::delegate_ = NULL;
Delegate* AutoUpdater::delegate_ = nullptr;
AutoUpdaterDelegate* AutoUpdater::GetDelegate() {
Delegate* AutoUpdater::GetDelegate() {
return delegate_;
}
void AutoUpdater::SetDelegate(AutoUpdaterDelegate* delegate) {
void AutoUpdater::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
#if defined(OS_MACOSX) && defined(MAS_BUILD)
#if !defined(OS_MACOSX) || defined(MAS_BUILD)
void AutoUpdater::SetFeedURL(const std::string& url) {
}
void AutoUpdater::CheckForUpdates() {
}
void AutoUpdater::QuitAndInstall() {
}
#endif
} // namespace auto_updater

View file

@ -9,21 +9,48 @@
#include "base/basictypes.h"
namespace base {
class Time;
}
namespace auto_updater {
class AutoUpdaterDelegate;
class Delegate {
public:
// An error happened.
virtual void OnError(const std::string& error) {}
// Checking to see if there is an update
virtual void OnCheckingForUpdate() {}
// There is an update available and it is being downloaded
virtual void OnUpdateAvailable() {}
// There is no available update.
virtual void OnUpdateNotAvailable() {}
// There is a new update which has been downloaded.
virtual void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url) {}
protected:
virtual ~Delegate() {}
};
class AutoUpdater {
public:
// Gets/Sets the delegate.
static AutoUpdaterDelegate* GetDelegate();
static void SetDelegate(AutoUpdaterDelegate* delegate);
static Delegate* GetDelegate();
static void SetDelegate(Delegate* delegate);
static void SetFeedURL(const std::string& url);
static void CheckForUpdates();
static void QuitAndInstall();
private:
static AutoUpdaterDelegate* delegate_;
static Delegate* delegate_;
DISALLOW_IMPLICIT_CONSTRUCTORS(AutoUpdater);
};

View file

@ -1,45 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
#define ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
#include <string>
#include "base/callback_forward.h"
namespace base {
class Time;
}
namespace auto_updater {
class AutoUpdaterDelegate {
public:
// An error happened.
virtual void OnError(const std::string& error) {}
// Checking to see if there is an update
virtual void OnCheckingForUpdate() {}
// There is an update available and it is being downloaded
virtual void OnUpdateAvailable() {}
// There is no available update.
virtual void OnUpdateNotAvailable() {}
// There is a new update which has been downloaded.
virtual void OnUpdateDownloaded(const std::string& release_notes,
const std::string& release_name,
const base::Time& release_date,
const std::string& update_url,
const base::Closure& quit_and_install) {}
protected:
virtual ~AutoUpdaterDelegate() {}
};
} // namespace auto_updater
#endif // ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_

View file

@ -1,17 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/auto_updater.h"
namespace auto_updater {
// static
void AutoUpdater::SetFeedURL(const std::string& url) {
}
// static
void AutoUpdater::CheckForUpdates() {
}
} // namespace auto_updater

View file

@ -12,9 +12,6 @@
#include "base/bind.h"
#include "base/time/time.h"
#include "base/strings/sys_string_conversions.h"
#include "atom/browser/auto_updater_delegate.h"
#include <iostream>
namespace auto_updater {
@ -23,20 +20,12 @@ namespace {
// The gloal SQRLUpdater object.
SQRLUpdater* g_updater = nil;
void RelaunchToInstallUpdate() {
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
AutoUpdaterDelegate* delegate = AutoUpdater::GetDelegate();
if (delegate)
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
}];
}
} // namespace
// static
void AutoUpdater::SetFeedURL(const std::string& feed) {
if (g_updater == nil) {
AutoUpdaterDelegate* delegate = GetDelegate();
Delegate* delegate = GetDelegate();
if (!delegate)
return;
@ -67,7 +56,7 @@ void AutoUpdater::SetFeedURL(const std::string& feed) {
// static
void AutoUpdater::CheckForUpdates() {
AutoUpdaterDelegate* delegate = GetDelegate();
Delegate* delegate = GetDelegate();
if (!delegate)
return;
@ -86,8 +75,7 @@ void AutoUpdater::CheckForUpdates() {
base::SysNSStringToUTF8(update.releaseNotes),
base::SysNSStringToUTF8(update.releaseName),
base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970),
base::SysNSStringToUTF8(update.updateURL.absoluteString),
base::Bind(RelaunchToInstallUpdate));
base::SysNSStringToUTF8(update.updateURL.absoluteString));
} else {
// When the completed event is sent with no update, then we know there
// is no update available.
@ -100,4 +88,12 @@ void AutoUpdater::CheckForUpdates() {
}];
}
void AutoUpdater::QuitAndInstall() {
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
Delegate* delegate = AutoUpdater::GetDelegate();
if (delegate)
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
}];
}
} // namespace auto_updater

View file

@ -1,17 +0,0 @@
// Copyright (c) 2013 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/auto_updater.h"
namespace auto_updater {
// static
void AutoUpdater::SetFeedURL(const std::string& url) {
}
// static
void AutoUpdater::CheckForUpdates() {
}
} // namespace auto_updater

View file

@ -8,13 +8,33 @@
namespace atom {
// static
std::vector<BridgeTaskRunner::TaskPair> BridgeTaskRunner::tasks_;
std::vector<BridgeTaskRunner::TaskPair> BridgeTaskRunner::non_nestable_tasks_;
// static
void BridgeTaskRunner::MessageLoopIsReady() {
auto message_loop = base::MessageLoop::current();
CHECK(message_loop);
for (const TaskPair& task : tasks_) {
message_loop->task_runner()->PostDelayedTask(
base::get<0>(task), base::get<1>(task), base::get<2>(task));
}
for (const TaskPair& task : non_nestable_tasks_) {
message_loop->task_runner()->PostNonNestableDelayedTask(
base::get<0>(task), base::get<1>(task), base::get<2>(task));
}
}
bool BridgeTaskRunner::PostDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
auto message_loop = base::MessageLoop::current();
if (!message_loop)
return false;
if (!message_loop) {
tasks_.push_back(base::MakeTuple(from_here, task, delay));
return true;
}
return message_loop->task_runner()->PostDelayedTask(from_here, task, delay);
}
@ -22,7 +42,7 @@ bool BridgeTaskRunner::PostDelayedTask(
bool BridgeTaskRunner::RunsTasksOnCurrentThread() const {
auto message_loop = base::MessageLoop::current();
if (!message_loop)
return false;
return true;
return message_loop->task_runner()->RunsTasksOnCurrentThread();
}
@ -32,8 +52,10 @@ bool BridgeTaskRunner::PostNonNestableDelayedTask(
const base::Closure& task,
base::TimeDelta delay) {
auto message_loop = base::MessageLoop::current();
if (!message_loop)
return false;
if (!message_loop) {
non_nestable_tasks_.push_back(base::MakeTuple(from_here, task, delay));
return true;
}
return message_loop->task_runner()->PostNonNestableDelayedTask(
from_here, task, delay);

View file

@ -5,17 +5,23 @@
#ifndef ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
#define ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
#include <vector>
#include "base/single_thread_task_runner.h"
#include "base/tuple.h"
namespace atom {
// Post all tasks to the current message loop's task runner if available,
// otherwise fail silently.
// otherwise delay the work until message loop is ready.
class BridgeTaskRunner : public base::SingleThreadTaskRunner {
public:
BridgeTaskRunner() {}
~BridgeTaskRunner() override {}
// Called when message loop is ready.
static void MessageLoopIsReady();
// base::SingleThreadTaskRunner:
bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
@ -27,6 +33,11 @@ class BridgeTaskRunner : public base::SingleThreadTaskRunner {
base::TimeDelta delay) override;
private:
using TaskPair = base::Tuple<
tracked_objects::Location, base::Closure, base::TimeDelta>;
static std::vector<TaskPair> tasks_;
static std::vector<TaskPair> non_nestable_tasks_;
DISALLOW_COPY_AND_ASSIGN(BridgeTaskRunner);
};

View file

@ -53,8 +53,14 @@ void Browser::Shutdown() {
is_quiting_ = true;
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
base::MessageLoop::current()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
if (base::MessageLoop::current()) {
base::MessageLoop::current()->PostTask(
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
} else {
// There is no message loop available so we are in early stage.
exit(0);
}
}
std::string Browser::GetVersion() const {
@ -128,6 +134,10 @@ void Browser::ClientCertificateSelector(
delegate.Pass()));
}
void Browser::RequestLogin(LoginHandler* login_handler) {
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
}
void Browser::NotifyAndShutdown() {
if (is_shutdown_)
return;

View file

@ -29,6 +29,8 @@ class MenuModel;
namespace atom {
class LoginHandler;
// This class is used for control application-wide operations.
class Browser : public WindowListObserver {
public:
@ -122,6 +124,9 @@ class Browser : public WindowListObserver {
net::SSLCertRequestInfo* cert_request_info,
scoped_ptr<content::ClientCertificateDelegate> delegate);
// Request basic auth login.
void RequestLogin(LoginHandler* login_handler);
void AddObserver(BrowserObserver* obs) {
observers_.AddObserver(obs);
}
@ -130,6 +135,7 @@ class Browser : public WindowListObserver {
observers_.RemoveObserver(obs);
}
bool is_shutting_down() const { return is_shutdown_; }
bool is_quiting() const { return is_quiting_; }
bool is_ready() const { return is_ready_; }

View file

@ -20,6 +20,8 @@ class SSLCertRequestInfo;
namespace atom {
class LoginHandler;
class BrowserObserver {
public:
// The browser is about to close all windows.
@ -57,6 +59,9 @@ class BrowserObserver {
net::SSLCertRequestInfo* cert_request_info,
scoped_ptr<content::ClientCertificateDelegate> delegate) {}
// The browser requests HTTP login.
virtual void OnLogin(LoginHandler* login_handler) {}
protected:
virtual ~BrowserObserver() {}
};

View file

@ -4,6 +4,7 @@
#include "atom/browser/javascript_environment.h"
#include "base/command_line.h"
#include "gin/array_buffer.h"
#include "gin/v8_initializer.h"
@ -20,6 +21,12 @@ JavascriptEnvironment::JavascriptEnvironment()
}
bool JavascriptEnvironment::Initialize() {
auto cmd = base::CommandLine::ForCurrentProcess();
if (cmd->HasSwitch("debug-brk")) {
// Need to be called before v8::Initialize().
const char expose_debug_as[] = "--expose_debug_as=v8debug";
v8::V8::SetFlagsFromString(expose_debug_as, sizeof(expose_debug_as) - 1);
}
gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode,
gin::ArrayBufferAllocator::SharedInstance());
return true;

View file

@ -10,6 +10,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
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? and value.constructor.name is 'Promise'
# Treat simple objects as value.
@ -36,6 +38,10 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
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.message = value.message
else if meta.type is 'date'
meta.value = value.getTime()
else
meta.type = 'value'
meta.value = value
@ -43,8 +49,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta
# Convert Error into meta data.
errorToMeta = (error) ->
type: 'error', message: error.message, stack: (error.stack || error)
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) ->
@ -100,19 +106,19 @@ ipc.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
try
event.returnValue = valueToMeta event.sender, process.mainModule.require(module)
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
try
event.returnValue = valueToMeta event.sender, global[name]
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
try
event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow()
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
event.returnValue = valueToMeta event.sender, event.sender
@ -126,7 +132,7 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
try
@ -134,7 +140,7 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
func = objectsRegistry.get id
callFunction event, func, global, args
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
try
@ -144,7 +150,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
try
@ -152,7 +158,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
obj = objectsRegistry.get id
callFunction event, obj[method], obj, args
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
try
@ -160,14 +166,14 @@ ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
obj[name] = value
event.returnValue = null
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
try
obj = objectsRegistry.get id
event.returnValue = valueToMeta event.sender, obj[name]
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
objectsRegistry.remove event.sender.getId(), id
@ -177,4 +183,4 @@ ipc.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
guestViewManager = require './guest-view-manager'
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
catch e
event.returnValue = errorToMeta e
event.returnValue = exceptionToMeta e

View file

@ -0,0 +1,109 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/login_handler.h"
#include "atom/browser/browser.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "net/base/auth.h"
#include "net/url_request/url_request.h"
using content::BrowserThread;
namespace atom {
namespace {
// Helper to remove the ref from an net::URLRequest to the LoginHandler.
// Should only be called from the IO thread, since it accesses an
// net::URLRequest.
void ResetLoginHandlerForRequest(net::URLRequest* request) {
content::ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request);
}
} // namespace
LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
net::URLRequest* request)
: handled_auth_(false),
auth_info_(auth_info),
request_(request),
render_process_host_id_(0),
render_frame_id_(0) {
content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
&render_process_host_id_, &render_frame_id_);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&Browser::RequestLogin,
base::Unretained(Browser::Get()),
make_scoped_refptr(this)));
}
LoginHandler::~LoginHandler() {
}
content::WebContents* LoginHandler::GetWebContents() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
render_process_host_id_, render_frame_id_);
return content::WebContents::FromRenderFrameHost(rfh);
}
void LoginHandler::Login(const base::string16& username,
const base::string16& password) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (TestAndSetAuthHandled())
return;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&LoginHandler::DoLogin, this, username, password));
}
void LoginHandler::CancelAuth() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (TestAndSetAuthHandled())
return;
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&LoginHandler::DoCancelAuth, this));
}
void LoginHandler::OnRequestCancelled() {
TestAndSetAuthHandled();
request_ = nullptr;
}
// Marks authentication as handled and returns the previous handled state.
bool LoginHandler::TestAndSetAuthHandled() {
base::AutoLock lock(handled_auth_lock_);
bool was_handled = handled_auth_;
handled_auth_ = true;
return was_handled;
}
void LoginHandler::DoCancelAuth() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (request_) {
request_->CancelAuth();
// Verify that CancelAuth doesn't destroy the request via our delegate.
DCHECK(request_ != nullptr);
ResetLoginHandlerForRequest(request_);
}
}
void LoginHandler::DoLogin(const base::string16& username,
const base::string16& password) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (request_) {
request_->SetAuth(net::AuthCredentials(username, password));
ResetLoginHandlerForRequest(request_);
}
}
} // namespace atom

View file

@ -0,0 +1,76 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_LOGIN_HANDLER_H_
#define ATOM_BROWSER_LOGIN_HANDLER_H_
#include "base/strings/string16.h"
#include "base/synchronization/lock.h"
#include "content/public/browser/resource_dispatcher_host_login_delegate.h"
namespace content {
class WebContents;
}
namespace net {
class AuthChallengeInfo;
class URLRequest;
}
namespace atom {
// Handles the HTTP basic auth, must be created on IO thread.
class LoginHandler : public content::ResourceDispatcherHostLoginDelegate {
public:
LoginHandler(net::AuthChallengeInfo* auth_info, net::URLRequest* request);
// Returns the WebContents associated with the request, must be called on UI
// thread.
content::WebContents* GetWebContents() const;
// The auth is cancelled, must be called on UI thread.
void CancelAuth();
// Login with |username| and |password|, must be called on UI thread.
void Login(const base::string16& username, const base::string16& password);
const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
const net::URLRequest* request() const { return request_; }
protected:
~LoginHandler() override;
// content::ResourceDispatcherHostLoginDelegate:
void OnRequestCancelled() override;
private:
// Must be called on IO thread.
void DoCancelAuth();
void DoLogin(const base::string16& username, const base::string16& password);
// Marks authentication as handled and returns the previous handled
// state.
bool TestAndSetAuthHandled();
// True if we've handled auth (Login or CancelAuth has been called).
bool handled_auth_;
mutable base::Lock handled_auth_lock_;
// Who/where/what asked for the authentication.
scoped_refptr<net::AuthChallengeInfo> auth_info_;
// The request that wants login data.
// This should only be accessed on the IO loop.
net::URLRequest* request_;
// Cached from the net::URLRequest, in case it goes NULL on us.
int render_process_host_id_;
int render_frame_id_;
DISALLOW_COPY_AND_ASSIGN(LoginHandler);
};
} // namespace atom
#endif // ATOM_BROWSER_LOGIN_HANDLER_H_

View file

@ -139,6 +139,10 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
if (options.Get(switches::kKiosk, &kiosk) && kiosk) {
SetKiosk(kiosk);
}
std::string color;
if (options.Get(switches::kBackgroundColor, &color)) {
SetBackgroundColor(color);
}
std::string title("Electron");
options.Get(switches::kTitle, &title);
SetTitle(title);
@ -460,6 +464,14 @@ void NativeWindow::NotifyWindowExecuteWindowsCommand(
OnExecuteWindowsCommand(command));
}
#if defined(OS_WIN)
void NativeWindow::NotifyWindowMessage(
UINT message, WPARAM w_param, LPARAM l_param) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
OnWindowMessage(message, w_param, l_param));
}
#endif
scoped_ptr<SkRegion> NativeWindow::DraggableRegionsToSkRegion(
const std::vector<DraggableRegion>& regions) {
scoped_ptr<SkRegion> sk_region(new SkRegion);

View file

@ -134,6 +134,7 @@ class NativeWindow : public base::SupportsUserData,
virtual void SetSkipTaskbar(bool skip) = 0;
virtual void SetKiosk(bool kiosk) = 0;
virtual bool IsKiosk() = 0;
virtual void SetBackgroundColor(const std::string& color_name) = 0;
virtual void SetRepresentedFilename(const std::string& filename);
virtual std::string GetRepresentedFilename();
virtual void SetDocumentEdited(bool edited);
@ -209,6 +210,10 @@ class NativeWindow : public base::SupportsUserData,
void NotifyWindowLeaveHtmlFullScreen();
void NotifyWindowExecuteWindowsCommand(const std::string& command);
#if defined(OS_WIN)
void NotifyWindowMessage(UINT message, WPARAM w_param, LPARAM l_param);
#endif
void AddObserver(NativeWindowObserver* obs) {
observers_.AddObserver(obs);
}

View file

@ -57,6 +57,7 @@ class NativeWindowMac : public NativeWindow {
void SetSkipTaskbar(bool skip) override;
void SetKiosk(bool kiosk) override;
bool IsKiosk() override;
void SetBackgroundColor(const std::string& color_name) override;
void SetRepresentedFilename(const std::string& filename) override;
std::string GetRepresentedFilename() override;
void SetDocumentEdited(bool edited) override;

View file

@ -635,6 +635,9 @@ bool NativeWindowMac::IsKiosk() {
return is_kiosk_;
}
void NativeWindowMac::SetBackgroundColor(const std::string& color_name) {
}
void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
[window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
}

View file

@ -11,6 +11,10 @@
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
namespace atom {
class NativeWindowObserver {
@ -55,6 +59,11 @@ class NativeWindowObserver {
virtual void OnWindowEnterHtmlFullScreen() {}
virtual void OnWindowLeaveHtmlFullScreen() {}
// Called when window message received
#if defined(OS_WIN)
virtual void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {}
#endif
// Called when renderer is hung.
virtual void OnRendererUnresponsive() {}

View file

@ -42,6 +42,7 @@
#elif defined(OS_WIN)
#include "atom/browser/ui/views/win_frame_view.h"
#include "atom/browser/ui/win/atom_desktop_window_tree_host_win.h"
#include "skia/ext/skia_utils_win.h"
#include "ui/base/win/shell.h"
#include "ui/gfx/win/dpi.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
@ -77,6 +78,29 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
(modifiers == (Modifiers::AltKey | Modifiers::IsRight));
}
SkColor ParseHexColor(const std::string& name) {
SkColor result = 0xFF000000;
unsigned value = 0;
auto color = name.substr(1);
unsigned length = color.size();
if (length != 3 && length != 6)
return result;
for (unsigned i = 0; i < length; ++i) {
if (!base::IsHexDigit(color[i]))
return result;
value <<= 4;
value |= (color[i] < 'A' ? color[i] - '0' : (color[i] - 'A' + 10) & 0xF);
}
if (length == 6) {
result |= value;
return result;
}
result |= (value & 0xF00) << 12 | (value & 0xF00) << 8
| (value & 0xF0) << 8 | (value & 0xF0) << 4
| (value & 0xF) << 4 | (value & 0xF);
return result;
}
class NativeWindowClientView : public views::ClientView {
public:
NativeWindowClientView(views::Widget* widget,
@ -205,7 +229,7 @@ NativeWindowViews::NativeWindowViews(
// Add web view.
SetLayoutManager(new MenuLayout(this, kMenuBarHeight));
set_background(views::Background::CreateStandardPanelBackground());
AddChildView(web_view_);
#if defined(OS_WIN)
@ -496,6 +520,21 @@ bool NativeWindowViews::IsKiosk() {
return IsFullscreen();
}
void NativeWindowViews::SetBackgroundColor(const std::string& color_name) {
// web views' background color.
SkColor background_color = ParseHexColor(color_name);
set_background(views::Background::CreateSolidBackground(background_color));
#if defined(OS_WIN)
// Set the background color of native window.
HBRUSH brush = CreateSolidBrush(skia::SkColorToCOLORREF(background_color));
ULONG_PTR previous_brush = SetClassLongPtr(
GetAcceleratedWidget(), GCLP_HBRBACKGROUND, (LONG)brush);
if (previous_brush)
DeleteObject((HBRUSH)previous_brush);
#endif
}
void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
if (menu_model == nullptr) {
// Remove accelerators

View file

@ -77,6 +77,7 @@ class NativeWindowViews : public NativeWindow,
void SetSkipTaskbar(bool skip) override;
void SetKiosk(bool kiosk) override;
bool IsKiosk() override;
void SetBackgroundColor(const std::string& color_name) override;
void SetMenu(ui::MenuModel* menu_model) override;
gfx::NativeWindow GetNativeWindow() override;
void SetOverlayIcon(const gfx::Image& overlay,

View file

@ -80,6 +80,8 @@ bool NativeWindowViews::ExecuteWindowsCommand(int command_id) {
bool NativeWindowViews::PreHandleMSG(
UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) {
NotifyWindowMessage(message, w_param, l_param);
switch (message) {
case WM_COMMAND:
// Handle thumbar button click message.

View file

@ -8,7 +8,6 @@
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/v8_value_converter.h"
#include "native_mate/function_template.h"
namespace atom {
@ -16,34 +15,14 @@ namespace internal {
namespace {
struct CallbackHolder {
ResponseCallback callback;
};
// Cached JavaScript version of |HandlerCallback|.
v8::Persistent<v8::FunctionTemplate> g_handler_callback_;
// The callback which is passed to |handler|.
void HandlerCallback(v8::Isolate* isolate,
v8::Local<v8::External> external,
v8::Local<v8::Object> state,
mate::Arguments* args) {
// Check if the callback has already been called.
v8::Local<v8::String> called_symbol = mate::StringToSymbol(isolate, "called");
if (state->Has(called_symbol))
return; // no nothing
else
state->Set(called_symbol, v8::Boolean::New(isolate, true));
void HandlerCallback(const ResponseCallback& callback, mate::Arguments* args) {
// If there is no argument passed then we failed.
scoped_ptr<CallbackHolder> holder(
static_cast<CallbackHolder*>(external->Value()));
CHECK(holder);
v8::Local<v8::Value> value;
if (!args->GetNext(&value)) {
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(holder->callback, false, nullptr));
base::Bind(callback, false, nullptr));
return;
}
@ -53,42 +32,7 @@ void HandlerCallback(v8::Isolate* isolate,
scoped_ptr<base::Value> options(converter.FromV8Value(value, context));
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(holder->callback, true, base::Passed(&options)));
}
// func.bind(func, arg1, arg2).
// NB(zcbenz): Using C++11 version crashes VS.
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2) {
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
CHECK(!bind.IsEmpty());
v8::Local<v8::Function> bind_func =
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
v8::Local<v8::Value> converted[] = { func, arg1, arg2 };
return bind_func->Call(
context, func, arraysize(converted), converted).ToLocalChecked();
}
// Generate the callback that will be passed to |handler|.
v8::MaybeLocal<v8::Value> GenerateCallback(v8::Isolate* isolate,
v8::Local<v8::Context> context,
const ResponseCallback& callback) {
// The FunctionTemplate is cached.
if (g_handler_callback_.IsEmpty())
g_handler_callback_.Reset(
isolate,
mate::CreateFunctionTemplate(isolate, base::Bind(&HandlerCallback)));
v8::Local<v8::FunctionTemplate> handler_callback =
v8::Local<v8::FunctionTemplate>::New(isolate, g_handler_callback_);
CallbackHolder* holder = new CallbackHolder;
holder->callback = callback;
return BindFunctionWith(isolate, context, handler_callback->GetFunction(),
v8::External::New(isolate, holder),
v8::Object::New(isolate));
base::Bind(callback, true, base::Passed(&options)));
}
} // namespace
@ -102,16 +46,9 @@ void AskForOptions(v8::Isolate* isolate,
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Context::Scope context_scope(context);
// We don't convert the callback to C++ directly because creating
// FunctionTemplate will cause memory leak since V8 never releases it. So we
// have to create the function object in JavaScript to work around it.
v8::MaybeLocal<v8::Value> wrapped_callback = GenerateCallback(
isolate, context, callback);
if (wrapped_callback.IsEmpty()) {
callback.Run(false, nullptr);
return;
}
handler.Run(request, wrapped_callback.ToLocalChecked());
handler.Run(request,
mate::ConvertToV8(isolate,
base::Bind(&HandlerCallback, callback)));
}
bool IsErrorOptions(base::Value* value, int* error) {

View file

@ -35,17 +35,14 @@ NodeDebugger::NodeDebugger(v8::Isolate* isolate)
weak_factory_(this) {
bool use_debug_agent = false;
int port = 5858;
bool wait_for_connection = false;
std::string port_str;
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
if (cmd->HasSwitch("debug")) {
use_debug_agent = true;
port_str = cmd->GetSwitchValueASCII("debug");
}
if (cmd->HasSwitch("debug-brk")) {
} else if (cmd->HasSwitch("debug-brk")) {
use_debug_agent = true;
wait_for_connection = true;
port_str = cmd->GetSwitchValueASCII("debug-brk");
}
@ -56,9 +53,6 @@ NodeDebugger::NodeDebugger(v8::Isolate* isolate)
isolate_->SetData(kIsolateSlot, this);
v8::Debug::SetMessageHandler(DebugMessageHandler);
if (wait_for_connection)
v8::Debug::DebugBreak(isolate_);
uv_async_init(uv_default_loop(), &weak_up_ui_handle_, ProcessMessageInUI);
// Start a new IO thread.

View file

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

View file

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

View file

@ -79,8 +79,25 @@ class FileDialog {
if (!title.empty())
GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str());
if (!filterspec.empty())
GetPtr()->SetDefaultExtension(filterspec.front().pszSpec);
// By default, *.* will be added to the file name if file type is "*.*". In
// Electron, we disable it to make a better experience.
//
// From MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/
// bb775970(v=vs.85).aspx
//
// If SetDefaultExtension is not called, the dialog will not update
// automatically when user choose a new file type in the file dialog.
//
// We set file extension to the first none-wildcard extension to make
// sure the dialog will update file extension automatically.
for (size_t i = 0; i < filterspec.size(); ++i) {
if (std::wstring(filterspec[i].pszSpec) != L"*.*") {
// SetFileTypeIndex is regarded as one-based index.
GetPtr()->SetFileTypeIndex(i+1);
GetPtr()->SetDefaultExtension(filterspec[i].pszSpec);
break;
}
}
SetDefaultFolder(default_path);
}
@ -255,7 +272,8 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window,
bool matched = false;
for (size_t i = 0; i < filter.second.size(); ++i) {
if (base::EndsWith(file_name, filter.second[i], false)) {
if (filter.second[i] == "*" ||
base::EndsWith(file_name, filter.second[i], false)) {
matched = true;
break;;
}

View file

@ -41,7 +41,7 @@ void FatalErrorCallback(const char* location, const char* message) {
}
void Log(const base::string16& message) {
std::cout << message;
std::cout << message << std::flush;
}
} // namespace

View file

@ -1,4 +1,5 @@
module.exports = process.atomBinding 'shell'
if process.platform is 'win32' and process.type is 'renderer'
module.exports.showItemInFolder = require('remote').process.atomBinding('shell').showItemInFolder
module.exports.showItemInFolder = (item) ->
require('remote').require('shell').showItemInFolder item

View file

@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 0
#define ATOM_MINOR_VERSION 34
#define ATOM_PATCH_VERSION 0
#define ATOM_PATCH_VERSION 1
#define ATOM_VERSION_IS_RELEASE 1

View file

@ -0,0 +1,78 @@
// Copyright (c) 2015 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/callback.h"
namespace mate {
namespace internal {
namespace {
struct TranslaterHolder {
Translater translater;
};
// Cached JavaScript version of |CallTranslater|.
v8::Persistent<v8::FunctionTemplate> g_call_translater;
void CallTranslater(v8::Local<v8::External> external,
v8::Local<v8::Object> state,
mate::Arguments* args) {
v8::Isolate* isolate = args->isolate();
// Check if the callback has already been called.
v8::Local<v8::String> called_symbol = mate::StringToSymbol(isolate, "called");
if (state->Has(called_symbol)) {
args->ThrowError("callback can only be called for once");
return;
} else {
state->Set(called_symbol, v8::Boolean::New(isolate, true));
}
TranslaterHolder* holder = static_cast<TranslaterHolder*>(external->Value());
holder->translater.Run(args);
delete holder;
}
// func.bind(func, arg1).
// NB(zcbenz): Using C++11 version crashes VS.
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Function> func,
v8::Local<v8::Value> arg1,
v8::Local<v8::Value> arg2) {
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
CHECK(!bind.IsEmpty());
v8::Local<v8::Function> bind_func =
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
v8::Local<v8::Value> converted[] = { func, arg1, arg2 };
return bind_func->Call(
context, func, arraysize(converted), converted).ToLocalChecked();
}
} // namespace
v8::Local<v8::Value> CreateFunctionFromTranslater(
v8::Isolate* isolate, const Translater& translater) {
// The FunctionTemplate is cached.
if (g_call_translater.IsEmpty())
g_call_translater.Reset(
isolate,
mate::CreateFunctionTemplate(isolate, base::Bind(&CallTranslater)));
v8::Local<v8::FunctionTemplate> call_translater =
v8::Local<v8::FunctionTemplate>::New(isolate, g_call_translater);
TranslaterHolder* holder = new TranslaterHolder;
holder->translater = translater;
return BindFunctionWith(isolate,
isolate->GetCurrentContext(),
call_translater->GetFunction(),
v8::External::New(isolate, holder),
v8::Object::New(isolate));
}
} // namespace internal
} // namespace mate

View file

@ -20,6 +20,7 @@ namespace internal {
typedef scoped_refptr<RefCountedPersistent<v8::Function> > SafeV8Function;
// Helper to invoke a V8 function with C++ parameters.
template <typename Sig>
struct V8FunctionInvoker {};
@ -70,21 +71,47 @@ struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
v8::Local<v8::Function> holder = function->NewHandle();
v8::Local<v8::Context> context = holder->CreationContext();
v8::Context::Scope context_scope(context);
ReturnType ret;
ReturnType ret = ReturnType();
std::vector<v8::Local<v8::Value>> args = { ConvertToV8(isolate, raw)... };
v8::Local<v8::Value> val(holder->Call(holder, args.size(), &args.front()));
Converter<ReturnType>::FromV8(isolate, val, &ret);
v8::Local<v8::Value> result;
auto maybe_result =
holder->Call(context, holder, args.size(), &args.front());
if (maybe_result.ToLocal(&result))
Converter<ReturnType>::FromV8(isolate, result, &ret);
return ret;
}
};
// Helper to pass a C++ funtion to JavaScript.
using Translater = base::Callback<void(Arguments* args)>;
v8::Local<v8::Value> CreateFunctionFromTranslater(
v8::Isolate* isolate, const Translater& translater);
// Calls callback with Arguments.
template <typename Sig>
struct NativeFunctionInvoker {};
template <typename ReturnType, typename... ArgTypes>
struct NativeFunctionInvoker<ReturnType(ArgTypes...)> {
static void Go(base::Callback<ReturnType(ArgTypes...)> val, Arguments* args) {
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
Invoker<Indices, ArgTypes...> invoker(args, 0);
if (invoker.IsOK())
invoker.DispatchToCallback(val);
}
};
} // namespace internal
template<typename Sig>
struct Converter<base::Callback<Sig> > {
struct Converter<base::Callback<Sig>> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const base::Callback<Sig>& val) {
return CreateFunctionTemplate(isolate, val)->GetFunction();
const base::Callback<Sig>& val) {
// We don't use CreateFunctionTemplate here because it creates a new
// FunctionTemplate everytime, which is cached by V8 and causes leaks.
internal::Translater translater = base::Bind(
&internal::NativeFunctionInvoker<Sig>::Go, val);
return internal::CreateFunctionFromTranslater(isolate, translater);
}
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,

View file

@ -0,0 +1,34 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/native_mate_converters/content_converter.h"
#include "native_mate/dictionary.h"
#include "net/url_request/url_request.h"
namespace mate {
// static
v8::Local<v8::Value> Converter<const net::URLRequest*>::ToV8(
v8::Isolate* isolate, const net::URLRequest* val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("method", val->method());
dict.Set("url", val->url().spec());
dict.Set("referrer", val->referrer());
return mate::ConvertToV8(isolate, dict);
}
// static
v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("isProxy", val->is_proxy);
dict.Set("scheme", val->scheme);
dict.Set("host", val->challenger.host());
dict.Set("port", static_cast<uint32_t>(val->challenger.port()));
dict.Set("realm", val->realm);
return mate::ConvertToV8(isolate, dict);
}
} // namespace mate

View file

@ -0,0 +1,31 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
#include "native_mate/converter.h"
namespace net {
class AuthChallengeInfo;
class URLRequest;
}
namespace mate {
template<>
struct Converter<const net::URLRequest*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::URLRequest* val);
};
template<>
struct Converter<const net::AuthChallengeInfo*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::AuthChallengeInfo* val);
};
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_

View file

@ -93,6 +93,9 @@ const char kDisableAutoHideCursor[] = "disable-auto-hide-cursor";
// Use the OS X's standard window instead of the textured window.
const char kStandardWindow[] = "standard-window";
// Default browser window background color.
const char kBackgroundColor[] = "background-color";
// Path to client certificate.
const char kClientCertificate[] = "client-certificate";

View file

@ -47,6 +47,7 @@ extern const char kTransparent[];
extern const char kType[];
extern const char kDisableAutoHideCursor[];
extern const char kStandardWindow[];
extern const char kBackgroundColor[];
extern const char kClientCertificate[];
extern const char kExperimentalFeatures[];

View file

@ -46,7 +46,9 @@ metaToValue = (meta) ->
when 'array' then (metaToValue(el) for el in meta.members)
when 'buffer' then new Buffer(meta.value)
when 'promise' then Promise.resolve(then: metaToValue(meta.then))
when 'error'
when 'error' then new Error(meta.message)
when 'date' then new Date(meta.value)
when 'exception'
throw new Error("#{meta.message}\n#{meta.stack}")
else
if meta.type is 'function'

View file

@ -39,16 +39,8 @@ namespace atom {
namespace {
bool IsSwitchEnabled(base::CommandLine* command_line,
const char* switch_string,
bool* enabled) {
std::string value = command_line->GetSwitchValueASCII(switch_string);
if (value == "true")
*enabled = true;
else if (value == "false")
*enabled = false;
else
return false;
return true;
const char* switch_string) {
return command_line->GetSwitchValueASCII(switch_string) == "true";
}
// Helper class to forward the messages to the client.
@ -216,10 +208,8 @@ bool AtomRendererClient::ShouldOverridePageVisibilityState(
const content::RenderFrame* render_frame,
blink::WebPageVisibilityState* override_state) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool b;
if (IsSwitchEnabled(command_line, switches::kPageVisibility, &b)
&& b) {
if (IsSwitchEnabled(command_line, switches::kPageVisibility)) {
*override_state = blink::WebPageVisibilityStateVisible;
return true;
}
@ -229,17 +219,17 @@ bool AtomRendererClient::ShouldOverridePageVisibilityState(
void AtomRendererClient::EnableWebRuntimeFeatures() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool b;
if (IsSwitchEnabled(command_line, switches::kExperimentalFeatures, &b))
blink::WebRuntimeFeatures::enableExperimentalFeatures(b);
if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures, &b))
blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(b);
if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars, &b))
blink::WebRuntimeFeatures::enableOverlayScrollbars(b);
if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo, &b))
blink::WebRuntimeFeatures::enableOverlayFullscreenVideo(b);
if (IsSwitchEnabled(command_line, switches::kSharedWorker, &b))
blink::WebRuntimeFeatures::enableSharedWorker(b);
if (IsSwitchEnabled(command_line, switches::kExperimentalFeatures))
blink::WebRuntimeFeatures::enableExperimentalFeatures(true);
if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures))
blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(true);
if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars))
blink::WebRuntimeFeatures::enableOverlayScrollbars(true);
if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo))
blink::WebRuntimeFeatures::enableOverlayFullscreenVideo(true);
if (IsSwitchEnabled(command_line, switches::kSharedWorker))
blink::WebRuntimeFeatures::enableSharedWorker(true);
}
} // namespace atom

View file

@ -0,0 +1,86 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chrome_process_finder_win.h"
#include <shellapi.h>
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/process.h"
#include "base/process/process_info.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/message_window.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
namespace {
int timeout_in_milliseconds = 20 * 1000;
} // namespace
namespace chrome {
HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
return base::win::MessageWindow::FindWindow(user_data_dir.value());
}
NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
bool fast_start) {
DCHECK(remote_window);
DWORD process_id = 0;
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
if (!thread_id || !process_id)
return NOTIFY_FAILED;
// Send the command line to the remote chrome window.
// Format is "START\0<<<current directory>>>\0<<<commandline>>>".
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
base::FilePath cur_dir;
if (!base::GetCurrentDirectory(&cur_dir))
return NOTIFY_FAILED;
to_send.append(cur_dir.value());
to_send.append(L"\0", 1); // Null separator.
to_send.append(::GetCommandLineW());
to_send.append(L"\0", 1); // Null separator.
// Allow the current running browser window to make itself the foreground
// window (otherwise it will just flash in the taskbar).
::AllowSetForegroundWindow(process_id);
COPYDATASTRUCT cds;
cds.dwData = 0;
cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
cds.lpData = const_cast<wchar_t*>(to_send.c_str());
DWORD_PTR result = 0;
if (::SendMessageTimeout(remote_window, WM_COPYDATA, NULL,
reinterpret_cast<LPARAM>(&cds), SMTO_ABORTIFHUNG,
timeout_in_milliseconds, &result)) {
return result ? NOTIFY_SUCCESS : NOTIFY_FAILED;
}
// It is possible that the process owning this window may have died by now.
if (!::IsWindow(remote_window))
return NOTIFY_FAILED;
// If the window couldn't be notified but still exists, assume it is hung.
return NOTIFY_WINDOW_HUNG;
}
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout) {
base::TimeDelta old_timeout =
base::TimeDelta::FromMilliseconds(timeout_in_milliseconds);
timeout_in_milliseconds = new_timeout.InMilliseconds();
return old_timeout;
}
} // namespace chrome

View file

@ -0,0 +1,39 @@
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROME_PROCESS_FINDER_WIN_H_
#define CHROME_BROWSER_CHROME_PROCESS_FINDER_WIN_H_
#include <windows.h>
#include "base/time/time.h"
namespace base {
class FilePath;
}
namespace chrome {
enum NotifyChromeResult {
NOTIFY_SUCCESS,
NOTIFY_FAILED,
NOTIFY_WINDOW_HUNG,
};
// Finds an already running Chrome window if it exists.
HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
// Attempts to send the current command line to an already running instance of
// Chrome via a WM_COPYDATA message.
// Returns true if a running Chrome is found and successfully notified.
// |fast_start| is true when this is being called on the window fast start path.
NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
bool fast_start);
// Changes the notification timeout to |new_timeout|, returns the old timeout.
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
} // namespace chrome
#endif // CHROME_BROWSER_CHROME_PROCESS_FINDER_WIN_H_

View file

@ -0,0 +1,182 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PROCESS_SINGLETON_H_
#define CHROME_BROWSER_PROCESS_SINGLETON_H_
#if defined(OS_WIN)
#include <windows.h>
#endif // defined(OS_WIN)
#include <set>
#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/process/process.h"
#include "base/threading/non_thread_safe.h"
#include "ui/gfx/native_widget_types.h"
#if defined(OS_POSIX) && !defined(OS_ANDROID)
#include "base/files/scoped_temp_dir.h"
#endif
#if defined(OS_WIN)
#include "base/win/message_window.h"
#endif // defined(OS_WIN)
namespace base {
class CommandLine;
}
// ProcessSingleton ----------------------------------------------------------
//
// This class allows different browser processes to communicate with
// each other. It is named according to the user data directory, so
// we can be sure that no more than one copy of the application can be
// running at once with a given data directory.
//
// Implementation notes:
// - the Windows implementation uses an invisible global message window;
// - the Linux implementation uses a Unix domain socket in the user data dir.
class ProcessSingleton : public base::NonThreadSafe {
public:
enum NotifyResult {
PROCESS_NONE,
PROCESS_NOTIFIED,
PROFILE_IN_USE,
LOCK_ERROR,
};
// Implement this callback to handle notifications from other processes. The
// callback will receive the command line and directory with which the other
// Chrome process was launched. Return true if the command line will be
// handled within the current browser instance or false if the remote process
// should handle it (i.e., because the current process is shutting down).
using NotificationCallback =
base::Callback<bool(const base::CommandLine::StringVector& command_line,
const base::FilePath& current_directory)>;
ProcessSingleton(const base::FilePath& user_data_dir,
const NotificationCallback& notification_callback);
~ProcessSingleton();
// Notify another process, if available. Otherwise sets ourselves as the
// singleton instance. Returns PROCESS_NONE if we became the singleton
// instance. Callers are guaranteed to either have notified an existing
// process or have grabbed the singleton (unless the profile is locked by an
// unreachable process).
// TODO(brettw): Make the implementation of this method non-platform-specific
// by making Linux re-use the Windows implementation.
NotifyResult NotifyOtherProcessOrCreate();
// Sets ourself up as the singleton instance. Returns true on success. If
// false is returned, we are not the singleton instance and the caller must
// exit.
// NOTE: Most callers should generally prefer NotifyOtherProcessOrCreate() to
// this method, only callers for whom failure is preferred to notifying
// another process should call this directly.
bool Create();
// Clear any lock state during shutdown.
void Cleanup();
#if defined(OS_POSIX) && !defined(OS_ANDROID)
static void DisablePromptForTesting();
#endif
#if defined(OS_WIN)
// Called to query whether to kill a hung browser process that has visible
// windows. Return true to allow killing the hung process.
using ShouldKillRemoteProcessCallback = base::Callback<bool()>;
void OverrideShouldKillRemoteProcessCallbackForTesting(
const ShouldKillRemoteProcessCallback& display_dialog_callback);
#endif
protected:
// Notify another process, if available.
// Returns true if another process was found and notified, false if we should
// continue with the current process.
// On Windows, Create() has to be called before this.
NotifyResult NotifyOtherProcess();
#if defined(OS_POSIX) && !defined(OS_ANDROID)
// Exposed for testing. We use a timeout on Linux, and in tests we want
// this timeout to be short.
NotifyResult NotifyOtherProcessWithTimeout(
const base::CommandLine& command_line,
int retry_attempts,
const base::TimeDelta& timeout,
bool kill_unresponsive);
NotifyResult NotifyOtherProcessWithTimeoutOrCreate(
const base::CommandLine& command_line,
int retry_attempts,
const base::TimeDelta& timeout);
void OverrideCurrentPidForTesting(base::ProcessId pid);
void OverrideKillCallbackForTesting(
const base::Callback<void(int)>& callback);
#endif
private:
NotificationCallback notification_callback_; // Handler for notifications.
#if defined(OS_WIN)
HWND remote_window_; // The HWND_MESSAGE of another browser.
base::win::MessageWindow window_; // The message-only window.
bool is_virtualized_; // Stuck inside Microsoft Softricity VM environment.
HANDLE lock_file_;
base::FilePath user_data_dir_;
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
#elif defined(OS_POSIX) && !defined(OS_ANDROID)
// Start listening to the socket.
void StartListening(int sock);
// Return true if the given pid is one of our child processes.
// Assumes that the current pid is the root of all pids of the current
// instance.
bool IsSameChromeInstance(pid_t pid);
// Extract the process's pid from a symbol link path and if it is on
// the same host, kill the process, unlink the lock file and return true.
// If the process is part of the same chrome instance, unlink the lock file
// and return true without killing it.
// If the process is on a different host, return false.
bool KillProcessByLockPath();
// Default function to kill a process, overridable by tests.
void KillProcess(int pid);
// Allow overriding for tests.
base::ProcessId current_pid_;
// Function to call when the other process is hung and needs to be killed.
// Allows overriding for tests.
base::Callback<void(int)> kill_callback_;
// Path in file system to the socket.
base::FilePath socket_path_;
// Path in file system to the lock.
base::FilePath lock_path_;
// Path in file system to the cookie file.
base::FilePath cookie_path_;
// Temporary directory to hold the socket.
base::ScopedTempDir socket_dir_;
// Helper class for linux specific messages. LinuxWatcher is ref counted
// because it posts messages between threads.
class LinuxWatcher;
scoped_refptr<LinuxWatcher> watcher_;
#endif
DISALLOW_COPY_AND_ASSIGN(ProcessSingleton);
};
#endif // CHROME_BROWSER_PROCESS_SINGLETON_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,328 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/process_singleton.h"
#include <shellapi.h>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/process/process.h"
#include "base/process/process_info.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/win/metro.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/browser/chrome_process_finder_win.h"
#include "content/public/common/result_codes.h"
#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/win/hwnd_util.h"
namespace {
const char kLockfile[] = "lockfile";
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
// scope.
class AutoLockMutex {
public:
explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
}
~AutoLockMutex() {
BOOL released = ::ReleaseMutex(mutex_);
DPCHECK(released);
}
private:
HANDLE mutex_;
DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
};
// A helper class that releases the given |mutex| while the AutoUnlockMutex is
// in scope and immediately re-acquires it when going out of scope.
class AutoUnlockMutex {
public:
explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
BOOL released = ::ReleaseMutex(mutex_);
DPCHECK(released);
}
~AutoUnlockMutex() {
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
}
private:
HANDLE mutex_;
DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
};
// Checks the visibility of the enumerated window and signals once a visible
// window has been found.
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
bool* result = reinterpret_cast<bool*>(param);
*result = ::IsWindowVisible(window) != 0;
// Stops enumeration if a visible window has been found.
return !*result;
}
// Convert Command line string to argv.
base::CommandLine::StringVector CommandLineStringToArgv(
const std::wstring& command_line_string) {
int num_args = 0;
wchar_t** args = NULL;
args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
base::CommandLine::StringVector argv;
for (int i = 0; i < num_args; ++i)
argv.push_back(std::wstring(args[i]));
LocalFree(args);
return argv;
}
bool ParseCommandLine(const COPYDATASTRUCT* cds,
base::CommandLine::StringVector* parsed_command_line,
base::FilePath* current_directory) {
// We should have enough room for the shortest command (min_message_size)
// and also be a multiple of wchar_t bytes. The shortest command
// possible is L"START\0\0" (empty current directory and command line).
static const int min_message_size = 7;
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
cds->cbData % sizeof(wchar_t) != 0) {
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
return false;
}
// We split the string into 4 parts on NULLs.
DCHECK(cds->lpData);
const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
cds->cbData / sizeof(wchar_t));
const std::wstring::size_type first_null = msg.find_first_of(L'\0');
if (first_null == 0 || first_null == std::wstring::npos) {
// no NULL byte, don't know what to do
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
", first null = " << first_null;
return false;
}
// Decode the command, which is everything until the first NULL.
if (msg.substr(0, first_null) == L"START") {
// Another instance is starting parse the command line & do what it would
// have done.
VLOG(1) << "Handling STARTUP request from another process";
const std::wstring::size_type second_null =
msg.find_first_of(L'\0', first_null + 1);
if (second_null == std::wstring::npos ||
first_null == msg.length() - 1 || second_null == msg.length()) {
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
"parts separated by NULLs";
return false;
}
// Get current directory.
*current_directory = base::FilePath(msg.substr(first_null + 1,
second_null - first_null));
const std::wstring::size_type third_null =
msg.find_first_of(L'\0', second_null + 1);
if (third_null == std::wstring::npos ||
third_null == msg.length()) {
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
"parts separated by NULLs";
}
// Get command line.
const std::wstring cmd_line =
msg.substr(second_null + 1, third_null - second_null);
*parsed_command_line = CommandLineStringToArgv(cmd_line);
return true;
}
return false;
}
bool ProcessLaunchNotification(
const ProcessSingleton::NotificationCallback& notification_callback,
UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
if (message != WM_COPYDATA)
return false;
// Handle the WM_COPYDATA message from another process.
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
base::CommandLine::StringVector parsed_command_line;
base::FilePath current_directory;
if (!ParseCommandLine(cds, &parsed_command_line, &current_directory)) {
*result = TRUE;
return true;
}
*result = notification_callback.Run(parsed_command_line, current_directory) ?
TRUE : FALSE;
return true;
}
bool TerminateAppWithError() {
// TODO: This is called when the secondary process can't ping the primary
// process. Need to find out what to do here.
return false;
}
} // namespace
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
is_virtualized_(false),
lock_file_(INVALID_HANDLE_VALUE),
user_data_dir_(user_data_dir),
should_kill_remote_process_callback_(
base::Bind(&TerminateAppWithError)) {
}
ProcessSingleton::~ProcessSingleton() {
if (lock_file_ != INVALID_HANDLE_VALUE)
::CloseHandle(lock_file_);
}
// Code roughly based on Mozilla.
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
if (is_virtualized_)
return PROCESS_NOTIFIED; // We already spawned the process in this case.
if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
return LOCK_ERROR;
} else if (!remote_window_) {
return PROCESS_NONE;
}
switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) {
case chrome::NOTIFY_SUCCESS:
return PROCESS_NOTIFIED;
case chrome::NOTIFY_FAILED:
remote_window_ = NULL;
return PROCESS_NONE;
case chrome::NOTIFY_WINDOW_HUNG:
// Fall through and potentially terminate the hung browser.
break;
}
DWORD process_id = 0;
DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
if (!thread_id || !process_id) {
remote_window_ = NULL;
return PROCESS_NONE;
}
base::Process process = base::Process::Open(process_id);
// The window is hung. Scan for every window to find a visible one.
bool visible_window = false;
::EnumThreadWindows(thread_id,
&BrowserWindowEnumeration,
reinterpret_cast<LPARAM>(&visible_window));
// If there is a visible browser window, ask the user before killing it.
if (visible_window && !should_kill_remote_process_callback_.Run()) {
// The user denied. Quit silently.
return PROCESS_NOTIFIED;
}
// Time to take action. Kill the browser process.
process.Terminate(content::RESULT_CODE_HUNG, true);
remote_window_ = NULL;
return PROCESS_NONE;
}
ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessOrCreate() {
ProcessSingleton::NotifyResult result = PROCESS_NONE;
if (!Create()) {
result = NotifyOtherProcess();
if (result == PROCESS_NONE)
result = PROFILE_IN_USE;
}
return result;
}
// Look for a Chrome instance that uses the same profile directory. If there
// isn't one, create a message window with its title set to the profile
// directory path.
bool ProcessSingleton::Create() {
static const wchar_t kMutexName[] = L"Local\\AtomProcessSingletonStartup!";
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
if (!remote_window_) {
// Make sure we will be the one and only process creating the window.
// We use a named Mutex since we are protecting against multi-process
// access. As documented, it's clearer to NOT request ownership on creation
// since it isn't guaranteed we will get it. It is better to create it
// without ownership and explicitly get the ownership afterward.
base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
if (!only_me.IsValid()) {
DPLOG(FATAL) << "CreateMutex failed";
return false;
}
AutoLockMutex auto_lock_only_me(only_me.Get());
// We now own the mutex so we are the only process that can create the
// window at this time, but we must still check if someone created it
// between the time where we looked for it above and the time the mutex
// was given to us.
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
if (!remote_window_) {
// We have to make sure there is no Chrome instance running on another
// machine that uses the same profile.
base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_DELETE_ON_CLOSE,
NULL);
DWORD error = ::GetLastError();
LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
<< "Lock file can not be created! Error code: " << error;
if (lock_file_ != INVALID_HANDLE_VALUE) {
// Set the window's title to the path of our user data directory so
// other Chrome instances can decide if they should forward to us.
bool result = window_.CreateNamed(
base::Bind(&ProcessLaunchNotification, notification_callback_),
user_data_dir_.value());
// NB: Ensure that if the primary app gets started as elevated
// admin inadvertently, secondary windows running not as elevated
// will still be able to send messages
::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW, NULL);
CHECK(result && window_.hwnd());
}
}
}
return window_.hwnd() != NULL;
}
void ProcessSingleton::Cleanup() {
}
void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
const ShouldKillRemoteProcessCallback& display_dialog_callback) {
should_kill_remote_process_callback_ = display_dialog_callback;
}

View file

@ -62,7 +62,7 @@
## Desarrollo
* [Guía de Estilo](development/coding-style.md)
* [Estructura de los directorios del Código Fuente](../../development/source-code-directory-structure.md)
* [Estructura de los directorios del Código Fuente](development/source-code-directory-structure.md)
* [Diferencias Técnicas con NW.js (anteriormente conocido como node-webkit)](../../development/atom-shell-vs-node-webkit.md)
* [Repaso del Sistema de Compilación](../../development/build-system-overview.md)
* [Instrucciones de Compilación (Mac)](../../development/build-instructions-osx.md)

View file

@ -0,0 +1,37 @@
# Guía de estilo de código
Esta es la guía de estilo de código para Electron.
## C++ y Python
Para C++ y Python, nosotros seguimos la [guía de estilo](http://www.chromium.org/developers/coding-style) de Chromium.
Además hay un script `script/cpplint.py` para verificar si todos los archivos
siguen el estilo.
La versión de Python que estamos usando ahora es Python 2.7.
El código C++ usa muchas abstracciones y tipos de Chromium, por eso
se recomienda familiarizarse con ellos. Un buen lugar para iniciar es
el documento de Chromium sobre [Abstracciones importantes y estructras de datos](https://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures). El documento menciona algunos tipos especiales, tipos por alcance (que
automaticamente liberan su memoria cuando salen de su alcance), mecanismos de
registro de eventos, etcétera.
## CoffeeScript
Para CoffeeScript, nosotros seguimos la [guía de estilo](https://github.com/styleguide/javascript) de Github y también las
siguientes reglas:
* Los archivos **NO** deberían terminar con una nueva línea, por que se busca
seguir los estilos que usa Google.
* Los nombres de los archivos debén estar concatenados con `-` en vez de `_`,
por ejemplo `nombre-de-archivo.coffee` en vez de `nombre_de_archivo.coffee`,
esto es por que en [github/atom](https://github.com/github/atom)
los nombres de los módulos usualmente estan en la forma `nombre-de-modulo`.
Esta regla aplica únicamente a los archivos `.coffee`.
## Nombres de las API
Al crear una nueva API, nosotros deberíamos preferir usar metodos `get` y `set`
en vez de usar el estilo de jQuery que utiliza una sola función. Por ejemplo,
se prefiere `.getText()` y `.setText()` por sobre `.text([text])`. Hay una
[discusión](https://github.com/atom/electron/issues/46) sobre esto.

View file

@ -0,0 +1,62 @@
# Estructura de los directorios del código fuente
El código fuente de electron es separado en pocas partes, en su mayoría
siguiendo las especificaciones para separar archivos que usa Chromium.
Quizá necesites familiarizarte con la [arquitectura multiprocesos](http://dev.chromium.org/developers/design-documents/multi-process-architecture) de Chromium para comprender mejor el código fuente.
## Estructura del código fuente
```
Electron
├──atom - Código fuente de Electron.
| ├── app - Código de arranque.
| ├── browser - La interfaz incluyendo la ventana principal, UI,
| | y todas las cosas del proceso principal. Este le habla al renderizador
| | para manejar las páginas web.
| | ├── lib - Código Javascript para inicializar el proceso principal.
| | ├── ui - Implementaciones de UI para distintas plataformas.
| | | ├── cocoa - Código fuente específico para Cocoa.
| | | ├── gtk - Código fuente específico para GTK+.
| | | └── win - Código fuente específico para Windows GUI.
| | ├── default_app - La página por defecto para mostrar cuando Electron
| | | es iniciado sin proveer una app.
| | ├── api - La implementación de las APIs para el proceso principal.
| | | └── lib - Código Javascript parte de la implementación de la API.
| | ├── net - Código relacionado a la red.
| | ├── mac - Código fuente de Objective-C específico para Mac.
| | └── resources - Iconos, archivos específicos de plataforma, etc.
| ├── renderer - Código que se ejecuta en el proceso de renderizado.
| | ├── lib - Código Javascript del proceso de inicio del renderizador.
| | └── api - La implementación de las APIs para el proceso de renderizado.
| | └── lib - Código Javascript parte de la implementación de la API.
| └── common - Código que se utiliza en ambos procesos, el principal y el de
| renderizado. Incluye algunas funciones de utilidad y código para integrar
| el ciclo de mensajes de Node en el ciclo de mensajes de Chromium.
| ├── lib - Código Javascript común para la inicialización.
| └── api - La implementación de APIs comunes, y los fundamentos de
| los módulos integrados de Electron.
| └── lib - Código Javascript parte de la implementación de la API.
├── chromium_src - Código fuente copiado de Chromium.
├── docs - Documentación.
├── spec - Pruebas automaticas.
├── atom.gyp - Reglas de compilado de Electron.
└── common.gypi - Configuración específica para compilar y reglas
de empaquetado para otros componentes como `node` y `breakpad`.
```
## Estructura de otros directorios
* **script** - Scripts usados para propositos de desarrollo
como compilar, empaquetar, realizar pruebas, etc.
* **tools** - Scripts de ayuda usados por los archivos gyp, contrario a la
carpeta `scripts`, estos scripts nunca deberían ser llamados por los usuarios.
* **vendor** - Código fuente de dependencias externas, no usamos `third_party`
como nombre por que se podría confundir con el mismo directorio
en las carpetas del código fuente de Chromium.
* **node_modules** - Módulos de node usados para la compilación.
* **out** - Directorio temporal de salida usado por `ninja`.
* **dist** - Directorio temporal creado por `script/create-dist.py` cuando
se esta creando una distribución.
* **external_binaries** - Binarios descargados de frameworks externos que no
soportan la compilación con `gyp`.

View file

@ -65,7 +65,6 @@ a ser ejecutado por el proceso principal. Un ejemplo de `package.json` podría v
```
El `main.js` debería crear las ventanas y gestionar los eventos del sistema, un ejemplo típico sería:
example being:
```javascript
var app = require('app'); // Módulo para controlar el ciclo de vida de la aplicación.

View file

@ -0,0 +1,30 @@
# Plataformas soportadas
Las siguientes plataformas son soportadas por Electron:
### OS X
Sólo se proveen binarios de 64 bit para OS X.
La versión mínima soportada es OS X 10.8.
### Windows
Windows 7 y posteriores son soportados, las versiones antiguas no son soportadas (y no funcionan).
Se proveen binarios para las arquitecturas `x86` y `amd64` (x64).
Nota: La versión para `ARM` de Windows no está soportada aún.
### Linux
Los binarios preconstruidos para `ia32`(`i686`) y `x64`(`amd64`) son construidos sobre
Ubuntu 12.04, el binario para `arm` es construido sobre ARM v7 con la ABI hard-float
y NEON para Debian Wheezy.
La posibilidad de que un binario preconstruido se ejecute en una distribución determinada
depende de las librerías contra las que fue enlazado Electron.
Por ahora sólo se garantiza la ejecución en Ubuntu 12.04, aunque también se ha verificado
el funcionamiento de los binarios preconstruidos en las siguientes plataformas:
* Ubuntu 12.04 and later
* Fedora 21
* Debian 8

View file

@ -0,0 +1,73 @@
## คู่มือ
* [แพลตฟอร์มที่รองรับ](tutorial/supported-platforms.md)
* [การเผยแพร่แอปพลิเคชัน](tutorial/application-distribution.md)
* [แนวทางการส่งแอปเข้า Mac App Store](tutorial/mac-app-store-submission-guide.md)
* [การบรรจุแอปพลิเคชัน](tutorial/application-packaging.md)
* [การใช้โมดูลของ Node](tutorial/using-native-node-modules.md)
* [การหาข้อผิดพลาดในกระบวนการหลัก](tutorial/debugging-main-process.md)
* [การใช้งาน Selenium และ WebDriver](tutorial/using-selenium-and-webdriver.md)
* [ส่วนเสริมของ DevTools](tutorial/devtools-extension.md)
* [การใช้งานส่วนเสริม Pepper Flash](tutorial/using-pepper-flash-plugin.md)
## แนะนำ
* [เริ่มต้นอย่างคราวๆ](tutorial/quick-start.md)
* [การร่วมกันของสภาพแวดล้อมบนเดสทอป](tutorial/desktop-environment-integration.md)
* [การตรวจจับเหตุการณ์ออนไลน์หรือออฟไลน์](tutorial/online-offline-events.md)
## แหล่งอ้างอิงของ API
* [สรุปความ](api/synopsis.md)
* [โปรเซสออบเจค](api/process.md)
* [คำสั่งสำหรับเปลี่ยนแปลงค่าของ Chrome ที่รองรับ](api/chrome-command-line-switches.md)
### การปรับแต่ง DOM:
* [วัตถุ `File`](api/file-object.md)
* [แท็ก `<webview>`](api/web-view-tag.md)
* [ฟังก์ชัน `window.open`](api/window-open.md)
### โมดูลสำหรับกระบวนการหลัก :
* [app](api/app.md)
* [auto-updater](api/auto-updater.md)
* [browser-window](api/browser-window.md)
* [content-tracing](api/content-tracing.md)
* [dialog](api/dialog.md)
* [global-shortcut](api/global-shortcut.md)
* [ipc (main process)](api/ipc-main-process.md)
* [menu](api/menu.md)
* [menu-item](api/menu-item.md)
* [power-monitor](api/power-monitor.md)
* [power-save-blocker](api/power-save-blocker.md)
* [protocol](api/protocol.md)
* [session](api/session.md)
* [web-contents](api/web-contents.md)
* [tray](api/tray.md)
### โมดูลสำหรับกระบวนการ Renderer (เว็บเพจ):
* [ipc (renderer)](api/ipc-renderer.md)
* [remote](api/remote.md)
* [web-frame](api/web-frame.md)
### Modules for Both Processes:
* [clipboard](api/clipboard.md)
* [crash-reporter](api/crash-reporter.md)
* [native-image](api/native-image.md)
* [screen](api/screen.md)
* [shell](api/shell.md)
## การพัฒนา
* [ลักษณะการเขียนโค้ด](development/coding-style.md)
* [โครงสร้างไดเรคทอรี่ของซอร์สโค้ด](development/source-code-directory-structure.md)
* [ความแตกต่างทางเทคนิคจาก NW.js (หรือ node-webkit)](development/atom-shell-vs-node-webkit.md)
* [ภาพรวมการสร้างระบบ](development/build-system-overview.md)
* [ขั้นตอนการสร้าง (OS X)](development/build-instructions-osx.md)
* [ขั้นตอนการสร้าง (Windows)](development/build-instructions-windows.md)
* [ขั้นตอนการสร้าง (Linux)](development/build-instructions-linux.md)
* [Setting Up Symbol Server in debugger](development/setting-up-symbol-server.md)
* [การติดตั้งเซิร์ฟเวอร์ Symbol Server ใน debugger](development/setting-up-symbol-server.md)

View file

@ -30,7 +30,7 @@ Electron 的用戶擁有在網頁中呼叫 Node.js APIs 的能力,允許低級
在網頁中,是不允許呼叫原生 GUI 相關 APIs 因為管理原生 GUI 資源在網頁上是非常危險而且容易造成資源洩露。
如果你想要在網頁中呼叫 GUI 相關的 APIs 的操作,網頁的渲染行程必須與主行程進行通訊,請求主行程進行相關的操作。
在 Electron ,我們提供用於在主行程與渲染行程之間通訊的 [ipc][1] 模組。並且也有一個遠端模使用 RPC 通訊方式 [remote][2]
在 Electron我們提供用於在主行程與渲染行程之間通訊的 [ipc](../api/ipc-renderer.md) 模組。並且也有一個遠端模組使用 RPC 通訊方式 [remote](../api/remote.md)
# 打造你第一個 Electron 應用
@ -43,7 +43,7 @@ your-app/
└── index.html
```
`package.json ` 的格式與 Node 的模組完全一樣,並且有個腳本被指定為 `main` 是用來啟動你的應用程式,它運行在主行程上。
`package.json` 的格式與 Node 的模組完全一樣,並且有個腳本被指定為 `main` 是用來啟動你的應用程式,它運行在主行程上。
你應用裡的 一個範例在你的 `package.json` 看起來可能像這樣:
```json
@ -88,7 +88,7 @@ app.on('ready', function() {
  mainWindow.loadUrl('file://' + __dirname + '/index.html');
  // 打開開發者工具
  mainWindow.openDevTools();
  mainWindow.webContents.openDevTools();
  // 當window 被關閉,這個事件會被觸發
  mainWindow.on('closed', function() {
@ -110,8 +110,9 @@ app.on('ready', function() {
  </head>
  <body>
    <h1>Hello World!</h1>
    We are using Node.js <script>document.write(process.version)</script>
    and Electron <script>document.write(process.versions['electron'])</script>.
We are using node <script>document.write(process.versions.node)</script>,
Chrome <script>document.write(process.versions.chrome)</script>,
and Electron <script>document.write(process.versions.electron)</script>.
  </body>
</html>
```
@ -160,6 +161,17 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/
# 作為版本發行
在你完成了你的應用程式後,你可以依照 [應用部署](https://github.com/atom/electron/blob/master/docs/tutorial/application-distribution.md) 指南發布一個版本,並且運行已經打包好的應用程式。
[1]: https://github.com/atom/electron/blob/master/docs/api/ipc-renderer.md
# 試試這個範例
[2]: https://github.com/atom/electron/blob/master/docs/api/remote.md
Clone 與執行本篇教學的程式碼,它們都放在 [`atom/electron-quick-start`](https://github.com/atom/electron-quick-start) 這個 repository。
**Note**: 執行這個範例需要 [Git](https://git-scm.com) 以及 [Node.js](https://nodejs.org/en/download/) (其中包括 [npm](https://npmjs.org)) 在你的作業系統。
```bash
# Clone the repository
$ git clone https://github.com/atom/electron-quick-start
# Go into the repository
$ cd electron-quick-start
# Install dependencies and run the app
$ npm install && npm start
```

View file

@ -133,18 +133,23 @@ Emitted when a new [browserWindow](browser-window.md) is created.
### Event: 'select-certificate'
Emitted when a client certificate is requested.
Returns:
* `event` Event
* `webContents` [WebContents](browser-window.md#class-webcontents)
* `url` String
* `webContents` [WebContents](web-contents.md)
* `url` URL
* `certificateList` [Objects]
* `data` PEM encoded data
* `issuerName` Issuer's Common Name
* `callback` Function
Emitted when a client certificate is requested.
The `url` corresponds to the navigation entry requesting the client certificate
and `callback` needs to be called with an entry filtered from the list. Using
`event.preventDefault()` prevents the application from using the first
certificate from the store.
```javascript
app.on('select-certificate', function(event, host, url, list, callback) {
event.preventDefault();
@ -152,10 +157,36 @@ app.on('select-certificate', function(event, host, url, list, callback) {
})
```
The `url` corresponds to the navigation entry requesting the client certificate
and `callback` needs to be called with an entry filtered from the list. Using
`event.preventDefault()` prevents the application from using the first
certificate from the store.
### Event: 'login'
Returns:
* `event` Event
* `webContents` [WebContents](web-contents.md)
* `request` Object
* `method` String
* `url` URL
* `referrer` URL
* `authInfo` Object
* `isProxy` Boolean
* `scheme` String
* `host` String
* `port` Integer
* `realm` String
* `callback` Function
Emitted when `webContents` wants to do basic auth.
The default behavior is to cancel all authentications, to override this you
should prevent the default behavior with `event.preventDefault()` and call
`callback(username, password)` with the credentials.
```javascript
app.on('login', function(event, webContents, request, authInfo, callback) {
event.preventDefault();
callback('username', 'secret');
})
```
### Event: 'gpu-process-crashed'
@ -245,7 +276,7 @@ Returns the current application locale.
Resolves the proxy information for `url`. The `callback` will be called with
`callback(proxy)` when the request is performed.
### `app.addRecentDocument(path)`
### `app.addRecentDocument(path)` _OS X_ _Windows_
* `path` String
@ -254,7 +285,7 @@ Adds `path` to the recent documents list.
This list is managed by the OS. On Windows you can visit the list from the task
bar, and on OS X you can visit it from dock menu.
### `app.clearRecentDocuments()`
### `app.clearRecentDocuments()` _OS X_ _Windows_
Clears the recent documents list.
@ -290,6 +321,60 @@ URLs that fall under "Local Intranet" sites (i.e. are in the same domain as you)
However, this detection often fails when corporate networks are badly configured,
so this lets you co-opt this behavior and enable it for all URLs.
### `app.makeSingleInstance(callback)`
* `callback` Function
This method makes your application a Single Instance Application - instead of
allowing multiple instances of your app to run, this will ensure that only a
single instance of your app is running, and other instances signal this
instance and exit.
`callback` will be called with `callback(argv, workingDirectory)` when a second
instance has been executed. `argv` is an Array of the second instance's command
line arguments, and `workingDirectory` is its current working directory. Usually
applications respond to this by making their primary window focused and
non-minimized.
The `callback` is guaranteed to be executed after the `ready` event of `app`
gets emitted.
This method returns `false` if your process is the primary instance of the
application and your app should continue loading. And returns `true` if your
process has sent its parameters to another instance, and you should immediately
quit.
On OS X the system enforces single instance automatically when users try to open
a second instance of your app in Finder, and the `open-file` and `open-url`
events will be emitted for that. However when users start your app in command
line the system's single instance machanism will be bypassed and you have to
use this method to ensure single instance.
An example of activating the window of primary instance when a second instance
starts:
```js
var myWindow = null;
var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
// Someone tried to run a second instance, we should focus our window
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore();
myWindow.focus();
}
return true;
});
if (shouldQuit) {
app.quit();
return;
}
// Create myWindow, load the rest of the app, etc...
app.on('ready', function() {
});
```
### `app.commandLine.appendSwitch(switch[, value])`
Append a switch (with optional `value`) to Chromium's command line.

View file

@ -1,106 +1,31 @@
# autoUpdater
**This module has only been implemented for OS X.**
This module provides an interface for the `Squirrel` auto-updater framework.
Check out [atom/grunt-electron-installer](https://github.com/atom/grunt-electron-installer)
to build a Windows installer for your app.
## Platform notices
The `auto-updater` module is a simple wrapper around the
[Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac) framework.
Though `autoUpdater` provides an uniform API for different platforms, there are
still some subtle differences on each platform.
Squirrel.Mac requires that your `.app` folder is signed using the
[codesign](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/codesign.1.html)
utility for updates to be installed.
### OS X
## Squirrel
On OS X the `autoUpdater` module is built upon [Squirrel.Mac][squirrel-mac], you
don't need any special setup to make it work. For server-side requirements, you
can read [Server Support][server-support].
Squirrel is an OS X framework focused on making application updates **as safe
and transparent as updates to a website**.
### Windows
Instead of publishing a feed of versions from which your app must select,
Squirrel updates to the version your server tells it to. This allows you to
intelligently update your clients based on the request you give to Squirrel.
On Windows you have to install your app into user's machine before you can use
the auto-updater, it is recommended to use [grunt-electron-installer][installer]
module to generate a Windows installer.
Your request can include authentication details, custom headers or a request
body so that your server has the context it needs in order to supply the most
suitable update.
The server-side setup is also different from OS X, you can read the documents of
[Squirrel.Windows][squirrel-windows] to get more details.
The update JSON Squirrel requests should be dynamically generated based on
criteria in the request and whether an update is required. Squirrel relies
on server-side support to determine whether an update is required. See
[Server Support](#server-support).
### Linux
Squirrel's installer is designed to be fault tolerant and ensures that any
updates installed are valid.
## Update Requests
Squirrel is indifferent to the request the client application provides for
update checking. `Accept: application/json` is added to the request headers
because Squirrel is responsible for parsing the response.
For the requirements imposed on the responses and the body format of an update
response, see [Server Support](#server-support).
Your update request must *at least* include a version identifier so that the
server can determine whether an update for this specific version is required. It
may also include other identifying criteria, such as operating system version or
username, to allow the server to deliver as fine grained an update as you
would like.
How you include the version identifier or other criteria is specific to the
server that you are requesting updates from. A common approach is to use query
parameters, like this:
```javascript
// In the main process
var app = require('app');
var autoUpdater = require('auto-updater');
autoUpdater.setFeedUrl('http://mycompany.com/myapp/latest?version=' + app.getVersion());
```
## Server Support
Your server should determine whether an update is required based on the
[Update Request](#update-requests) your client issues.
If an update is required, your server should respond with a status code of
[200 OK](http://tools.ietf.org/html/rfc2616#section-10.2.1) and include the
[update JSON](#update-json-format) in the body. Squirrel **will** download and
install this update, even if the version of the update is the same as the
currently running version. To save redundantly downloading the same version
multiple times your server must not inform the client to update.
If no update is required your server must respond with a status code of
[204 No Content](http://tools.ietf.org/html/rfc2616#section-10.2.5). Squirrel
will check for an update again at the interval you specify.
## Update JSON Format
When an update is available, Squirrel expects the following schema in response
to the update request provided:
```json
{
"url": "http://mycompany.com/myapp/releases/myrelease",
"name": "My Release Name",
"notes": "Theses are some release notes innit",
"pub_date": "2013-09-18T12:29:53+01:00"
}
```
The only required key is "url"; the others are optional.
Squirrel will request "url" with `Accept: application/zip` and only supports
installing ZIP updates. If future update formats are supported their MIME type
will be added to the `Accept` header so that your server can return the
appropriate format.
`pub_date` (if present) must be formatted according to ISO 8601.
## Update server implementations
[Nuts](https://github.com/GitbookIO/nuts) is an open source implementation of the update server described above, it integrates beautifully with GitHub releases. Nuts manages downloads and updates, its compatible with `Squirrel.Mac` and `Squirrel.Windows` so you get cross-platform support out of the box.
There is not built-in support for auto-updater on Linux, it is recommended to
use the distribution's package manager to update your app.
## Events
@ -110,8 +35,7 @@ The `autoUpdater` object emits the following events:
Returns:
* `event` Event
* `message` String
* `error` Error
Emitted when there is an error while updating.
@ -137,10 +61,10 @@ Returns:
* `releaseName` String
* `releaseDate` Date
* `updateUrl` String
* `quitAndUpdate` Function
Emitted when an update has been downloaded. Calling `quitAndUpdate()` will
restart the application and install the update.
Emitted when an update has been downloaded.
On Windows only `releaseName` is available.
## Methods
@ -150,10 +74,20 @@ The `autoUpdater` object has the following methods:
* `url` String
Set the `url` and initialize the auto updater. The `url` cannot be changed
Sets the `url` and initialize the auto updater. The `url` cannot be changed
once it is set.
### `autoUpdater.checkForUpdates()`
Ask the server whether there is an update. You must call `setFeedUrl` before
Asks the server whether there is an update. You must call `setFeedUrl` before
using this API.
### `autoUpdater.quitAndUpdate()`
Restarts the app and install the update after it has been downloaded. It should
only be called after `update-downloaded` has been emitted.
[squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac
[server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support
[squirrel-windows]: https://github.com/Squirrel/Squirrel.Windows
[installer]: https://github.com/atom/grunt-electron-installer

View file

@ -61,6 +61,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
key is pressed.
* `enable-larger-than-screen` Boolean - Enable the window to be resized larger
than screen.
* `background-color` String - Window's background color as Hexadecimal value,
like `#66CD00` or `#FFF`. This is only implemented on Linux and Windows.
* `dark-theme` Boolean - Forces using dark theme for the window, only works on
some GTK+3 desktop environments.
* `transparent` Boolean - Makes the window [transparent](frameless-window.md).
@ -550,6 +552,30 @@ Enters or leaves the kiosk mode.
Returns whether the window is in kiosk mode.
### `win.hookWindowMessage(message, callback)` _WINDOWS_
* `message` Integer
* `callback` Function
Hooks a windows message. The `callback` is called when
the message is received in the WndProc.
### `win.isWindowMessageHooked(message)` _WINDOWS_
* `message` Integer
Returns `true` or `false` depending on whether the message is hooked.
### `win.unhookWindowMessage(message)` _WINDOWS_
* `message` Integer
Unhook the window message.
### `win.unhookAllWindowMessages()` _WINDOWS_
Unhooks all of the window messages.
### `win.setRepresentedFilename(filename)` _OS X_
* `filename` String

View file

@ -59,11 +59,10 @@ ID.
The crash reporter will send the following data to the `submitUrl` as `POST`:
* `rept` String - e.g. 'electron-crash-service'.
* `ver` String - The version of Electron.
* `platform` String - e.g. 'win32'.
* `process_type` String - e.g. 'renderer'.
* `ptime` Number
* `guid` String - e.g. '5e1286fc-da97-479e-918b-6bfb0c3d1c72'
* `_version` String - The version in `package.json`.
* `_productName` String - The product name in the `crashReporter` `options`
object.

View file

@ -3,12 +3,7 @@
The `remote` module provides a simple way to do inter-process communication
(IPC) between the renderer process (web page) and the main process.
In Electron, only GUI-unrelated modules are available in the renderer process.
Without the `remote` module, users who want to call a main process API in
the renderer process will have to explicitly send inter-process messages
to the main process. With the `remote` module, you can invoke methods of the
main process object without explicitly sending inter-process messages, similar
to Java's [RMI](http://en.wikipedia.org/wiki/Java_remote_method_invocation).
In Electron, GUI-related modules (such as `dialog`, `menu` etc.) are only available in the main process, not in the renderer process. In order to use them from the renderer process, the `ipc` module is necessary to send inter-process messages to the main process. With the `remote` module, you can invoke methods of the main process object without explicitly sending inter-process messages, similar to Java's [RMI](http://en.wikipedia.org/wiki/Java_remote_method_invocation).
An example of creating a browser window from a renderer process:

View file

@ -154,7 +154,9 @@ Clears the data of web storages.
* `config` String
* `callback` Function - Called when operation is done.
Parses the `config` indicating which proxies to use for the session.
If `config` is a PAC url, it is used directly otherwise
`config` is parsed based on the following rules indicating which
proxies to use for the session.
```
config = scheme-proxies[";"<scheme-proxies>]

View file

@ -36,6 +36,7 @@ Returns:
This event is like `did-finish-load` but emitted when the load failed or was
cancelled, e.g. `window.stop()` is invoked.
The full list of error codes and their meaning is available [here](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
### Event: 'did-frame-finish-load'
@ -166,6 +167,27 @@ Emitted when DevTools is closed.
Emitted when DevTools is focused / opened.
### Event: 'login'
Returns:
* `event` Event
* `request` Object
* `method` String
* `url` URL
* `referrer` URL
* `authInfo` Object
* `isProxy` Boolean
* `scheme` String
* `host` String
* `port` Integer
* `realm` String
* `callback` Function
Emitted when `webContents` wants to do basic auth.
The usage is the same with [the `login` event of `app`](app.md#event-login).
## Instance Methods
The `webContents` object has the following instance methods:
@ -182,6 +204,7 @@ See [session documentation](session.md) for this object's methods.
* `options` Object (optional), properties:
* `httpReferrer` String - A HTTP Referrer url.
* `userAgent` String - A user agent originating the request.
* `extraHeaders` String - Extra headers separated by "\n"
Loads the `url` in the window, the `url` must contain the protocol prefix,
e.g. the `http://` or `file://`.

View file

@ -1,26 +1,26 @@
# Mac App Store Submission Guide
Since v0.34.0, Electron allows submitting packaged apps to the Mac App Store
(MAS). This guide provides information on: how to submit your app and the
Since v0.34.0, Electron allows submitting packaged apps to the Mac App Store
(MAS). This guide provides information on: how to submit your app and the
limitations of the MAS build.
## How to Submit Your App
The following steps introduce a simple way to submit your app to Mac App Store.
However, these steps do not ensure sure your app will be approved by Apple; you
still need to read Apple's [Submitting Your App][submitting-your-app] guide on
The following steps introduce a simple way to submit your app to Mac App Store.
However, these steps do not ensure sure your app will be approved by Apple; you
still need to read Apple's [Submitting Your App][submitting-your-app] guide on
how to meet the Mac App Store requirements.
### Get Certificate
To submit your app to the Mac App Store, you first must get a certificate from
To submit your app to the Mac App Store, you first must get a certificate from
Apple. You can follow these [existing guides][nwjs-guide] on web.
### Sign Your App
After getting the certificate from Apple, you can package your app by following
[Application Distribution](application-distribution.md), and then proceed to
signing your app. This step is basically the same with other programs, but the
[Application Distribution](application-distribution.md), and then proceed to
signing your app. This step is basically the same with other programs, but the
key is to sign every dependency of Electron one by one.
First, you need to prepare two entitlements files.
@ -79,9 +79,9 @@ codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP
codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH"
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$APP_PATH"
```
a
If you are new to app sandboxing under OS X, you should also read through
Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then
Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then
add keys for the permissions needed by your app to the entitlements files.
### Upload Your App and Submit for Review
@ -92,7 +92,7 @@ before uploading. Then you can [submit your app for review][submit-for-review].
## Limitations of MAS Build
In order to satisfy all requirements for app sandboxing, the following modules
In order to satisfy all requirements for app sandboxing, the following modules
have been disabled in the MAS build:
* `crash-reporter`

View file

@ -31,8 +31,8 @@ which handles the manual steps of downloading headers and building native module
```sh
npm install --save-dev electron-rebuild
# Every time you run npm install, run this
node ./node_modules/.bin/electron-rebuild
# Every time you run "npm install", run this
./node_modules/.bin/electron-rebuild
```
### The npm Way

View file

@ -11,6 +11,9 @@
'atom/browser/api/lib/app.coffee',
'atom/browser/api/lib/atom-delegate.coffee',
'atom/browser/api/lib/auto-updater.coffee',
'atom/browser/api/lib/auto-updater/auto-updater-mac.coffee',
'atom/browser/api/lib/auto-updater/auto-updater-win.coffee',
'atom/browser/api/lib/auto-updater/squirrel-update-win.coffee',
'atom/browser/api/lib/browser-window.coffee',
'atom/browser/api/lib/content-tracing.coffee',
'atom/browser/api/lib/dialog.coffee',
@ -112,10 +115,7 @@
'atom/browser/api/save_page_handler.h',
'atom/browser/auto_updater.cc',
'atom/browser/auto_updater.h',
'atom/browser/auto_updater_delegate.h',
'atom/browser/auto_updater_linux.cc',
'atom/browser/auto_updater_mac.mm',
'atom/browser/auto_updater_win.cc',
'atom/browser/atom_access_token_store.cc',
'atom/browser/atom_access_token_store.h',
'atom/browser/atom_browser_client.cc',
@ -150,6 +150,8 @@
'atom/browser/common_web_contents_delegate.h',
'atom/browser/javascript_environment.cc',
'atom/browser/javascript_environment.h',
'atom/browser/login_handler.cc',
'atom/browser/login_handler.h',
'atom/browser/mac/atom_application.h',
'atom/browser/mac/atom_application.mm',
'atom/browser/mac/atom_application_delegate.h',
@ -302,7 +304,10 @@
'atom/common/native_mate_converters/accelerator_converter.h',
'atom/common/native_mate_converters/blink_converter.cc',
'atom/common/native_mate_converters/blink_converter.h',
'atom/common/native_mate_converters/callback.cc',
'atom/common/native_mate_converters/callback.h',
'atom/common/native_mate_converters/content_converter.cc',
'atom/common/native_mate_converters/content_converter.h',
'atom/common/native_mate_converters/file_path_converter.h',
'atom/common/native_mate_converters/gfx_converter.cc',
'atom/common/native_mate_converters/gfx_converter.h',
@ -346,6 +351,8 @@
'atom/utility/atom_content_utility_client.h',
'chromium_src/chrome/browser/browser_process.cc',
'chromium_src/chrome/browser/browser_process.h',
'chromium_src/chrome/browser/chrome_process_finder_win.cc',
'chromium_src/chrome/browser/chrome_process_finder_win.h',
'chromium_src/chrome/browser/chrome_notification_types.h',
'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc',
'chromium_src/chrome/browser/extensions/global_shortcut_listener.h',
@ -374,6 +381,9 @@
'chromium_src/chrome/browser/printing/printing_message_filter.h',
'chromium_src/chrome/browser/printing/print_preview_message_handler.cc',
'chromium_src/chrome/browser/printing/print_preview_message_handler.h',
'chromium_src/chrome/browser/process_singleton_posix.cc',
'chromium_src/chrome/browser/process_singleton_win.cc',
'chromium_src/chrome/browser/process_singleton.h',
'chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc',
'chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h',
'chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc',

View file

@ -176,7 +176,8 @@ def create_dist_zip():
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
with scoped_cwd(DIST_DIR):
files = TARGET_BINARIES[PLATFORM] + ['LICENSE', 'version']
files = TARGET_BINARIES[PLATFORM] + ['LICENSE', 'LICENSES.chromium.html',
'version']
if PLATFORM == 'linux':
files += [lib for lib in SYSTEM_LIBRARIES if os.path.exists(lib)]
dirs = TARGET_DIRECTORIES[PLATFORM]
@ -189,7 +190,7 @@ def create_chrome_binary_zip(binary, version):
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
with scoped_cwd(DIST_DIR):
files = ['LICENSE']
files = ['LICENSE', 'LICENSES.chromium.html']
if PLATFORM == 'win32':
files += [binary + '.exe']
else:
@ -205,7 +206,7 @@ def create_symbols_zip():
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
with scoped_cwd(DIST_DIR):
files = ['LICENSE', 'version']
files = ['LICENSE', 'LICENSES.chromium.html', 'version']
dirs = ['{0}.breakpad.syms'.format(PROJECT_NAME)]
make_zip(zip_file, files, dirs)

View file

@ -8,7 +8,7 @@ import sys
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
'http://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
LIBCHROMIUMCONTENT_COMMIT = '78e54bc39a04b758ed5167cd980cc4d9951bd629'
LIBCHROMIUMCONTENT_COMMIT = '464aff2398f619b1d4d91b9187de69803919dca2'
PLATFORM = {
'cygwin': 'win32',

View file

@ -23,9 +23,12 @@ describe 'protocol module', ->
it 'does not crash when handler is called twice', (done) ->
doubleHandler = (request, callback) ->
callback(text)
callback()
try
callback(text)
callback()
catch
protocol.registerStringProtocol protocolName, doubleHandler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -36,6 +39,7 @@ describe 'protocol module', ->
it 'sends error when callback is called with nothing', (done) ->
protocol.registerBufferProtocol protocolName, emptyHandler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -48,6 +52,7 @@ describe 'protocol module', ->
handler = (request, callback) ->
setImmediate -> callback(text)
protocol.registerStringProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -66,6 +71,7 @@ describe 'protocol module', ->
it 'sends string as response', (done) ->
handler = (request, callback) -> callback(text)
protocol.registerStringProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -77,6 +83,7 @@ describe 'protocol module', ->
it 'sends object as response', (done) ->
handler = (request, callback) -> callback(data: text, mimeType: 'text/html')
protocol.registerStringProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data, statux, request) ->
@ -88,6 +95,7 @@ describe 'protocol module', ->
it 'fails when sending object other than string', (done) ->
handler = (request, callback) -> callback(new Date)
protocol.registerBufferProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -102,6 +110,7 @@ describe 'protocol module', ->
it 'sends Buffer as response', (done) ->
handler = (request, callback) -> callback(buffer)
protocol.registerBufferProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -113,6 +122,7 @@ describe 'protocol module', ->
it 'sends object as response', (done) ->
handler = (request, callback) -> callback(data: buffer, mimeType: 'text/html')
protocol.registerBufferProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data, statux, request) ->
@ -124,6 +134,7 @@ describe 'protocol module', ->
it 'fails when sending string', (done) ->
handler = (request, callback) -> callback(text)
protocol.registerBufferProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -142,6 +153,7 @@ describe 'protocol module', ->
it 'sends file path as response', (done) ->
handler = (request, callback) -> callback(filePath)
protocol.registerFileProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -153,6 +165,7 @@ describe 'protocol module', ->
it 'sends object as response', (done) ->
handler = (request, callback) -> callback(path: filePath)
protocol.registerFileProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data, statux, request) ->
@ -164,6 +177,7 @@ describe 'protocol module', ->
it 'can send normal file', (done) ->
handler = (request, callback) -> callback(normalPath)
protocol.registerFileProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -176,6 +190,7 @@ describe 'protocol module', ->
fakeFilePath = path.join __dirname, 'fixtures', 'asar', 'a.asar', 'not-exist'
handler = (request, callback) -> callback(fakeFilePath)
protocol.registerBufferProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -187,6 +202,7 @@ describe 'protocol module', ->
it 'fails when sending unsupported content', (done) ->
handler = (request, callback) -> callback(new Date)
protocol.registerBufferProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -206,6 +222,7 @@ describe 'protocol module', ->
url = "http://127.0.0.1:#{port}"
handler = (request, callback) -> callback({url})
protocol.registerHttpProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -217,6 +234,7 @@ describe 'protocol module', ->
it 'fails when sending invalid url', (done) ->
handler = (request, callback) -> callback({url: 'url'})
protocol.registerHttpProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -228,6 +246,7 @@ describe 'protocol module', ->
it 'fails when sending unsupported content', (done) ->
handler = (request, callback) -> callback(new Date)
protocol.registerHttpProtocol protocolName, handler, (error) ->
return done(error) if error
$.ajax
url: "#{protocolName}://fake-host"
success: (data) ->
@ -285,9 +304,12 @@ describe 'protocol module', ->
it 'does not crash when handler is called twice', (done) ->
doubleHandler = (request, callback) ->
callback(text)
callback()
try
callback(text)
callback()
catch
protocol.interceptStringProtocol 'http', doubleHandler, (error) ->
return done(error) if error
$.ajax
url: 'http://fake-host'
success: (data) ->
@ -298,6 +320,7 @@ describe 'protocol module', ->
it 'sends error when callback is called with nothing', (done) ->
protocol.interceptBufferProtocol 'http', emptyHandler, (error) ->
return done(error) if error
$.ajax
url: 'http://fake-host'
success: (data) ->
@ -310,6 +333,7 @@ describe 'protocol module', ->
it 'can intercept http protocol', (done) ->
handler = (request, callback) -> callback(text)
protocol.interceptStringProtocol 'http', handler, (error) ->
return done(error) if error
$.ajax
url: 'http://fake-host'
success: (data) ->
@ -322,6 +346,7 @@ describe 'protocol module', ->
handler = (request, callback) ->
callback({mimeType: 'application/json', data: '{"value": 1}'})
protocol.interceptStringProtocol 'http', handler, (error) ->
return done(error) if error
$.ajax
url: 'http://fake-host'
success: (data) ->
@ -335,6 +360,7 @@ describe 'protocol module', ->
it 'can intercept http protocol', (done) ->
handler = (request, callback) -> callback(new Buffer(text))
protocol.interceptBufferProtocol 'http', handler, (error) ->
return done(error) if error
$.ajax
url: 'http://fake-host'
success: (data) ->