electron/patches/chromium/feat_add_data_transfer_to_requestsingleinstancelock.patch
electron-roller[bot] 7e59d784a0
chore: bump chromium to 102.0.4961.0 (main) (#33091)
* chore: bump chromium in DEPS to 101.0.4911.0

* chore: bump chromium in DEPS to 101.0.4913.0

* chore: bump chromium in DEPS to 101.0.4915.0

* chore: bump chromium in DEPS to 101.0.4917.0

* chore: bump chromium in DEPS to 101.0.4919.0

* chore: bump chromium in DEPS to 101.0.4921.0

* chore: bump chromium in DEPS to 101.0.4923.0

* chore: bump chromium in DEPS to 101.0.4925.0

* chore: bump chromium in DEPS to 101.0.4927.0

* chore: bump chromium in DEPS to 101.0.4929.0

* chore: update patches

* chore: bump chromium in DEPS to 101.0.4931.0

* chore: update patches

* 3475388: Remove mojo::InterfacePtr<T> and mojo::InterfacePtrInfo<T>

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3475388

Actual fixes in 1824792: Migrate DisplayClient to the new Mojo types | https://chromium-review.googlesource.com/c/chromium/src/+/1824792

* 3503874: Remove base::size(), base::empty(), and base::data().

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3503874

* chore: reconcile patches with main rebase

* chore: bump chromium in DEPS to 101.0.4933.0

* chore: update patches

* 3329593: [Fenced Frame] Ensure to support external protocols in a fenced frame

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3329593

* 3503874: Remove base::size(), base::empty(), and base::data().

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3503874

* 3446451: Use forward decl of ImageSkiaRep in //ui/gfx/image/image_skia.h

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3446451

* 3499818: partition_alloc: Rename AllocFlags to AllocWithFlags

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3499818

* chore: bump chromium in DEPS to 101.0.4935.0

* chore: update patches

* 3463286: partition_alloc: Move PartitionAlloc into the own namespaces (15 of N)

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3463286

* 3506590: Reland "Support ChromeOS external protocol dialog for Fenced Frame navigations"

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3506590

* 3475388: Remove mojo::InterfacePtr<T> and mojo::InterfacePtrInfo<T>

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3475388

Actual fixes in 1880987: Convert URLLoaderReqeust from //content to new Mojo types | https://chromium-review.googlesource.com/c/chromium/src/+/1880987 The change in the roll started causing the legacy types to fail

* chore: missing SkRegion include

* 3499600: Introduce blink::WebCssOrigin

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3499600

* fixup!: 3503874: Remove base::size(), base::empty(), and base::data().

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3503874

* chore: bump chromium in DEPS to 101.0.4937.0

* chore: update patches

* 3500826: [locales] Refactor locales for ios

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3500826

* 3509531: Make some public Blink media files private

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3509531

* 3497377: bluetooth: Add BluetoothDevice.forget()

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3497377

* chore: bump chromium in DEPS to 101.0.4939.0

* chore: bump chromium in DEPS to 101.0.4941.0

* 3514804: Deprecate all existing uses of mojo_base.mojom.{Dictionary,List}Value.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3514804

* 3502592: Delete PPAPI init/shutdown code in //pdf.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3502592

* chore: update patches

* fixup! 3502592: Delete PPAPI init/shutdown code in //pdf.

* chore: bump chromium in DEPS to 101.0.4943.0

* chore: fix lint, remove unneeded headers

* fixup! 3475388: Remove mojo::InterfacePtr<T> and mojo::InterfacePtrInfo<T>

* update mojo calls in offscreen patch

* update hunspell filenames

* chore: bump chromium in DEPS to 101.0.4945.0

* chore: update patches

* fix offscreen patch again

* chore: bump chromium in DEPS to 101.0.4947.0

* chore: update patches

* chore: bump chromium in DEPS to 101.0.4949.0

* support unseasoned pdf

* update patches

* chore: update patches

* chore: [IWYU] include missing skia headers

* chore: bump chromium in DEPS to 101.0.4951.0

* chore: update patches

* 3457645: media: Remove IsKeySystemsUpdateNeeded()

https://chromium-review.googlesource.com/c/chromium/src/+/3457645

* chore: bump chromium in DEPS to 102.0.4952.2

* chore: update patches

* 3488672: Add documentId as a parameter in tabs.connect() and tabs.sendMessage().

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3488672

* 3508375: Fix an issue dangerous dialog is not shown for some apk download

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3508375

* chore: bump chromium in DEPS to 102.0.4953.0

* chore: update patches

* 3510189: Harden up drag and drop support across same-process boundaries.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3510189

* 3526815: Remove hardcoded colors from chrome/browser/ui/views/overlay/.

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3526815

* chore: bump chromium in DEPS to 102.0.4955.0

* build: add af and ur locale to manifests

3498914 [locales] Add af and ur to desktop

Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3498914

* fixup! build: add af and ur locale to manifests

* chore: bump chromium in DEPS to 102.0.4957.0

* 3529090: gin: set JS flags before v8 initialization

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/3529090

chore: fix code shear in chromium/gin_enable_disable_v8_platform.patch

* chore: update patches

* 3536433: [network] Rename data_path and http_cache_path from _path to _directory.

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/3536433

* 3111565: Relocated Page.printToPDF implementation to //components

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/3111565

refactor: inject E args to PrintRequestedPages() and ScriptedPrintCallback

TODO: currently passes a placeholder for job_settings. We have other
code paths that inject settings from electron_api_web_contents.cc.
Should those be injected here as well? (CC @codebytere)

* fixup! 3111565: Relocated Page.printToPDF implementation to //components

* fixup! 3111565: Relocated Page.printToPDF implementation to //components

* 3520025: Make "libcxx_abi_unstable" not a gn arg

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/3520025

build: since it is no longer a gn arg, patch it in

* chore: change usages of std::vector with const elements (#33373)

* chore: bump chromium in DEPS to 102.0.4959.0

* chore: update patches

* build: iwyu base/threading/platform_thread.h

* 3525774: Add GPSForCurrentDocument() into PermissionControllerDelegate.

Xref: https://chromium-review.googlesource.com/c/chromium/src/+/3525774

refactor: copy upstream impl of GetPermissionStatusForCurrentDocument into +ElectronPermissionManager

* use gclient_gn_args_from instead of hand-copying

* checkout mac on mac

* chore: update patches

* Revert "checkout mac on mac"

This reverts commit fe9ccf49ec6139868ccf2272c2016cefa3f32769.

* fixup! 3525774: Add GPSForCurrentDocument() into PermissionControllerDelegate.

* fixup! 3457645: media: Remove IsKeySystemsUpdateNeeded()

add nogncheck

* fix: set .eslintrc.json to root to avoid cascade to chromium eslintrc

* Xref: 6dfdf79b8c

Xref: https://reviews.llvm.org/D101458

Upstream added a CMakeLists.txt in an include dir ¯\_(ツ)_/¯ and
so it must be enumerated in filenames.libcxxabi.gni

* 3511268: Remove unused headers from cxx17_backports.h

https://chromium-review.googlesource.com/c/chromium/src/+/3511268

use std::size instead of base::size

* iwyu: SkPaint

3488428: [includes] Fix transitive includes of SkImageEncoder

* chore: [IWYU] include missing skia headers

* fixup! 3511268: Remove unused headers from cxx17_backports.h

* chore: bump chromium in DEPS to 102.0.4961.0

* chore: update patches

* fixup! 3475388: Remove mojo::InterfacePtr<T> and mojo::InterfacePtrInfo<T>

chore: remove unused #include

* fixup! 3510189: Harden up drag and drop support across same-process boundaries. | https://chromium-review.googlesource.com/c/chromium/src/+/3510189

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
Co-authored-by: VerteDinde <vertedinde@electronjs.org>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
Co-authored-by: VerteDinde <keeleymhammond@gmail.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-03-24 21:39:03 -04:00

600 lines
25 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Raymond Zhao <raymondzhao@microsoft.com>
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..5b701b1361707b610ed60c344e441e67ca701362 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<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 be2c417c07a4206fac4a9a6c03e516fd0493c942..78f74b0b21242553b6af98628dc48190f7d1137d 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -146,7 +146,7 @@ const char kACKToken[] = "ACK";
const char kShutdownToken[] = "SHUTDOWN";
const char kTokenDelimiter = '\0';
const int kMaxMessageLength = 32 * 1024;
-const int kMaxACKMessageLength = std::size(kShutdownToken) - 1;
+const int kMaxACKMessageLength = kMaxMessageLength;
bool g_disable_prompt = false;
bool g_skip_is_chrome_process_check = false;
@@ -612,6 +612,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<uint8_t> additional_data,
SocketReader* reader);
private:
@@ -636,6 +637,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_;
};
@@ -666,16 +670,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<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, std::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
@@ -685,6 +694,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, std::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);
@@ -720,7 +745,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;
@@ -750,10 +776,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<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<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
@@ -782,8 +826,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);
@@ -902,7 +950,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);
@@ -912,11 +961,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.
@@ -958,6 +1017,17 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
linux_ui->NotifyWindowManagerStartupComplete();
#endif
+ size_t ack_data_len = len - (std::size(kACKToken) - 1);
+ if (ack_data_len) {
+ const uint8_t* raw_ack_data =
+ reinterpret_cast<const uint8_t*>(buf + std::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 ec725b44296266bea1a51aea889463a0bba8449c..a3d4dd1efc950033855a1f2783f941384b249a5d 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -21,6 +21,7 @@
#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 +46,14 @@
namespace {
const char kLockfile[] = "lockfile";
+const LPCWSTR kPipeName = L"\\\\.\\pipe\\electronAckPipe";
+const DWORD kPipeTimeout = 10000;
+const DWORD kMaxMessageLength = 32 * 1024;
+
+std::unique_ptr<std::vector<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 +89,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<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 +144,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<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<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 +233,23 @@ 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, &current_directory)) {
+ std::vector<uint8_t> additional_data;
+ if (!ParseCommandLine(cds, &parsed_command_line, &current_directory,
+ &additional_data)) {
*result = TRUE;
return true;
}
- *result = notification_callback.Run(parsed_command_line, current_directory) ?
- TRUE : FALSE;
+ g_write_ack_callback_called = false;
+ *result = notification_callback.Run(parsed_command_line, current_directory,
+ std::move(additional_data),
+ base::BindRepeating(&StoreAck))
+ ? TRUE
+ : FALSE;
+ g_ack_timer.Start(FROM_HERE, base::Seconds(0),
+ base::BindOnce(&SendBackAck));
return true;
}
@@ -261,9 +350,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),
@@ -278,6 +371,37 @@ ProcessSingleton::~ProcessSingleton() {
::CloseHandle(lock_file_);
}
+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback) {
+ // We are reading the ack from the first instance.
+ // First, wait for the pipe.
+ ::WaitNamedPipe(kPipeName, NMPWAIT_USE_DEFAULT_WAIT);
+
+ HANDLE read_ack_pipe = ::CreateFile(kPipeName,
+ 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");
@@ -290,8 +414,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_);
return PROCESS_NOTIFIED;
case chrome::NOTIFY_FAILED:
remote_window_ = NULL;
@@ -429,6 +554,18 @@ 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.
+ ack_pipe_ = ::CreateNamedPipe(kPipeName,
+ 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");
@@ -456,6 +593,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);