feat_add_data_parameter_to_processsingleton.patch 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
  2. From: Raymond Zhao <[email protected]>
  3. Date: Tue, 7 Sep 2021 14:54:25 -0700
  4. Subject: feat: Add data parameter to ProcessSingleton
  5. This patch adds an additional_data parameter to the constructor of
  6. ProcessSingleton, so that the second instance can send additional
  7. data over to the first instance while requesting the ProcessSingleton
  8. lock.
  9. On the Electron side, we then expose an extra parameter to the
  10. app.requestSingleInstanceLock API so that users can pass in a JSON
  11. object for the second instance to send to the first instance.
  12. diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
  13. index 31f5b160e4cd755cfb56a62b04261ee1bee80277..4305ba61d2489c5817785077d1ace8767b41bdde 100644
  14. --- a/chrome/browser/process_singleton.h
  15. +++ b/chrome/browser/process_singleton.h
  16. @@ -18,6 +18,8 @@
  17. #include "base/functional/callback.h"
  18. #include "base/memory/ref_counted.h"
  19. #include "base/process/process.h"
  20. +#include "base/containers/span.h"
  21. +#include "base/memory/raw_span.h"
  22. #include "ui/gfx/native_widget_types.h"
  23. #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
  24. @@ -100,21 +102,24 @@ class ProcessSingleton {
  25. // should handle it (i.e., because the current process is shutting down).
  26. using NotificationCallback =
  27. base::RepeatingCallback<bool(base::CommandLine command_line,
  28. - const base::FilePath& current_directory)>;
  29. + const base::FilePath& current_directory,
  30. + const std::vector<uint8_t> additional_data)>;
  31. #if BUILDFLAG(IS_WIN)
  32. ProcessSingleton(const std::string& program_name,
  33. const base::FilePath& user_data_dir,
  34. + const base::raw_span<const uint8_t> additional_data,
  35. bool is_sandboxed,
  36. const NotificationCallback& notification_callback);
  37. #else
  38. ProcessSingleton(const base::FilePath& user_data_dir,
  39. + const base::raw_span<const uint8_t> additional_data,
  40. const NotificationCallback& notification_callback);
  41. +#endif
  42. ProcessSingleton(const ProcessSingleton&) = delete;
  43. ProcessSingleton& operator=(const ProcessSingleton&) = delete;
  44. -#endif
  45. ~ProcessSingleton();
  46. // Notify another process, if available. Otherwise sets ourselves as the
  47. @@ -178,7 +183,10 @@ class ProcessSingleton {
  48. #endif
  49. private:
  50. + // A callback to run when the first instance receives data from the second.
  51. NotificationCallback notification_callback_; // Handler for notifications.
  52. + // Custom data to pass to the other instance during notify.
  53. + base::raw_span<const uint8_t> additional_data_;
  54. #if BUILDFLAG(IS_WIN)
  55. bool EscapeVirtualization(const base::FilePath& user_data_dir);
  56. diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
  57. index 08cbe32a258bf478f1da0a07064d3e9ef14c44a5..b9f2a43cb90fac4b031a4b4da38d6435a50990d2 100644
  58. --- a/chrome/browser/process_singleton_posix.cc
  59. +++ b/chrome/browser/process_singleton_posix.cc
  60. @@ -614,6 +614,7 @@ class ProcessSingleton::LinuxWatcher
  61. // |reader| is for sending back ACK message.
  62. void HandleMessage(const std::string& current_dir,
  63. const std::vector<std::string>& argv,
  64. + const std::vector<uint8_t> additional_data,
  65. SocketReader* reader);
  66. // Called when the ProcessSingleton that owns this class is about to be
  67. @@ -673,13 +674,17 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
  68. }
  69. void ProcessSingleton::LinuxWatcher::HandleMessage(
  70. - const std::string& current_dir, const std::vector<std::string>& argv,
  71. + const std::string& current_dir,
  72. + const std::vector<std::string>& argv,
  73. + const std::vector<uint8_t> additional_data,
  74. SocketReader* reader) {
  75. DCHECK(ui_task_runner_->BelongsToCurrentThread());
  76. DCHECK(reader);
  77. if (parent_ && parent_->notification_callback_.Run(
  78. - base::CommandLine(argv), base::FilePath(current_dir))) {
  79. + base::CommandLine(argv),
  80. + base::FilePath(current_dir),
  81. + std::move(additional_data))) {
  82. // Send back "ACK" message to prevent the client process from starting up.
  83. reader->FinishWithACK(kACKToken, std::size(kACKToken) - 1);
  84. } else {
  85. @@ -727,7 +732,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
  86. }
  87. }
  88. - // Validate the message. The shortest message is kStartToken\0x\0x
  89. + // Validate the message. The shortest message kStartToken\0\00
  90. + // The shortest message with additional data is kStartToken\0\00\00\0.
  91. const size_t kMinMessageLength = std::size(kStartToken) + 4;
  92. if (bytes_read_ < kMinMessageLength) {
  93. buf_[bytes_read_] = 0;
  94. @@ -757,10 +763,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
  95. tokens.erase(tokens.begin());
  96. tokens.erase(tokens.begin());
  97. + size_t num_args;
  98. + base::StringToSizeT(tokens[0], &num_args);
  99. + std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
  100. +
  101. + std::vector<uint8_t> additional_data;
  102. + if (tokens.size() >= 3 + num_args) {
  103. + size_t additional_data_size;
  104. + base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
  105. + std::string remaining_args = base::JoinString(
  106. + base::span(tokens.begin() + 2 + num_args, tokens.end()),
  107. + std::string(1, kTokenDelimiter));
  108. + const uint8_t* additional_data_bits =
  109. + reinterpret_cast<const uint8_t*>(remaining_args.c_str());
  110. + additional_data = std::vector<uint8_t>(
  111. + additional_data_bits, additional_data_bits + additional_data_size);
  112. + }
  113. +
  114. // Return to the UI thread to handle opening a new browser tab.
  115. ui_task_runner_->PostTask(
  116. FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
  117. - parent_, current_dir, tokens, this));
  118. + parent_, current_dir, command_line,
  119. + std::move(additional_data), this));
  120. fd_watch_controller_.reset();
  121. // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
  122. @@ -789,8 +813,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
  123. //
  124. ProcessSingleton::ProcessSingleton(
  125. const base::FilePath& user_data_dir,
  126. + const base::raw_span<const uint8_t> additional_data,
  127. const NotificationCallback& notification_callback)
  128. : notification_callback_(notification_callback),
  129. + additional_data_(additional_data),
  130. current_pid_(base::GetCurrentProcId()) {
  131. socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
  132. lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
  133. @@ -911,7 +937,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
  134. sizeof(socket_timeout));
  135. // Found another process, prepare our command line
  136. - // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
  137. + // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
  138. + // \0<additional-data-length>\0<additional-data>".
  139. std::string to_send(kStartToken);
  140. to_send.push_back(kTokenDelimiter);
  141. @@ -921,11 +948,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
  142. to_send.append(current_dir.value());
  143. const std::vector<std::string>& argv = cmd_line.argv();
  144. + to_send.push_back(kTokenDelimiter);
  145. + to_send.append(base::NumberToString(argv.size()));
  146. for (auto it = argv.begin(); it != argv.end(); ++it) {
  147. to_send.push_back(kTokenDelimiter);
  148. to_send.append(*it);
  149. }
  150. + size_t data_to_send_size = additional_data_.size_bytes();
  151. + if (data_to_send_size) {
  152. + to_send.push_back(kTokenDelimiter);
  153. + to_send.append(base::NumberToString(data_to_send_size));
  154. + to_send.push_back(kTokenDelimiter);
  155. + to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
  156. + }
  157. +
  158. // Send the message
  159. if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
  160. // Try to kill the other process, because it might have been dead.
  161. diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
  162. index d91f58ebe3a024bc41ed72121c49172f68e0d862..7b85ba5ed8d0c2a152899ad65f275e6680a93dba 100644
  163. --- a/chrome/browser/process_singleton_win.cc
  164. +++ b/chrome/browser/process_singleton_win.cc
  165. @@ -81,10 +81,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
  166. bool ParseCommandLine(const COPYDATASTRUCT* cds,
  167. base::CommandLine* parsed_command_line,
  168. - base::FilePath* current_directory) {
  169. + base::FilePath* current_directory,
  170. + std::vector<uint8_t>* parsed_additional_data) {
  171. // We should have enough room for the shortest command (min_message_size)
  172. // and also be a multiple of wchar_t bytes. The shortest command
  173. - // possible is L"START\0\0" (empty current directory and command line).
  174. + // possible is L"START\0\0" (empty command line, current directory,
  175. + // and additional data).
  176. static const int min_message_size = 7;
  177. if (cds->cbData < min_message_size * sizeof(wchar_t) ||
  178. cds->cbData % sizeof(wchar_t) != 0) {
  179. @@ -134,6 +136,23 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
  180. const std::wstring cmd_line =
  181. msg.substr(second_null + 1, third_null - second_null);
  182. *parsed_command_line = base::CommandLine::FromString(cmd_line);
  183. +
  184. + const std::wstring::size_type fourth_null =
  185. + msg.find_first_of(L'\0', third_null + 1);
  186. + if (fourth_null == std::wstring::npos ||
  187. + fourth_null == msg.length()) {
  188. + // No additional data was provided.
  189. + return true;
  190. + }
  191. +
  192. + // Get the actual additional data.
  193. + const std::wstring additional_data =
  194. + msg.substr(third_null + 1, fourth_null - third_null);
  195. + base::span<const uint8_t> additional_data_bytes =
  196. + base::as_byte_span(additional_data);
  197. + *parsed_additional_data = std::vector<uint8_t>(
  198. + additional_data_bytes.begin(), additional_data_bytes.end());
  199. +
  200. return true;
  201. }
  202. return false;
  203. @@ -155,13 +174,14 @@ bool ProcessLaunchNotification(
  204. base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
  205. base::FilePath current_directory;
  206. - if (!ParseCommandLine(cds, &parsed_command_line, &current_directory)) {
  207. + std::vector<uint8_t> additional_data;
  208. + if (!ParseCommandLine(cds, &parsed_command_line, &current_directory, &additional_data)) {
  209. *result = TRUE;
  210. return true;
  211. }
  212. - *result = notification_callback.Run(parsed_command_line, current_directory) ?
  213. - TRUE : FALSE;
  214. + *result = notification_callback.Run(parsed_command_line,
  215. + current_directory, std::move(additional_data)) ? TRUE : FALSE;
  216. return true;
  217. }
  218. @@ -265,9 +285,11 @@ bool ProcessSingleton::EscapeVirtualization(
  219. ProcessSingleton::ProcessSingleton(
  220. const std::string& program_name,
  221. const base::FilePath& user_data_dir,
  222. + const base::raw_span<const uint8_t> additional_data,
  223. bool is_app_sandboxed,
  224. const NotificationCallback& notification_callback)
  225. : notification_callback_(notification_callback),
  226. + additional_data_(additional_data),
  227. program_name_(program_name),
  228. is_app_sandboxed_(is_app_sandboxed),
  229. is_virtualized_(false),
  230. @@ -294,7 +316,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
  231. return PROCESS_NONE;
  232. }
  233. - switch (AttemptToNotifyRunningChrome(remote_window_)) {
  234. + switch (AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
  235. case NotifyChromeResult::NOTIFY_SUCCESS:
  236. return PROCESS_NOTIFIED;
  237. case NotifyChromeResult::NOTIFY_FAILED:
  238. diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
  239. index 019ac7e93e009a713ce56ee8bcacf467b4fe769d..283693966c041340983aa78a95f8a274db601fb4 100644
  240. --- a/chrome/browser/win/chrome_process_finder.cc
  241. +++ b/chrome/browser/win/chrome_process_finder.cc
  242. @@ -39,7 +39,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
  243. return base::win::MessageWindow::FindWindow(user_data_dir.value());
  244. }
  245. -NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
  246. +NotifyChromeResult AttemptToNotifyRunningChrome(
  247. + HWND remote_window,
  248. + const base::raw_span<const uint8_t> additional_data) {
  249. TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
  250. DCHECK(remote_window);
  251. @@ -68,12 +70,24 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
  252. new_command_line.AppendSwitchNative(switches::kSourceShortcut, si.lpTitle);
  253. // Send the command line to the remote chrome window.
  254. - // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
  255. + // Format is
  256. + // "START\0<current-directory>\0<command-line>\0<additional-data>".
  257. std::wstring to_send = base::StrCat(
  258. {std::wstring_view{L"START\0", 6}, cur_dir.value(),
  259. std::wstring_view{L"\0", 1}, new_command_line.GetCommandLineString(),
  260. std::wstring_view{L"\0", 1}});
  261. + size_t additional_data_size = additional_data.size_bytes();
  262. + if (additional_data_size) {
  263. + size_t padded_size = additional_data_size / sizeof(wchar_t);
  264. + if (additional_data_size % sizeof(wchar_t) != 0) {
  265. + padded_size++;
  266. + }
  267. + to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
  268. + padded_size);
  269. + to_send.append(L"\0", 1); // Null separator.
  270. + }
  271. +
  272. // Allow the current running browser window to make itself the foreground
  273. // window (otherwise it will just flash in the taskbar).
  274. ::AllowSetForegroundWindow(process_id);
  275. diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
  276. index 91e5e623840b9912bd05d024c12e3eb3f1ba2f53..63b5b10013c96dea4e77e5e56a060973a1752faa 100644
  277. --- a/chrome/browser/win/chrome_process_finder.h
  278. +++ b/chrome/browser/win/chrome_process_finder.h
  279. @@ -7,6 +7,7 @@
  280. #include <windows.h>
  281. +#include "base/memory/raw_span.h"
  282. #include "base/time/time.h"
  283. namespace base {
  284. @@ -25,7 +26,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
  285. // Attempts to send the current command line to an already running instance of
  286. // Chrome via a WM_COPYDATA message.
  287. // Returns true if a running Chrome is found and successfully notified.
  288. -NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
  289. +NotifyChromeResult AttemptToNotifyRunningChrome(
  290. + HWND remote_window,
  291. + const base::raw_span<const uint8_t> additional_data);
  292. // Changes the notification timeout to |new_timeout|, returns the old timeout.
  293. base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);