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/common/native_mate_converters/callback.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/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
|
@ -112,6 +111,23 @@ 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,
|
||||
|
@ -163,10 +179,7 @@ void App::OnQuit() {
|
|||
Emit("quit");
|
||||
|
||||
if (process_singleton_.get()) {
|
||||
if (process_notify_result_ == ProcessSingleton::PROCESS_NONE) {
|
||||
process_singleton_->Cleanup();
|
||||
}
|
||||
|
||||
process_singleton_.reset();
|
||||
}
|
||||
}
|
||||
|
@ -196,10 +209,6 @@ void App::OnFinishLaunching() {
|
|||
auto handle = Session::CreateFrom(isolate(), browser_context);
|
||||
default_session_.Reset(isolate(), handle.ToV8());
|
||||
|
||||
if (process_singleton_.get()) {
|
||||
process_singleton_startup_lock_->Unlock();
|
||||
}
|
||||
|
||||
Emit("ready");
|
||||
}
|
||||
|
||||
|
@ -281,35 +290,24 @@ v8::Local<v8::Value> App::DefaultSession(v8::Isolate* isolate) {
|
|||
return v8::Local<v8::Value>::New(isolate, default_session_);
|
||||
}
|
||||
|
||||
bool App::MakeSingleInstance(ProcessSingleton::NotificationCallback callback) {
|
||||
base::FilePath userDir;
|
||||
PathService::Get(brightray::DIR_USER_DATA, &userDir);
|
||||
|
||||
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:
|
||||
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;
|
||||
default:
|
||||
case ProcessSingleton::NotifyResult::PROCESS_NONE:
|
||||
default: // Shouldn't be needed, but VS warns if it is not there.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "atom/browser/browser_observer.h"
|
||||
#include "atom/common/native_mate_converters/callback.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 "native_mate/handle.h"
|
||||
|
||||
|
@ -68,19 +67,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(ProcessSingleton::NotificationCallback callback);
|
||||
|
||||
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_;
|
||||
scoped_ptr<ProcessSingletonStartupLock> process_singleton_startup_lock_;
|
||||
ProcessSingleton::NotifyResult process_notify_result_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(App);
|
||||
};
|
||||
|
|
|
@ -62,7 +62,6 @@ 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.
|
||||
|
@ -72,7 +71,6 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
|
|||
// 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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -53,8 +53,14 @@ void Browser::Shutdown() {
|
|||
is_quiting_ = true;
|
||||
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
||||
|
||||
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 {
|
||||
|
|
|
@ -130,6 +130,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_; }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
return NOTIFY_FAILED;
|
||||
|
||||
base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
|
||||
|
||||
// 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.
|
||||
|
@ -52,7 +50,7 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
|
|||
return NOTIFY_FAILED;
|
||||
to_send.append(cur_dir.value());
|
||||
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.
|
||||
|
||||
// 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
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback =
|
||||
base::Callback<bool(const base::CommandLine& command_line,
|
||||
base::Callback<bool(const base::CommandLine::StringVector& command_line,
|
||||
const base::FilePath& current_directory)>;
|
||||
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
|
@ -133,6 +133,9 @@ class ProcessSingleton : public base::NonThreadSafe {
|
|||
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.
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/atom_command_line.h"
|
||||
#include "base/base_paths.h"
|
||||
#include "base/basictypes.h"
|
||||
#include "base/bind.h"
|
||||
|
@ -316,8 +317,7 @@ bool IsChromeProcess(pid_t pid) {
|
|||
PathService::Get(base::FILE_EXE, &exec_path);
|
||||
|
||||
return (!other_chrome_path.empty() &&
|
||||
other_chrome_path.BaseName() ==
|
||||
exec_path.BaseName());
|
||||
other_chrome_path.BaseName() == exec_path.BaseName());
|
||||
}
|
||||
|
||||
// A helper class to hold onto a socket.
|
||||
|
@ -600,7 +600,7 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(
|
|||
DCHECK(ui_message_loop_ == base::MessageLoop::current());
|
||||
DCHECK(reader);
|
||||
|
||||
if (parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
if (parent_->notification_callback_.Run(argv,
|
||||
base::FilePath(current_dir))) {
|
||||
// Send back "ACK" message to prevent the client process from starting up.
|
||||
reader->FinishWithACK(kACKToken, arraysize(kACKToken) - 1);
|
||||
|
@ -716,8 +716,7 @@ ProcessSingleton::ProcessSingleton(
|
|||
const base::FilePath& user_data_dir,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
current_pid_(base::GetCurrentProcId()) {
|
||||
socket_path_ = user_data_dir.Append(kSingletonSocketFilename);
|
||||
lock_path_ = user_data_dir.Append(kSingletonLockFilename);
|
||||
cookie_path_ = user_data_dir.Append(kSingletonCookieFilename);
|
||||
|
@ -819,7 +818,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
|||
return PROCESS_NONE;
|
||||
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();
|
||||
it != argv.end(); ++it) {
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
@ -988,13 +987,15 @@ bool ProcessSingleton::Create() {
|
|||
if (listen(sock, 5) < 0)
|
||||
NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
|
||||
|
||||
DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::IO));
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO,
|
||||
// In Electron the ProcessSingleton is created earlier than the IO
|
||||
// thread gets created, so we have to postpone the call until message
|
||||
// loop is up an running.
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
|
||||
base::ThreadTaskRunnerHandle::Get());
|
||||
task_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&ProcessSingleton::LinuxWatcher::StartListening,
|
||||
watcher_.get(),
|
||||
sock));
|
||||
base::Bind(&ProcessSingleton::StartListening,
|
||||
base::Unretained(this), sock));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1005,6 +1006,17 @@ void ProcessSingleton::Cleanup() {
|
|||
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) {
|
||||
pid_t cur_pid = current_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;
|
||||
}
|
||||
|
||||
// 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* parsed_command_line,
|
||||
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
|
||||
|
@ -131,7 +144,7 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
|||
// Get command line.
|
||||
const std::wstring cmd_line =
|
||||
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 false;
|
||||
|
@ -149,7 +162,7 @@ bool ProcessLaunchNotification(
|
|||
// Handle the WM_COPYDATA message from another process.
|
||||
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;
|
||||
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
*result = TRUE;
|
||||
|
|
|
@ -299,41 +299,51 @@ 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` is called when a second instance has been executed, and provides
|
||||
the command-line (including Chromium flags) and the working directory of the
|
||||
secondary instance. Usually applications respond to this by making their
|
||||
primary window focused and non-minimized.
|
||||
`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.
|
||||
|
||||
`callback` should return `true` if the message was successfully handled, or
|
||||
`false` if the secondary process should retry sending it or it failed.
|
||||
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;
|
||||
app.on('ready', function() {
|
||||
var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
|
||||
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();
|
||||
}
|
||||
|
||||
// We successfully handled the command line
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
if (shouldQuit) {
|
||||
if (shouldQuit) {
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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])`
|
||||
|
||||
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.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',
|
||||
|
@ -376,8 +377,8 @@
|
|||
'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_startup_lock.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',
|
||||
|
|
Loading…
Add table
Reference in a new issue