123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
- From: Raymond Zhao <[email protected]>
- Date: Tue, 7 Sep 2021 14:54:25 -0700
- Subject: feat: Add data parameter to ProcessSingleton
- This patch adds an additional_data parameter to the constructor of
- ProcessSingleton, so that the second instance can send additional
- data over to the first instance while requesting the ProcessSingleton
- lock.
- On the Electron side, we then expose an extra parameter to the
- 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 9de82e5422428d6419a24401e0479fbd19a15147..a30b3aae436bbe762ada1bdae69ef83127f0144b 100644
- --- a/chrome/browser/process_singleton.h
- +++ b/chrome/browser/process_singleton.h
- @@ -18,6 +18,7 @@
- #include "base/files/file_path.h"
- #include "base/memory/ref_counted.h"
- #include "base/process/process.h"
- +#include "base/containers/span.h"
- #include "ui/gfx/native_widget_types.h"
-
- #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
- @@ -99,22 +100,25 @@ class ProcessSingleton {
- // 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::RepeatingCallback<bool(const base::CommandLine& command_line,
- - const base::FilePath& current_directory)>;
- + base::RepeatingCallback<bool(const base::CommandLine& command_line,
- + const base::FilePath& current_directory,
- + const std::vector<const uint8_t> additional_data)>;
-
- #if BUILDFLAG(IS_WIN)
- ProcessSingleton(const std::string& program_name,
- const base::FilePath& user_data_dir,
- + const base::span<const uint8_t> additional_data,
- bool is_sandboxed,
- const NotificationCallback& notification_callback);
- #else
- ProcessSingleton(const base::FilePath& user_data_dir,
- + const base::span<const uint8_t> additional_data,
- const NotificationCallback& notification_callback);
- +#endif
-
- ProcessSingleton(const ProcessSingleton&) = delete;
- ProcessSingleton& operator=(const ProcessSingleton&) = delete;
-
- -#endif
- ~ProcessSingleton();
-
- // Notify another process, if available. Otherwise sets ourselves as the
- @@ -178,7 +182,10 @@ class ProcessSingleton {
- #endif
-
- private:
- + // A callback to run when the first instance receives data from the second.
- NotificationCallback notification_callback_; // Handler for notifications.
- + // Custom data to pass to the other instance during notify.
- + base::span<const uint8_t> additional_data_;
-
- #if BUILDFLAG(IS_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 99b42b0e8f81c6a5696d56fede3e168cfc282cc3..c205a14239eb9aa2b422a45755d3b07935f379c8 100644
- --- a/chrome/browser/process_singleton_posix.cc
- +++ b/chrome/browser/process_singleton_posix.cc
- @@ -607,6 +607,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:
- @@ -661,13 +662,16 @@ 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);
-
- if (parent_->notification_callback_.Run(base::CommandLine(argv),
- - base::FilePath(current_dir))) {
- + base::FilePath(current_dir),
- + std::move(additional_data))) {
- // Send back "ACK" message to prevent the client process from starting up.
- reader->FinishWithACK(kACKToken, std::size(kACKToken) - 1);
- } else {
- @@ -715,7 +719,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
- }
- }
-
- - // Validate the message. The shortest message is kStartToken\0x\0x
- + // Validate the message. The shortest message kStartToken\0\00
- + // The shortest message with additional data is kStartToken\0\00\00\0.
- const size_t kMinMessageLength = std::size(kStartToken) + 4;
- if (bytes_read_ < kMinMessageLength) {
- buf_[bytes_read_] = 0;
- @@ -745,10 +750,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
- tokens.erase(tokens.begin());
- tokens.erase(tokens.begin());
-
- + size_t num_args;
- + base::StringToSizeT(tokens[0], &num_args);
- + std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
- +
- + std::vector<const uint8_t> additional_data;
- + if (tokens.size() >= 3 + num_args) {
- + size_t additional_data_size;
- + base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
- + std::string remaining_args = base::JoinString(
- + base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
- + std::string(1, kTokenDelimiter));
- + const uint8_t* additional_data_bits =
- + reinterpret_cast<const uint8_t*>(remaining_args.c_str());
- + additional_data = std::vector<const uint8_t>(
- + 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,
- + std::move(additional_data), this));
- fd_watch_controller_.reset();
-
- // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
- @@ -777,8 +800,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()) {
- socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
- lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
- @@ -896,7 +921,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);
-
- @@ -906,11 +932,21 @@ 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(base::NumberToString(argv.size()));
- for (auto it = argv.begin(); it != argv.end(); ++it) {
- to_send.push_back(kTokenDelimiter);
- to_send.append(*it);
- }
-
- + size_t data_to_send_size = additional_data_.size_bytes();
- + if (data_to_send_size) {
- + to_send.push_back(kTokenDelimiter);
- + to_send.append(base::NumberToString(data_to_send_size));
- + to_send.push_back(kTokenDelimiter);
- + to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
- + }
- +
- // 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 c375a587abd3d575b5c75a0863b01c9611e8287c..33948d69a4a3bf098187b1c383821d1d67be5818 100644
- --- a/chrome/browser/process_singleton_win.cc
- +++ b/chrome/browser/process_singleton_win.cc
- @@ -80,10 +80,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
-
- bool ParseCommandLine(const COPYDATASTRUCT* cds,
- base::CommandLine* parsed_command_line,
- - base::FilePath* current_directory) {
- + base::FilePath* current_directory,
- + std::vector<const uint8_t>* parsed_additional_data) {
- // 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).
- + // possible is L"START\0\0" (empty command line, current directory,
- + // and additional data).
- static const int min_message_size = 7;
- if (cds->cbData < min_message_size * sizeof(wchar_t) ||
- cds->cbData % sizeof(wchar_t) != 0) {
- @@ -133,6 +135,37 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
- const std::wstring cmd_line =
- msg.substr(second_null + 1, third_null - second_null);
- *parsed_command_line = base::CommandLine::FromString(cmd_line);
- +
- + const std::wstring::size_type fourth_null =
- + msg.find_first_of(L'\0', third_null + 1);
- + if (fourth_null == std::wstring::npos ||
- + fourth_null == msg.length()) {
- + // No additional data was provided.
- + return true;
- + }
- +
- + // 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;
- + base::StringToSizeT(additional_data_length_string, &additional_data_length);
- +
- + const std::wstring::size_type fifth_null =
- + msg.find_first_of(L'\0', fourth_null + 1);
- + if (fifth_null == std::wstring::npos ||
- + fifth_null == msg.length()) {
- + LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
- + "parts separated by NULLs";
- + }
- +
- + // Get the actual additional data.
- + const std::wstring additional_data =
- + msg.substr(fourth_null + 1, fifth_null - fourth_null);
- + const uint8_t* additional_data_bytes =
- + reinterpret_cast<const uint8_t*>(additional_data.c_str());
- + *parsed_additional_data = std::vector<const uint8_t>(additional_data_bytes,
- + additional_data_bytes + additional_data_length);
- +
- return true;
- }
- return false;
- @@ -154,13 +187,14 @@ bool ProcessLaunchNotification(
-
- base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
- base::FilePath current_directory;
- - if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
- + std::vector<const uint8_t> additional_data;
- + if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory, &additional_data)) {
- *result = TRUE;
- return true;
- }
-
- - *result = notification_callback.Run(parsed_command_line, current_directory) ?
- - TRUE : FALSE;
- + *result = notification_callback.Run(parsed_command_line,
- + current_directory, std::move(additional_data)) ? TRUE : FALSE;
- return true;
- }
-
- @@ -261,9 +295,11 @@ bool ProcessSingleton::EscapeVirtualization(
- ProcessSingleton::ProcessSingleton(
- const std::string& program_name,
- const base::FilePath& user_data_dir,
- + const base::span<const uint8_t> additional_data,
- bool is_app_sandboxed,
- const NotificationCallback& notification_callback)
- : notification_callback_(notification_callback),
- + additional_data_(additional_data),
- program_name_(program_name),
- is_app_sandboxed_(is_app_sandboxed),
- is_virtualized_(false),
- @@ -290,7 +326,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
- return PROCESS_NONE;
- }
-
- - switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
- + switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
- case chrome::NOTIFY_SUCCESS:
- return PROCESS_NOTIFIED;
- case chrome::NOTIFY_FAILED:
- diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
- index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..cfdb2d75532d270e3dd548eb7475a6cdbddf1016 100644
- --- a/chrome/browser/win/chrome_process_finder.cc
- +++ b/chrome/browser/win/chrome_process_finder.cc
- @@ -36,7 +36,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
- return base::win::MessageWindow::FindWindow(user_data_dir.value());
- }
-
- -NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
- +NotifyChromeResult AttemptToNotifyRunningChrome(
- + HWND remote_window,
- + const base::span<const uint8_t> additional_data) {
- TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
-
- DCHECK(remote_window);
- @@ -50,7 +52,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_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<command-line>\0<additional-data-length>\0<additional-data>".
- std::wstring to_send(L"START\0", 6); // want the NULL in the string.
- base::FilePath cur_dir;
- if (!base::GetCurrentDirectory(&cur_dir)) {
- @@ -64,6 +67,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
- base::CommandLine::ForCurrentProcess()->GetCommandLineString());
- to_send.append(L"\0", 1); // Null separator.
-
- + size_t additional_data_size = additional_data.size_bytes();
- + if (additional_data_size) {
- + // Send over the size, because the reinterpret cast to wchar_t could
- + // add padding.
- + to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
- + to_send.append(L"\0", 1); // Null separator.
- +
- + size_t padded_size = additional_data_size / sizeof(wchar_t);
- + if (additional_data_size % sizeof(wchar_t) != 0) {
- + padded_size++;
- + }
- + to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
- + padded_size);
- + 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);
- diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
- index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
- --- a/chrome/browser/win/chrome_process_finder.h
- +++ b/chrome/browser/win/chrome_process_finder.h
- @@ -7,6 +7,7 @@
-
- #include <windows.h>
-
- +#include "base/containers/span.h"
- #include "base/time/time.h"
-
- namespace base {
- @@ -27,7 +28,9 @@ 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.
- -NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
- +NotifyChromeResult AttemptToNotifyRunningChrome(
- + HWND remote_window,
- + const base::span<const uint8_t> additional_data);
-
- // Changes the notification timeout to |new_timeout|, returns the old timeout.
- base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|