Skip to content

Commit

Permalink
Implement posix side
Browse files Browse the repository at this point in the history
  • Loading branch information
rzhao271 committed Sep 10, 2021
1 parent eeab4c9 commit e28a18d
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 8 deletions.
8 changes: 6 additions & 2 deletions docs/api/app.md
Expand Up @@ -482,6 +482,7 @@ Returns:
* `event` Event
* `argv` String[] - An array of the second instance's command line arguments
* `workingDirectory` String - The second instance's working directory
* `additionalData` unknown - A JSON object of additional data passed from the second instance

This event will be emitted inside the primary instance of your application
when a second instance has been executed and calls `app.requestSingleInstanceLock()`.
Expand Down Expand Up @@ -940,7 +941,9 @@ app.setJumpList([

### `app.requestSingleInstanceLock()`

Returns `Boolean`
Returns `Boolean`:

* `additionalData` unknown (optional) - A JSON object containing additional data to send to the first instance.

The return value of this method indicates whether or not this instance of your
application successfully obtained the lock. If it failed to obtain the lock,
Expand All @@ -965,7 +968,8 @@ starts:
const { app } = require('electron')
let myWindow = null

const gotTheLock = app.requestSingleInstanceLock()
const additionalData = { myKey: 'myValue' }
const gotTheLock = app.requestSingleInstanceLock(additionalData)

if (!gotTheLock) {
app.quit()
Expand Down
136 changes: 131 additions & 5 deletions patches/chromium/feat_add_data_parameter_to_processsingleton.patch
Expand Up @@ -13,7 +13,7 @@ app.requestSingleInstanceLock API so that users can pass in a JSON
object for the second instance to send to the first instance.

diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
index 9418bbacb11094628c4468d0a7b735a0f742e5f2..9ad4559a9408418f6de1d84bd1aa00f419c5d5bf 100644
index 9418bbacb11094628c4468d0a7b735a0f742e5f2..c942be131f6f41b60276579ef78d291f9c92afd6 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -19,6 +19,7 @@
Expand All @@ -40,7 +40,7 @@ index 9418bbacb11094628c4468d0a7b735a0f742e5f2..9ad4559a9408418f6de1d84bd1aa00f4
const NotificationCallback& notification_callback);
#else
ProcessSingleton(const base::FilePath& user_data_dir,
+ base::span<const uint8_t> additional_data,
+ const base::span<const uint8_t>& additional_data,
const NotificationCallback& notification_callback);
#endif
~ProcessSingleton();
Expand All @@ -53,11 +53,129 @@ index 9418bbacb11094628c4468d0a7b735a0f742e5f2..9ad4559a9408418f6de1d84bd1aa00f4

#if defined(OS_WIN)
bool EscapeVirtualization(const base::FilePath& user_data_dir);
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 31b387ca3cae53c94d8a7baefaeb17f3dbffe5e0..b04a8806c813c5b1834fb09102ac940e808403f2 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -563,6 +563,7 @@ class ProcessSingleton::LinuxWatcher
// |reader| is for sending back ACK message.
void HandleMessage(const std::string& current_dir,
const std::vector<std::string>& argv,
+ const std::vector<const uint8_t>& additional_data,
SocketReader* reader);

private:
@@ -619,13 +620,18 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
}

void ProcessSingleton::LinuxWatcher::HandleMessage(
- const std::string& current_dir, const std::vector<std::string>& argv,
+ const std::string& current_dir,
+ const std::vector<std::string>& argv,
+ const std::vector<const uint8_t>& additional_data,
SocketReader* reader) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(reader);

+ base::span<const uint8_t> additional_data_span = base::make_span(additional_data.begin(), additional_data.end());
+
if (parent_->notification_callback_.Run(base::CommandLine(argv),
- base::FilePath(current_dir))) {
+ base::FilePath(current_dir),
+ additional_data_span)) {
// Send back "ACK" message to prevent the client process from starting up.
reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
} else {
@@ -673,8 +679,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
}
}

- // Validate the message. The shortest message is kStartToken\0x\0x
- const size_t kMinMessageLength = base::size(kStartToken) + 4;
+ // Validate the message. The shortest message is kStartToken\0\00\00\0
+ const size_t kMinMessageLength = base::size(kStartToken) + 6;
if (bytes_read_ < kMinMessageLength) {
buf_[bytes_read_] = 0;
LOG(ERROR) << "Invalid socket message (wrong length):" << buf_;
@@ -687,7 +693,7 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
str, std::string(1, kTokenDelimiter),
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);

- if (tokens.size() < 3 || tokens[0] != kStartToken) {
+ if (tokens.size() < 5 || tokens[0] != kStartToken) {
LOG(ERROR) << "Wrong message format: " << str;
CleanupAndDeleteSelf();
return;
@@ -698,15 +704,23 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
timer_.Stop();

std::string current_dir = tokens[1];
+#if 0
// Remove the first two tokens. The remaining tokens should be the command
// line argv array.
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
+#endif
+
+ size_t num_args = static_cast<size_t>(std::stoul(tokens[2]));
+ std::vector<std::string> command_line(tokens.begin() + 3, tokens.begin() + 3 + num_args);
+ size_t additional_data_size = static_cast<size_t>(std::stoul(tokens[3 + num_args]));
+ const uint8_t* additional_data_bits = reinterpret_cast<const uint8_t*>(tokens[4 + num_args].c_str());
+ std::vector<const uint8_t> additional_data(additional_data_bits, additional_data_bits + additional_data_size);

// Return to the UI thread to handle opening a new browser tab.
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
- parent_, current_dir, tokens, this));
+ parent_, current_dir, command_line, additional_data, this));
fd_watch_controller_.reset();

// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
@@ -735,8 +749,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
//
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
+ const base::span<const uint8_t>& additional_data,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
+ additional_data_(additional_data),
current_pid_(base::GetCurrentProcId()),
watcher_(new LinuxWatcher(this)) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
@@ -853,7 +869,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
sizeof(socket_timeout));

// Found another process, prepare our command line
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
+ // \0<additional-data-length>\0<additional-data>".
std::string to_send(kStartToken);
to_send.push_back(kTokenDelimiter);

@@ -863,11 +880,18 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
to_send.append(current_dir.value());

const std::vector<std::string>& argv = cmd_line.argv();
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(std::to_string(argv.size()));
for (auto it = argv.begin(); it != argv.end(); ++it) {
to_send.push_back(kTokenDelimiter);
to_send.append(*it);
}

+ to_send.push_back(kTokenDelimiter);
+ to_send.append(std::to_string(additional_data_.size_bytes()));
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), additional_data_.size_bytes());
+
// Send the message
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
// Try to kill the other process, because it might have been dead.
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index 875269776e45c96ac43a3430768f1406c9608dd3..b04b01ef79994b5cd2e328d040c737a8a38116da 100644
index 875269776e45c96ac43a3430768f1406c9608dd3..caaebad93c23cfd0037a1555032dce6a842a2864 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -94,11 +94,13 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
@@ -94,18 +94,20 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {

bool ParseCommandLine(const COPYDATASTRUCT* cds,
base::CommandLine* parsed_command_line,
Expand All @@ -74,6 +192,14 @@ index 875269776e45c96ac43a3430768f1406c9608dd3..b04b01ef79994b5cd2e328d040c737a8
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.
+ // We split the string into 6 parts on NULLs.
DCHECK(cds->lpData);
const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
cds->cbData / sizeof(wchar_t));
@@ -126,7 +128,7 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
msg.find_first_of(L'\0', first_null + 1);
if (second_null == std::wstring::npos ||
Expand Down Expand Up @@ -108,7 +234,7 @@ index 875269776e45c96ac43a3430768f1406c9608dd3..b04b01ef79994b5cd2e328d040c737a8
+ // Get length of the additional data.
+ const std::wstring additional_data_length_string =
+ msg.substr(third_null + 1, fourth_null - third_null);
+ size_t additional_data_length = static_cast<size_t>(std::stoi(additional_data_length_string));
+ size_t additional_data_length = static_cast<size_t>(std::stoul(additional_data_length_string));
+
+ const std::wstring::size_type fifth_null =
+ msg.find_first_of(L'\0', fourth_null + 1);
Expand Down
2 changes: 1 addition & 1 deletion shell/browser/api/electron_api_app.cc
Expand Up @@ -1118,7 +1118,7 @@ bool App::RequestSingleInstanceLock(gin::Arguments* args) {
app_is_sandboxed, base::BindRepeating(NotificationCallbackWrapper, cb));
#else
process_singleton_ = std::make_unique<ProcessSingleton>(
user_dir, &additional_data,
user_dir, additional_data_message.encoded_message,
base::BindRepeating(NotificationCallbackWrapper, cb));
#endif

Expand Down

0 comments on commit e28a18d

Please sign in to comment.