Merge pull request #3175 from atom/improve-make-single-instance
Fix some problems of makeSingleInstance
This commit is contained in:
commit
e3ce1b50ec
17 changed files with 174 additions and 253 deletions
|
@ -19,7 +19,6 @@
|
||||||
#include "atom/browser/api/atom_api_web_contents.h"
|
#include "atom/browser/api/atom_api_web_contents.h"
|
||||||
#include "atom/common/native_mate_converters/callback.h"
|
#include "atom/common/native_mate_converters/callback.h"
|
||||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||||
#include "atom/common/native_mate_converters/command_line_converter.h"
|
|
||||||
#include "atom/common/node_includes.h"
|
#include "atom/common/node_includes.h"
|
||||||
#include "atom/common/options_switches.h"
|
#include "atom/common/options_switches.h"
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
|
@ -112,6 +111,23 @@ int GetPathConstant(const std::string& name) {
|
||||||
return -1;
|
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(
|
void OnClientCertificateSelected(
|
||||||
v8::Isolate* isolate,
|
v8::Isolate* isolate,
|
||||||
std::shared_ptr<content::ClientCertificateDelegate> delegate,
|
std::shared_ptr<content::ClientCertificateDelegate> delegate,
|
||||||
|
@ -163,10 +179,7 @@ void App::OnQuit() {
|
||||||
Emit("quit");
|
Emit("quit");
|
||||||
|
|
||||||
if (process_singleton_.get()) {
|
if (process_singleton_.get()) {
|
||||||
if (process_notify_result_ == ProcessSingleton::PROCESS_NONE) {
|
|
||||||
process_singleton_->Cleanup();
|
process_singleton_->Cleanup();
|
||||||
}
|
|
||||||
|
|
||||||
process_singleton_.reset();
|
process_singleton_.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,10 +209,6 @@ void App::OnFinishLaunching() {
|
||||||
auto handle = Session::CreateFrom(isolate(), browser_context);
|
auto handle = Session::CreateFrom(isolate(), browser_context);
|
||||||
default_session_.Reset(isolate(), handle.ToV8());
|
default_session_.Reset(isolate(), handle.ToV8());
|
||||||
|
|
||||||
if (process_singleton_.get()) {
|
|
||||||
process_singleton_startup_lock_->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
Emit("ready");
|
Emit("ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,35 +290,24 @@ v8::Local<v8::Value> App::DefaultSession(v8::Isolate* isolate) {
|
||||||
return v8::Local<v8::Value>::New(isolate, default_session_);
|
return v8::Local<v8::Value>::New(isolate, default_session_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool App::MakeSingleInstance(ProcessSingleton::NotificationCallback callback) {
|
bool App::MakeSingleInstance(
|
||||||
base::FilePath userDir;
|
const ProcessSingleton::NotificationCallback& callback) {
|
||||||
PathService::Get(brightray::DIR_USER_DATA, &userDir);
|
if (process_singleton_.get())
|
||||||
|
|
||||||
if (!process_singleton_.get()) {
|
|
||||||
auto browser = Browser::Get();
|
|
||||||
process_singleton_startup_lock_.reset(
|
|
||||||
new ProcessSingletonStartupLock(callback));
|
|
||||||
|
|
||||||
process_singleton_.reset(
|
|
||||||
new ProcessSingleton(
|
|
||||||
userDir,
|
|
||||||
process_singleton_startup_lock_->AsNotificationCallback()));
|
|
||||||
|
|
||||||
if (browser->is_ready()) {
|
|
||||||
process_singleton_startup_lock_->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
process_notify_result_ = process_singleton_->NotifyOtherProcessOrCreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (process_notify_result_) {
|
|
||||||
case ProcessSingleton::NotifyResult::PROCESS_NONE:
|
|
||||||
return false;
|
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::LOCK_ERROR:
|
||||||
case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
|
case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
|
||||||
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
|
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
|
||||||
|
process_singleton_.reset();
|
||||||
return true;
|
return true;
|
||||||
default:
|
case ProcessSingleton::NotifyResult::PROCESS_NONE:
|
||||||
|
default: // Shouldn't be needed, but VS warns if it is not there.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "atom/browser/browser_observer.h"
|
#include "atom/browser/browser_observer.h"
|
||||||
#include "atom/common/native_mate_converters/callback.h"
|
#include "atom/common/native_mate_converters/callback.h"
|
||||||
#include "chrome/browser/process_singleton.h"
|
#include "chrome/browser/process_singleton.h"
|
||||||
#include "chrome/browser/process_singleton_startup_lock.h"
|
|
||||||
#include "content/public/browser/gpu_data_manager_observer.h"
|
#include "content/public/browser/gpu_data_manager_observer.h"
|
||||||
#include "native_mate/handle.h"
|
#include "native_mate/handle.h"
|
||||||
|
|
||||||
|
@ -68,19 +67,15 @@ class App : public mate::EventEmitter,
|
||||||
|
|
||||||
void SetDesktopName(const std::string& desktop_name);
|
void SetDesktopName(const std::string& desktop_name);
|
||||||
void SetAppUserModelId(const std::string& app_id);
|
void SetAppUserModelId(const std::string& app_id);
|
||||||
|
|
||||||
void AllowNTLMCredentialsForAllDomains(bool should_allow);
|
void AllowNTLMCredentialsForAllDomains(bool should_allow);
|
||||||
|
bool MakeSingleInstance(
|
||||||
bool MakeSingleInstance(ProcessSingleton::NotificationCallback callback);
|
const ProcessSingleton::NotificationCallback& callback);
|
||||||
|
|
||||||
std::string GetLocale();
|
std::string GetLocale();
|
||||||
v8::Local<v8::Value> DefaultSession(v8::Isolate* isolate);
|
v8::Local<v8::Value> DefaultSession(v8::Isolate* isolate);
|
||||||
|
|
||||||
v8::Global<v8::Value> default_session_;
|
v8::Global<v8::Value> default_session_;
|
||||||
|
|
||||||
scoped_ptr<ProcessSingleton> process_singleton_;
|
scoped_ptr<ProcessSingleton> process_singleton_;
|
||||||
scoped_ptr<ProcessSingletonStartupLock> process_singleton_startup_lock_;
|
|
||||||
ProcessSingleton::NotifyResult process_notify_result_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(App);
|
DISALLOW_COPY_AND_ASSIGN(App);
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,7 +62,6 @@ void AtomBrowserMainParts::PreEarlyInitialization() {
|
||||||
void AtomBrowserMainParts::PostEarlyInitialization() {
|
void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||||
brightray::BrowserMainParts::PostEarlyInitialization();
|
brightray::BrowserMainParts::PostEarlyInitialization();
|
||||||
|
|
||||||
{
|
|
||||||
// Temporary set the bridge_task_runner_ as current thread's 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
|
// 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.
|
// of getting current message loop's task runner, which is null for now.
|
||||||
|
@ -72,7 +71,6 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||||
// The ProxyResolverV8 has setup a complete V8 environment, in order to
|
// The ProxyResolverV8 has setup a complete V8 environment, in order to
|
||||||
// avoid conflicts we only initialize our V8 environment after that.
|
// avoid conflicts we only initialize our V8 environment after that.
|
||||||
js_env_.reset(new JavascriptEnvironment);
|
js_env_.reset(new JavascriptEnvironment);
|
||||||
}
|
|
||||||
|
|
||||||
node_bindings_->Initialize();
|
node_bindings_->Initialize();
|
||||||
|
|
||||||
|
@ -107,6 +105,7 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
||||||
1000));
|
1000));
|
||||||
|
|
||||||
brightray::BrowserMainParts::PreMainMessageLoopRun();
|
brightray::BrowserMainParts::PreMainMessageLoopRun();
|
||||||
|
BridgeTaskRunner::MessageLoopIsReady();
|
||||||
|
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
|
libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
|
||||||
|
|
|
@ -8,13 +8,33 @@
|
||||||
|
|
||||||
namespace atom {
|
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(
|
bool BridgeTaskRunner::PostDelayedTask(
|
||||||
const tracked_objects::Location& from_here,
|
const tracked_objects::Location& from_here,
|
||||||
const base::Closure& task,
|
const base::Closure& task,
|
||||||
base::TimeDelta delay) {
|
base::TimeDelta delay) {
|
||||||
auto message_loop = base::MessageLoop::current();
|
auto message_loop = base::MessageLoop::current();
|
||||||
if (!message_loop)
|
if (!message_loop) {
|
||||||
return false;
|
tasks_.push_back(base::MakeTuple(from_here, task, delay));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return message_loop->task_runner()->PostDelayedTask(from_here, task, delay);
|
return message_loop->task_runner()->PostDelayedTask(from_here, task, delay);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +42,7 @@ bool BridgeTaskRunner::PostDelayedTask(
|
||||||
bool BridgeTaskRunner::RunsTasksOnCurrentThread() const {
|
bool BridgeTaskRunner::RunsTasksOnCurrentThread() const {
|
||||||
auto message_loop = base::MessageLoop::current();
|
auto message_loop = base::MessageLoop::current();
|
||||||
if (!message_loop)
|
if (!message_loop)
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
return message_loop->task_runner()->RunsTasksOnCurrentThread();
|
return message_loop->task_runner()->RunsTasksOnCurrentThread();
|
||||||
}
|
}
|
||||||
|
@ -32,8 +52,10 @@ bool BridgeTaskRunner::PostNonNestableDelayedTask(
|
||||||
const base::Closure& task,
|
const base::Closure& task,
|
||||||
base::TimeDelta delay) {
|
base::TimeDelta delay) {
|
||||||
auto message_loop = base::MessageLoop::current();
|
auto message_loop = base::MessageLoop::current();
|
||||||
if (!message_loop)
|
if (!message_loop) {
|
||||||
return false;
|
non_nestable_tasks_.push_back(base::MakeTuple(from_here, task, delay));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return message_loop->task_runner()->PostNonNestableDelayedTask(
|
return message_loop->task_runner()->PostNonNestableDelayedTask(
|
||||||
from_here, task, delay);
|
from_here, task, delay);
|
||||||
|
|
|
@ -5,17 +5,23 @@
|
||||||
#ifndef ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
|
#ifndef ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
|
||||||
#define ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
|
#define ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "base/single_thread_task_runner.h"
|
#include "base/single_thread_task_runner.h"
|
||||||
|
#include "base/tuple.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
// Post all tasks to the current message loop's task runner if available,
|
// 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 {
|
class BridgeTaskRunner : public base::SingleThreadTaskRunner {
|
||||||
public:
|
public:
|
||||||
BridgeTaskRunner() {}
|
BridgeTaskRunner() {}
|
||||||
~BridgeTaskRunner() override {}
|
~BridgeTaskRunner() override {}
|
||||||
|
|
||||||
|
// Called when message loop is ready.
|
||||||
|
static void MessageLoopIsReady();
|
||||||
|
|
||||||
// base::SingleThreadTaskRunner:
|
// base::SingleThreadTaskRunner:
|
||||||
bool PostDelayedTask(const tracked_objects::Location& from_here,
|
bool PostDelayedTask(const tracked_objects::Location& from_here,
|
||||||
const base::Closure& task,
|
const base::Closure& task,
|
||||||
|
@ -27,6 +33,11 @@ class BridgeTaskRunner : public base::SingleThreadTaskRunner {
|
||||||
base::TimeDelta delay) override;
|
base::TimeDelta delay) override;
|
||||||
|
|
||||||
private:
|
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);
|
DISALLOW_COPY_AND_ASSIGN(BridgeTaskRunner);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,14 @@ void Browser::Shutdown() {
|
||||||
is_quiting_ = true;
|
is_quiting_ = true;
|
||||||
|
|
||||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
||||||
|
|
||||||
|
if (base::MessageLoop::current()) {
|
||||||
base::MessageLoop::current()->PostTask(
|
base::MessageLoop::current()->PostTask(
|
||||||
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
|
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 {
|
std::string Browser::GetVersion() const {
|
||||||
|
|
|
@ -130,6 +130,7 @@ class Browser : public WindowListObserver {
|
||||||
observers_.RemoveObserver(obs);
|
observers_.RemoveObserver(obs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_shutting_down() const { return is_shutdown_; }
|
||||||
bool is_quiting() const { return is_quiting_; }
|
bool is_quiting() const { return is_quiting_; }
|
||||||
bool is_ready() const { return is_ready_; }
|
bool is_ready() const { return is_ready_; }
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ void FatalErrorCallback(const char* location, const char* message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Log(const base::string16& message) {
|
void Log(const base::string16& message) {
|
||||||
std::cout << message;
|
std::cout << message << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Copyright (c) 2014 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_COMMAND_LINE_CONVERTER_H_
|
|
||||||
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_COMMAND_LINE_CONVERTER_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
|
||||||
#include "base/command_line.h"
|
|
||||||
|
|
||||||
namespace mate {
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct Converter<base::CommandLine> {
|
|
||||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
|
||||||
const base::CommandLine& val) {
|
|
||||||
return Converter<base::CommandLine::StringType>::ToV8(
|
|
||||||
isolate, val.GetCommandLineString());
|
|
||||||
}
|
|
||||||
static bool FromV8(v8::Isolate* isolate,
|
|
||||||
v8::Local<v8::Value> val,
|
|
||||||
base::CommandLine* out) {
|
|
||||||
base::FilePath::StringType path;
|
|
||||||
|
|
||||||
if (Converter<base::FilePath::StringType>::FromV8(isolate, val, &path)) {
|
|
||||||
*out = base::CommandLine(base::FilePath(path));
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace mate
|
|
||||||
|
|
||||||
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_COMMAND_LINE_CONVERTER_H_
|
|
|
@ -42,8 +42,6 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
|
||||||
if (!thread_id || !process_id)
|
if (!thread_id || !process_id)
|
||||||
return NOTIFY_FAILED;
|
return NOTIFY_FAILED;
|
||||||
|
|
||||||
base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
|
|
||||||
|
|
||||||
// Send the command line to the remote chrome window.
|
// Send the command line to the remote chrome window.
|
||||||
// Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
// Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||||
|
@ -52,7 +50,7 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
|
||||||
return NOTIFY_FAILED;
|
return NOTIFY_FAILED;
|
||||||
to_send.append(cur_dir.value());
|
to_send.append(cur_dir.value());
|
||||||
to_send.append(L"\0", 1); // Null separator.
|
to_send.append(L"\0", 1); // Null separator.
|
||||||
to_send.append(command_line.GetCommandLineString());
|
to_send.append(::GetCommandLineW());
|
||||||
to_send.append(L"\0", 1); // Null separator.
|
to_send.append(L"\0", 1); // Null separator.
|
||||||
|
|
||||||
// Allow the current running browser window to make itself the foreground
|
// Allow the current running browser window to make itself the foreground
|
||||||
|
|
|
@ -60,7 +60,7 @@ class ProcessSingleton : public base::NonThreadSafe {
|
||||||
// handled within the current browser instance or false if the remote process
|
// handled within the current browser instance or false if the remote process
|
||||||
// should handle it (i.e., because the current process is shutting down).
|
// should handle it (i.e., because the current process is shutting down).
|
||||||
using NotificationCallback =
|
using NotificationCallback =
|
||||||
base::Callback<bool(const base::CommandLine& command_line,
|
base::Callback<bool(const base::CommandLine::StringVector& command_line,
|
||||||
const base::FilePath& current_directory)>;
|
const base::FilePath& current_directory)>;
|
||||||
|
|
||||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||||
|
@ -133,6 +133,9 @@ class ProcessSingleton : public base::NonThreadSafe {
|
||||||
base::FilePath user_data_dir_;
|
base::FilePath user_data_dir_;
|
||||||
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
||||||
#elif defined(OS_POSIX) && !defined(OS_ANDROID)
|
#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.
|
// 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
|
// Assumes that the current pid is the root of all pids of the current
|
||||||
// instance.
|
// instance.
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "atom/common/atom_command_line.h"
|
||||||
#include "base/base_paths.h"
|
#include "base/base_paths.h"
|
||||||
#include "base/basictypes.h"
|
#include "base/basictypes.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
|
@ -316,8 +317,7 @@ bool IsChromeProcess(pid_t pid) {
|
||||||
PathService::Get(base::FILE_EXE, &exec_path);
|
PathService::Get(base::FILE_EXE, &exec_path);
|
||||||
|
|
||||||
return (!other_chrome_path.empty() &&
|
return (!other_chrome_path.empty() &&
|
||||||
other_chrome_path.BaseName() ==
|
other_chrome_path.BaseName() == exec_path.BaseName());
|
||||||
exec_path.BaseName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A helper class to hold onto a socket.
|
// A helper class to hold onto a socket.
|
||||||
|
@ -600,7 +600,7 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||||
DCHECK(ui_message_loop_ == base::MessageLoop::current());
|
DCHECK(ui_message_loop_ == base::MessageLoop::current());
|
||||||
DCHECK(reader);
|
DCHECK(reader);
|
||||||
|
|
||||||
if (parent_->notification_callback_.Run(base::CommandLine(argv),
|
if (parent_->notification_callback_.Run(argv,
|
||||||
base::FilePath(current_dir))) {
|
base::FilePath(current_dir))) {
|
||||||
// Send back "ACK" message to prevent the client process from starting up.
|
// Send back "ACK" message to prevent the client process from starting up.
|
||||||
reader->FinishWithACK(kACKToken, arraysize(kACKToken) - 1);
|
reader->FinishWithACK(kACKToken, arraysize(kACKToken) - 1);
|
||||||
|
@ -716,8 +716,7 @@ ProcessSingleton::ProcessSingleton(
|
||||||
const base::FilePath& user_data_dir,
|
const base::FilePath& user_data_dir,
|
||||||
const NotificationCallback& notification_callback)
|
const NotificationCallback& notification_callback)
|
||||||
: notification_callback_(notification_callback),
|
: notification_callback_(notification_callback),
|
||||||
current_pid_(base::GetCurrentProcId()),
|
current_pid_(base::GetCurrentProcId()) {
|
||||||
watcher_(new LinuxWatcher(this)) {
|
|
||||||
socket_path_ = user_data_dir.Append(kSingletonSocketFilename);
|
socket_path_ = user_data_dir.Append(kSingletonSocketFilename);
|
||||||
lock_path_ = user_data_dir.Append(kSingletonLockFilename);
|
lock_path_ = user_data_dir.Append(kSingletonLockFilename);
|
||||||
cookie_path_ = user_data_dir.Append(kSingletonCookieFilename);
|
cookie_path_ = user_data_dir.Append(kSingletonCookieFilename);
|
||||||
|
@ -819,7 +818,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||||
return PROCESS_NONE;
|
return PROCESS_NONE;
|
||||||
to_send.append(current_dir.value());
|
to_send.append(current_dir.value());
|
||||||
|
|
||||||
const std::vector<std::string>& argv = cmd_line.argv();
|
const std::vector<std::string>& argv = atom::AtomCommandLine::argv();
|
||||||
for (std::vector<std::string>::const_iterator it = argv.begin();
|
for (std::vector<std::string>::const_iterator it = argv.begin();
|
||||||
it != argv.end(); ++it) {
|
it != argv.end(); ++it) {
|
||||||
to_send.push_back(kTokenDelimiter);
|
to_send.push_back(kTokenDelimiter);
|
||||||
|
@ -988,13 +987,15 @@ bool ProcessSingleton::Create() {
|
||||||
if (listen(sock, 5) < 0)
|
if (listen(sock, 5) < 0)
|
||||||
NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
|
NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
|
||||||
|
|
||||||
DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
|
// In Electron the ProcessSingleton is created earlier than the IO
|
||||||
BrowserThread::PostTask(
|
// thread gets created, so we have to postpone the call until message
|
||||||
BrowserThread::IO,
|
// loop is up an running.
|
||||||
|
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
|
||||||
|
base::ThreadTaskRunnerHandle::Get());
|
||||||
|
task_runner->PostTask(
|
||||||
FROM_HERE,
|
FROM_HERE,
|
||||||
base::Bind(&ProcessSingleton::LinuxWatcher::StartListening,
|
base::Bind(&ProcessSingleton::StartListening,
|
||||||
watcher_.get(),
|
base::Unretained(this), sock));
|
||||||
sock));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1005,6 +1006,17 @@ void ProcessSingleton::Cleanup() {
|
||||||
UnlinkPath(lock_path_);
|
UnlinkPath(lock_path_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProcessSingleton::StartListening(int sock) {
|
||||||
|
watcher_ = new LinuxWatcher(this);
|
||||||
|
DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
|
||||||
|
BrowserThread::PostTask(
|
||||||
|
BrowserThread::IO,
|
||||||
|
FROM_HERE,
|
||||||
|
base::Bind(&ProcessSingleton::LinuxWatcher::StartListening,
|
||||||
|
watcher_.get(),
|
||||||
|
sock));
|
||||||
|
}
|
||||||
|
|
||||||
bool ProcessSingleton::IsSameChromeInstance(pid_t pid) {
|
bool ProcessSingleton::IsSameChromeInstance(pid_t pid) {
|
||||||
pid_t cur_pid = current_pid_;
|
pid_t cur_pid = current_pid_;
|
||||||
while (pid != cur_pid) {
|
while (pid != cur_pid) {
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "chrome/browser/process_singleton_startup_lock.h"
|
|
||||||
|
|
||||||
#include "base/bind.h"
|
|
||||||
#include "base/logging.h"
|
|
||||||
|
|
||||||
ProcessSingletonStartupLock::ProcessSingletonStartupLock(
|
|
||||||
const ProcessSingleton::NotificationCallback& original_callback)
|
|
||||||
: locked_(true),
|
|
||||||
original_callback_(original_callback) {}
|
|
||||||
|
|
||||||
ProcessSingletonStartupLock::~ProcessSingletonStartupLock() {}
|
|
||||||
|
|
||||||
ProcessSingleton::NotificationCallback
|
|
||||||
ProcessSingletonStartupLock::AsNotificationCallback() {
|
|
||||||
return base::Bind(&ProcessSingletonStartupLock::NotificationCallbackImpl,
|
|
||||||
base::Unretained(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessSingletonStartupLock::Unlock() {
|
|
||||||
DCHECK(CalledOnValidThread());
|
|
||||||
locked_ = false;
|
|
||||||
|
|
||||||
// Replay the command lines of the messages which were received while the
|
|
||||||
// ProcessSingleton was locked. Only replay each message once.
|
|
||||||
std::set<DelayedStartupMessage> replayed_messages;
|
|
||||||
for (std::vector<DelayedStartupMessage>::const_iterator it =
|
|
||||||
saved_startup_messages_.begin();
|
|
||||||
it != saved_startup_messages_.end(); ++it) {
|
|
||||||
if (replayed_messages.find(*it) != replayed_messages.end())
|
|
||||||
continue;
|
|
||||||
original_callback_.Run(base::CommandLine(it->first), it->second);
|
|
||||||
replayed_messages.insert(*it);
|
|
||||||
}
|
|
||||||
saved_startup_messages_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProcessSingletonStartupLock::NotificationCallbackImpl(
|
|
||||||
const base::CommandLine& command_line,
|
|
||||||
const base::FilePath& current_directory) {
|
|
||||||
if (locked_) {
|
|
||||||
// If locked, it means we are not ready to process this message because
|
|
||||||
// we are probably in a first run critical phase.
|
|
||||||
saved_startup_messages_.push_back(
|
|
||||||
std::make_pair(command_line.argv(), current_directory));
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return original_callback_.Run(command_line, current_directory);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#ifndef CHROME_BROWSER_PROCESS_SINGLETON_STARTUP_LOCK_H_
|
|
||||||
#define CHROME_BROWSER_PROCESS_SINGLETON_STARTUP_LOCK_H_
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "base/basictypes.h"
|
|
||||||
#include "base/command_line.h"
|
|
||||||
#include "base/files/file_path.h"
|
|
||||||
#include "base/threading/non_thread_safe.h"
|
|
||||||
#include "chrome/browser/process_singleton.h"
|
|
||||||
|
|
||||||
// Provides a ProcessSingleton::NotificationCallback that can queue up
|
|
||||||
// command-line invocations during startup and execute them when startup
|
|
||||||
// completes.
|
|
||||||
//
|
|
||||||
// The object starts in a locked state. |Unlock()| must be called
|
|
||||||
// when the process is prepared to handle command-line invocations.
|
|
||||||
//
|
|
||||||
// Once unlocked, notifications are forwarded to a wrapped NotificationCallback.
|
|
||||||
class ProcessSingletonStartupLock : public base::NonThreadSafe {
|
|
||||||
public:
|
|
||||||
explicit ProcessSingletonStartupLock(
|
|
||||||
const ProcessSingleton::NotificationCallback& original_callback);
|
|
||||||
~ProcessSingletonStartupLock();
|
|
||||||
|
|
||||||
// Returns the ProcessSingleton::NotificationCallback.
|
|
||||||
// The callback is only valid during the lifetime of the
|
|
||||||
// ProcessSingletonStartupLock instance.
|
|
||||||
ProcessSingleton::NotificationCallback AsNotificationCallback();
|
|
||||||
|
|
||||||
// Executes previously queued command-line invocations and allows future
|
|
||||||
// invocations to be executed immediately.
|
|
||||||
void Unlock();
|
|
||||||
|
|
||||||
bool locked() { return locked_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef std::pair<base::CommandLine::StringVector, base::FilePath>
|
|
||||||
DelayedStartupMessage;
|
|
||||||
|
|
||||||
bool NotificationCallbackImpl(const base::CommandLine& command_line,
|
|
||||||
const base::FilePath& current_directory);
|
|
||||||
|
|
||||||
bool locked_;
|
|
||||||
std::vector<DelayedStartupMessage> saved_startup_messages_;
|
|
||||||
ProcessSingleton::NotificationCallback original_callback_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ProcessSingletonStartupLock);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CHROME_BROWSER_PROCESS_SINGLETON_STARTUP_LOCK_H_
|
|
|
@ -77,8 +77,21 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||||
return !*result;
|
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,
|
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||||
base::CommandLine* parsed_command_line,
|
base::CommandLine::StringVector* parsed_command_line,
|
||||||
base::FilePath* current_directory) {
|
base::FilePath* current_directory) {
|
||||||
// We should have enough room for the shortest command (min_message_size)
|
// We should have enough room for the shortest command (min_message_size)
|
||||||
// and also be a multiple of wchar_t bytes. The shortest command
|
// and also be a multiple of wchar_t bytes. The shortest command
|
||||||
|
@ -131,7 +144,7 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||||
// Get command line.
|
// Get command line.
|
||||||
const std::wstring cmd_line =
|
const std::wstring cmd_line =
|
||||||
msg.substr(second_null + 1, third_null - second_null);
|
msg.substr(second_null + 1, third_null - second_null);
|
||||||
*parsed_command_line = base::CommandLine::FromString(cmd_line);
|
*parsed_command_line = CommandLineStringToArgv(cmd_line);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -149,7 +162,7 @@ bool ProcessLaunchNotification(
|
||||||
// Handle the WM_COPYDATA message from another process.
|
// Handle the WM_COPYDATA message from another process.
|
||||||
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
|
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
|
||||||
|
|
||||||
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
|
base::CommandLine::StringVector parsed_command_line;
|
||||||
base::FilePath current_directory;
|
base::FilePath current_directory;
|
||||||
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||||
*result = TRUE;
|
*result = TRUE;
|
||||||
|
|
|
@ -299,25 +299,38 @@ 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
|
single instance of your app is running, and other instances signal this
|
||||||
instance and exit.
|
instance and exit.
|
||||||
|
|
||||||
`callback` is called when a second instance has been executed, and provides
|
`callback` will be called with `callback(argv, workingDirectory)` when a second
|
||||||
the command-line (including Chromium flags) and the working directory of the
|
instance has been executed. `argv` is an Array of the second instance's command
|
||||||
secondary instance. Usually applications respond to this by making their
|
line arguments, and `workingDirectory` is its current working directory. Usually
|
||||||
primary window focused and non-minimized.
|
applications respond to this by making their primary window focused and
|
||||||
|
non-minimized.
|
||||||
|
|
||||||
`callback` should return `true` if the message was successfully handled, or
|
The `callback` is guaranteed to be executed after the `ready` event of `app`
|
||||||
`false` if the secondary process should retry sending it or it failed.
|
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
|
```js
|
||||||
var myWindow;
|
var myWindow = null;
|
||||||
app.on('ready', function() {
|
|
||||||
var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
|
var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
|
||||||
// Someone tried to run a second instance, we should focus our window
|
// Someone tried to run a second instance, we should focus our window
|
||||||
if (myWindow) {
|
if (myWindow) {
|
||||||
if (myWindow.isMinimized()) myWindow.restore();
|
if (myWindow.isMinimized()) myWindow.restore();
|
||||||
myWindow.focus();
|
myWindow.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We successfully handled the command line
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -327,13 +340,10 @@ app.on('ready', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create myWindow, load the rest of the app, etc...
|
// Create myWindow, load the rest of the app, etc...
|
||||||
|
app.on('ready', function() {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns a Boolean - if `false`, your process is the primary instance of the
|
|
||||||
application and your app should continue loading. If `true`, your process has
|
|
||||||
sent its parameters to another instance, and you should immediately quit.
|
|
||||||
|
|
||||||
### `app.commandLine.appendSwitch(switch[, value])`
|
### `app.commandLine.appendSwitch(switch[, value])`
|
||||||
|
|
||||||
Append a switch (with optional `value`) to Chromium's command line.
|
Append a switch (with optional `value`) to Chromium's command line.
|
||||||
|
|
|
@ -347,6 +347,7 @@
|
||||||
'chromium_src/chrome/browser/browser_process.cc',
|
'chromium_src/chrome/browser/browser_process.cc',
|
||||||
'chromium_src/chrome/browser/browser_process.h',
|
'chromium_src/chrome/browser/browser_process.h',
|
||||||
'chromium_src/chrome/browser/chrome_process_finder_win.cc',
|
'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/chrome_notification_types.h',
|
||||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc',
|
'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc',
|
||||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener.h',
|
'chromium_src/chrome/browser/extensions/global_shortcut_listener.h',
|
||||||
|
@ -376,8 +377,8 @@
|
||||||
'chromium_src/chrome/browser/printing/print_preview_message_handler.cc',
|
'chromium_src/chrome/browser/printing/print_preview_message_handler.cc',
|
||||||
'chromium_src/chrome/browser/printing/print_preview_message_handler.h',
|
'chromium_src/chrome/browser/printing/print_preview_message_handler.h',
|
||||||
'chromium_src/chrome/browser/process_singleton_posix.cc',
|
'chromium_src/chrome/browser/process_singleton_posix.cc',
|
||||||
'chromium_src/chrome/browser/process_singleton_startup_lock.cc',
|
|
||||||
'chromium_src/chrome/browser/process_singleton_win.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.cc',
|
||||||
'chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h',
|
'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',
|
'chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc',
|
||||||
|
|
Loading…
Add table
Reference in a new issue