|
@@ -1,639 +0,0 @@
|
|
|
-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 transfer mechanism to requestSingleInstanceLock flow
|
|
|
-
|
|
|
-This patch adds code that allows for the second instance to send
|
|
|
-additional data to the first instance, and for the first instance
|
|
|
-to send additional data back to the second instance, during the
|
|
|
-app.requestSingleInstanceLock call.
|
|
|
-
|
|
|
-Firstly, 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.
|
|
|
-
|
|
|
-Then, we add additional processing to the second-instance event, both
|
|
|
-so the first instance can receive additional data from the second
|
|
|
-instance, but also so the second instance can send back additional
|
|
|
-data to the first instance if needed.
|
|
|
-
|
|
|
-diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
|
|
|
-index 5a64220aaf1309832dc0ad543e353de67fe0a779..a568dd10d1ef8679d66f4cdc6a471c251cbcd4eb 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)
|
|
|
-@@ -93,6 +94,9 @@ class ProcessSingleton {
|
|
|
-
|
|
|
- static constexpr int kNumNotifyResults = LAST_VALUE + 1;
|
|
|
-
|
|
|
-+ using NotificationAckCallback =
|
|
|
-+ base::RepeatingCallback<void(const base::span<const uint8_t>* ack_data)>;
|
|
|
-+
|
|
|
- // Implement this callback to handle notifications from other processes. The
|
|
|
- // callback will receive the command line and directory with which the other
|
|
|
- // Chrome process was launched. Return true if the command line will be
|
|
|
-@@ -100,21 +104,27 @@ class ProcessSingleton {
|
|
|
- // 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)>;
|
|
|
-+ const base::FilePath& current_directory,
|
|
|
-+ const std::vector<const uint8_t> additional_data,
|
|
|
-+ const NotificationAckCallback& ack_callback)>;
|
|
|
-
|
|
|
- #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);
|
|
|
-+ const NotificationCallback& notification_callback,
|
|
|
-+ const NotificationAckCallback& ack_notification_callback);
|
|
|
- #else
|
|
|
- ProcessSingleton(const base::FilePath& user_data_dir,
|
|
|
-- const NotificationCallback& notification_callback);
|
|
|
-+ const base::span<const uint8_t> additional_data,
|
|
|
-+ const NotificationCallback& notification_callback,
|
|
|
-+ const NotificationAckCallback& ack_notification_callback);
|
|
|
-+#endif
|
|
|
-
|
|
|
- ProcessSingleton(const ProcessSingleton&) = delete;
|
|
|
- ProcessSingleton& operator=(const ProcessSingleton&) = delete;
|
|
|
-
|
|
|
--#endif
|
|
|
- ~ProcessSingleton();
|
|
|
-
|
|
|
- // Notify another process, if available. Otherwise sets ourselves as the
|
|
|
-@@ -177,7 +187,13 @@ class ProcessSingleton {
|
|
|
- #endif
|
|
|
-
|
|
|
- private:
|
|
|
-- NotificationCallback notification_callback_; // Handler for notifications.
|
|
|
-+ // A callback to run when the first instance receives data from the second.
|
|
|
-+ NotificationCallback notification_callback_;
|
|
|
-+ // A callback to run when the second instance
|
|
|
-+ // receives an acknowledgement from the first.
|
|
|
-+ NotificationAckCallback notification_ack_callback_;
|
|
|
-+ // 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);
|
|
|
-@@ -190,6 +206,7 @@ class ProcessSingleton {
|
|
|
- HANDLE lock_file_;
|
|
|
- base::FilePath user_data_dir_;
|
|
|
- ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
|
|
-+ HANDLE ack_pipe_;
|
|
|
- #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
|
|
- // 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
|
|
|
-diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
|
|
|
-index 7d3a441bdb64268ed5fbfa7bf589fb35a2fd1b75..b23c16fde275fdba559abb1f30e42f65ddbfc332 100644
|
|
|
---- a/chrome/browser/process_singleton_posix.cc
|
|
|
-+++ b/chrome/browser/process_singleton_posix.cc
|
|
|
-@@ -148,7 +148,7 @@ const char kACKToken[] = "ACK";
|
|
|
- const char kShutdownToken[] = "SHUTDOWN";
|
|
|
- const char kTokenDelimiter = '\0';
|
|
|
- const int kMaxMessageLength = 32 * 1024;
|
|
|
--const int kMaxACKMessageLength = base::size(kShutdownToken) - 1;
|
|
|
-+const int kMaxACKMessageLength = kMaxMessageLength;
|
|
|
-
|
|
|
- bool g_disable_prompt = false;
|
|
|
- bool g_skip_is_chrome_process_check = false;
|
|
|
-@@ -614,6 +614,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:
|
|
|
-@@ -638,6 +639,9 @@ class ProcessSingleton::LinuxWatcher
|
|
|
- // The ProcessSingleton that owns us.
|
|
|
- ProcessSingleton* const parent_;
|
|
|
-
|
|
|
-+ bool ack_callback_called_ = false;
|
|
|
-+ void AckCallback(SocketReader* reader, const base::span<const uint8_t>* response);
|
|
|
-+
|
|
|
- std::set<std::unique_ptr<SocketReader>, base::UniquePtrComparator> readers_;
|
|
|
- };
|
|
|
-
|
|
|
-@@ -668,16 +672,21 @@ 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))) {
|
|
|
-- // Send back "ACK" message to prevent the client process from starting up.
|
|
|
-- reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
|
|
|
-- } else {
|
|
|
-+ auto wrapped_ack_callback =
|
|
|
-+ base::BindRepeating(&ProcessSingleton::LinuxWatcher::AckCallback,
|
|
|
-+ base::Unretained(this), reader);
|
|
|
-+ ack_callback_called_ = false;
|
|
|
-+ if (!parent_->notification_callback_.Run(base::CommandLine(argv),
|
|
|
-+ base::FilePath(current_dir),
|
|
|
-+ std::move(additional_data),
|
|
|
-+ wrapped_ack_callback)) {
|
|
|
- LOG(WARNING) << "Not handling interprocess notification as browser"
|
|
|
- " is shutting down";
|
|
|
- // Send back "SHUTDOWN" message, so that the client process can start up
|
|
|
-@@ -687,6 +696,22 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-+void ProcessSingleton::LinuxWatcher::AckCallback(
|
|
|
-+ SocketReader* reader,
|
|
|
-+ const base::span<const uint8_t>* response) {
|
|
|
-+ // Send back "ACK" message to prevent the client process from starting up.
|
|
|
-+ if (!ack_callback_called_) {
|
|
|
-+ ack_callback_called_ = true;
|
|
|
-+ std::string ack_message;
|
|
|
-+ ack_message.append(kACKToken, base::size(kACKToken) - 1);
|
|
|
-+ if (response && response->size_bytes()) {
|
|
|
-+ ack_message.append(reinterpret_cast<const char*>(response->data()),
|
|
|
-+ response->size_bytes());
|
|
|
-+ }
|
|
|
-+ reader->FinishWithACK(ack_message.c_str(), ack_message.size());
|
|
|
-+ }
|
|
|
-+}
|
|
|
-+
|
|
|
- void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
|
|
|
- DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
|
|
- DCHECK(reader);
|
|
|
-@@ -722,7 +747,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 = base::size(kStartToken) + 4;
|
|
|
- if (bytes_read_ < kMinMessageLength) {
|
|
|
- buf_[bytes_read_] = 0;
|
|
|
-@@ -752,10 +778,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
|
|
|
-@@ -784,8 +828,12 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
|
|
- //
|
|
|
- ProcessSingleton::ProcessSingleton(
|
|
|
- const base::FilePath& user_data_dir,
|
|
|
-- const NotificationCallback& notification_callback)
|
|
|
-+ const base::span<const uint8_t> additional_data,
|
|
|
-+ const NotificationCallback& notification_callback,
|
|
|
-+ const NotificationAckCallback& notification_ack_callback)
|
|
|
- : notification_callback_(notification_callback),
|
|
|
-+ notification_ack_callback_(notification_ack_callback),
|
|
|
-+ additional_data_(additional_data),
|
|
|
- current_pid_(base::GetCurrentProcId()),
|
|
|
- watcher_(new LinuxWatcher(this)) {
|
|
|
- socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
|
|
-@@ -904,7 +952,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);
|
|
|
-
|
|
|
-@@ -914,11 +963,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.
|
|
|
-@@ -960,6 +1019,17 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
|
|
- linux_ui->NotifyWindowManagerStartupComplete();
|
|
|
- #endif
|
|
|
-
|
|
|
-+ size_t ack_data_len = len - (base::size(kACKToken) - 1);
|
|
|
-+ if (ack_data_len) {
|
|
|
-+ const uint8_t* raw_ack_data =
|
|
|
-+ reinterpret_cast<const uint8_t*>(buf + base::size(kACKToken) - 1);
|
|
|
-+ base::span<const uint8_t> ack_data =
|
|
|
-+ base::make_span(raw_ack_data, raw_ack_data + ack_data_len);
|
|
|
-+ notification_ack_callback_.Run(&ack_data);
|
|
|
-+ } else {
|
|
|
-+ notification_ack_callback_.Run(nullptr);
|
|
|
-+ }
|
|
|
-+
|
|
|
- // Assume the other process is handling the request.
|
|
|
- return PROCESS_NOTIFIED;
|
|
|
- }
|
|
|
-diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
|
|
|
-index 0ea5eb3e3cf055d981ab73486115bac53287f2d7..d9a47fc419b7d42e31498cc2d803560984f00810 100644
|
|
|
---- a/chrome/browser/process_singleton_win.cc
|
|
|
-+++ b/chrome/browser/process_singleton_win.cc
|
|
|
-@@ -13,14 +13,17 @@
|
|
|
- #include "base/command_line.h"
|
|
|
- #include "base/debug/activity_tracker.h"
|
|
|
- #include "base/files/file_path.h"
|
|
|
-+#include "base/files/file_util.h"
|
|
|
- #include "base/logging.h"
|
|
|
- #include "base/metrics/histogram_functions.h"
|
|
|
- #include "base/metrics/histogram_macros.h"
|
|
|
- #include "base/process/process.h"
|
|
|
- #include "base/process/process_info.h"
|
|
|
-+#include "base/rand_util.h"
|
|
|
- #include "base/strings/string_number_conversions.h"
|
|
|
- #include "base/strings/utf_string_conversions.h"
|
|
|
- #include "base/time/time.h"
|
|
|
-+#include "base/timer/timer.h"
|
|
|
- #include "base/trace_event/base_tracing.h"
|
|
|
- #include "base/win/registry.h"
|
|
|
- #include "base/win/scoped_handle.h"
|
|
|
-@@ -45,6 +48,13 @@
|
|
|
- namespace {
|
|
|
-
|
|
|
- const char kLockfile[] = "lockfile";
|
|
|
-+const DWORD kPipeTimeout = 10000;
|
|
|
-+const DWORD kMaxMessageLength = 32 * 1024;
|
|
|
-+
|
|
|
-+std::unique_ptr<std::vector<const uint8_t>> g_ack_data;
|
|
|
-+base::OneShotTimer g_ack_timer;
|
|
|
-+HANDLE g_write_ack_pipe;
|
|
|
-+bool g_write_ack_callback_called = false;
|
|
|
-
|
|
|
- // A helper class that acquires the given |mutex| while the AutoLockMutex is in
|
|
|
- // scope.
|
|
|
-@@ -80,10 +90,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,11 +145,82 @@ 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;
|
|
|
- }
|
|
|
-
|
|
|
-+void StoreAck(const base::span<const uint8_t>* ack_data) {
|
|
|
-+ if (ack_data) {
|
|
|
-+ g_ack_data = std::make_unique<std::vector<const uint8_t>>(ack_data->begin(),
|
|
|
-+ ack_data->end());
|
|
|
-+ } else {
|
|
|
-+ g_ack_data = nullptr;
|
|
|
-+ }
|
|
|
-+}
|
|
|
-+
|
|
|
-+void SendBackAck() {
|
|
|
-+ // This is the first instance sending the ack back to the second instance.
|
|
|
-+ if (!g_write_ack_callback_called) {
|
|
|
-+ g_write_ack_callback_called = true;
|
|
|
-+ const uint8_t* data_buffer = nullptr;
|
|
|
-+ DWORD data_to_send_size = 0;
|
|
|
-+ if (g_ack_data) {
|
|
|
-+ data_buffer = g_ack_data->data();
|
|
|
-+ DWORD ack_data_size = g_ack_data->size() * sizeof(uint8_t);
|
|
|
-+ data_to_send_size = (ack_data_size < kMaxMessageLength) ? ack_data_size : kMaxMessageLength;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ ::ConnectNamedPipe(g_write_ack_pipe, NULL);
|
|
|
-+
|
|
|
-+ DWORD bytes_written = 0;
|
|
|
-+ ::WriteFile(g_write_ack_pipe,
|
|
|
-+ (LPCVOID)data_buffer,
|
|
|
-+ data_to_send_size,
|
|
|
-+ &bytes_written,
|
|
|
-+ NULL);
|
|
|
-+ DCHECK(bytes_written == data_to_send_size);
|
|
|
-+
|
|
|
-+ ::FlushFileBuffers(g_write_ack_pipe);
|
|
|
-+ ::DisconnectNamedPipe(g_write_ack_pipe);
|
|
|
-+
|
|
|
-+ if (g_ack_data) {
|
|
|
-+ g_ack_data.reset();
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+}
|
|
|
-+
|
|
|
- bool ProcessLaunchNotification(
|
|
|
- const ProcessSingleton::NotificationCallback& notification_callback,
|
|
|
- UINT message,
|
|
|
-@@ -151,16 +234,35 @@ 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::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;
|
|
|
-+ // notification_callback.Run waits for StoreAck to
|
|
|
-+ // run to completion before moving onwards.
|
|
|
-+ // Therefore, we cannot directly send the SendBackAck
|
|
|
-+ // callback instead, as it would hang the program
|
|
|
-+ // during the ConnectNamedPipe call.
|
|
|
-+ g_write_ack_callback_called = false;
|
|
|
-+ *result = notification_callback.Run(parsed_command_line, current_directory,
|
|
|
-+ std::move(additional_data),
|
|
|
-+ base::BindRepeating(&StoreAck))
|
|
|
-+ ? TRUE
|
|
|
-+ : FALSE;
|
|
|
-+ if (*result) {
|
|
|
-+ // If *result is TRUE, we return NOTIFY_SUCCESS.
|
|
|
-+ // Only for that case does the second process read
|
|
|
-+ // the acknowledgement. Therefore, only send back
|
|
|
-+ // the acknowledgement if *result is TRUE,
|
|
|
-+ // otherwise the program hangs during the ConnectNamedPipe call.
|
|
|
-+ g_ack_timer.Start(FROM_HERE, base::Seconds(0),
|
|
|
-+ base::BindOnce(&SendBackAck));
|
|
|
-+ }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
-@@ -254,9 +356,13 @@ 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)
|
|
|
-+ const NotificationCallback& notification_callback,
|
|
|
-+ const NotificationAckCallback& notification_ack_callback)
|
|
|
- : notification_callback_(notification_callback),
|
|
|
-+ notification_ack_callback_(notification_ack_callback),
|
|
|
-+ additional_data_(additional_data),
|
|
|
- program_name_(program_name),
|
|
|
- is_app_sandboxed_(is_app_sandboxed),
|
|
|
- is_virtualized_(false),
|
|
|
-@@ -271,6 +377,47 @@ ProcessSingleton::~ProcessSingleton() {
|
|
|
- ::CloseHandle(lock_file_);
|
|
|
- }
|
|
|
-
|
|
|
-+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback,
|
|
|
-+ const std::string program_name,
|
|
|
-+ base::FilePath& user_data_dir) {
|
|
|
-+ // We are reading the ack from the first instance.
|
|
|
-+ // First, wait for the pipe.
|
|
|
-+ HWND remote_window = chrome::FindRunningChromeWindow(user_data_dir);
|
|
|
-+ DWORD process_id;
|
|
|
-+ DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
|
|
-+ std::string identifier = base::NumberToString(process_id) +
|
|
|
-+ base::NumberToString(thread_id);
|
|
|
-+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
|
|
|
-+ program_name);
|
|
|
-+ const LPCWSTR w_pipe_name = pipe_name.c_str();
|
|
|
-+ ::WaitNamedPipe(w_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
|
|
|
-+
|
|
|
-+ HANDLE read_ack_pipe = ::CreateFile(w_pipe_name,
|
|
|
-+ GENERIC_READ,
|
|
|
-+ FILE_SHARE_READ,
|
|
|
-+ NULL,
|
|
|
-+ OPEN_EXISTING,
|
|
|
-+ FILE_ATTRIBUTE_NORMAL,
|
|
|
-+ NULL);
|
|
|
-+ CHECK(read_ack_pipe != INVALID_HANDLE_VALUE);
|
|
|
-+
|
|
|
-+ DWORD bytes_read;
|
|
|
-+ uint8_t read_ack_buffer[kMaxMessageLength];
|
|
|
-+ ::ReadFile(read_ack_pipe,
|
|
|
-+ (LPVOID)read_ack_buffer,
|
|
|
-+ kMaxMessageLength,
|
|
|
-+ &bytes_read,
|
|
|
-+ NULL);
|
|
|
-+
|
|
|
-+ if (!bytes_read) {
|
|
|
-+ ack_callback.Run(nullptr);
|
|
|
-+ } else {
|
|
|
-+ base::span<const uint8_t> out_span(read_ack_buffer, read_ack_buffer + bytes_read);
|
|
|
-+ ack_callback.Run(&out_span);
|
|
|
-+ }
|
|
|
-+ ::CloseHandle(read_ack_pipe);
|
|
|
-+}
|
|
|
-+
|
|
|
- // Code roughly based on Mozilla.
|
|
|
- ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
|
|
- TRACE_EVENT0("startup", "ProcessSingleton::NotifyOtherProcess");
|
|
|
-@@ -283,8 +430,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
|
|
- return PROCESS_NONE;
|
|
|
- }
|
|
|
-
|
|
|
-- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
|
|
|
-+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
|
|
|
- case chrome::NOTIFY_SUCCESS:
|
|
|
-+ ReadAck(notification_ack_callback_, program_name_, user_data_dir_);
|
|
|
- return PROCESS_NOTIFIED;
|
|
|
- case chrome::NOTIFY_FAILED:
|
|
|
- remote_window_ = NULL;
|
|
|
-@@ -422,6 +570,26 @@ bool ProcessSingleton::Create() {
|
|
|
- << "Lock file can not be created! Error code: " << error;
|
|
|
-
|
|
|
- if (lock_file_ != INVALID_HANDLE_VALUE) {
|
|
|
-+ // We are the first instance. Create a pipe to send out ack data.
|
|
|
-+ // Create a per-process pipename using a combination of the
|
|
|
-+ // username, process id, thread id, and program name. Pipe names max
|
|
|
-+ // at 256 characters, can include any character other than a backslash
|
|
|
-+ std::string identifier = base::NumberToString(::GetCurrentProcessId()) +
|
|
|
-+ base::NumberToString(::GetCurrentThreadId());
|
|
|
-+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
|
|
|
-+ program_name_);
|
|
|
-+ const LPCWSTR w_pipe_name = pipe_name.c_str();
|
|
|
-+ ack_pipe_ = ::CreateNamedPipe(w_pipe_name,
|
|
|
-+ PIPE_ACCESS_OUTBOUND,
|
|
|
-+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
|
|
|
-+ PIPE_UNLIMITED_INSTANCES,
|
|
|
-+ kMaxMessageLength,
|
|
|
-+ 0,
|
|
|
-+ kPipeTimeout,
|
|
|
-+ NULL);
|
|
|
-+ CHECK(ack_pipe_ != INVALID_HANDLE_VALUE);
|
|
|
-+ g_write_ack_pipe = ack_pipe_;
|
|
|
-+
|
|
|
- // Set the window's title to the path of our user data directory so
|
|
|
- // other Chrome instances can decide if they should forward to us.
|
|
|
- TRACE_EVENT0("startup", "ProcessSingleton::Create:CreateWindow");
|
|
|
-@@ -449,6 +617,7 @@ bool ProcessSingleton::Create() {
|
|
|
- }
|
|
|
-
|
|
|
- void ProcessSingleton::Cleanup() {
|
|
|
-+ ::CloseHandle(ack_pipe_);
|
|
|
- }
|
|
|
-
|
|
|
- void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
|
|
|
-diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
|
|
|
-index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..ce851d09d501ebcc6d6c4065e746e869d5275b2b 100644
|
|
|
---- a/chrome/browser/win/chrome_process_finder.cc
|
|
|
-+++ b/chrome/browser/win/chrome_process_finder.cc
|
|
|
-@@ -36,9 +36,10 @@ 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);
|
|
|
- DWORD process_id = 0;
|
|
|
- DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
|
|
-@@ -50,7 +51,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 +66,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);
|