Skip to content

Commit

Permalink
fix: Use message window for requestSingleInstanceLock
Browse files Browse the repository at this point in the history
  • Loading branch information
rzhao271 committed May 19, 2022
1 parent c185516 commit dab83ee
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,8 @@ 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/base/win/message_window.cc b/base/win/message_window.cc
index 596f939bb80c001e167b73e9bced4f3b8e29c232..e32544cfd61f9fbd80ae908a7e92bb15a4a0972b 100644
--- a/base/win/message_window.cc
+++ b/base/win/message_window.cc
@@ -96,8 +96,12 @@ bool MessageWindow::CreateNamed(MessageCallback message_callback,

// static
HWND MessageWindow::FindWindow(const std::wstring& window_name) {
- return FindWindowEx(HWND_MESSAGE, nullptr, kMessageWindowClassName,
+ HWND window = FindWindowEx(HWND_MESSAGE, nullptr, kMessageWindowClassName,
window_name.c_str());
+ if (window == nullptr) {
+ DLOG(ERROR) << "FindWindow returned error code " << ::GetLastError();
+ }
+ return window;
}

bool MessageWindow::DoCreate(MessageCallback message_callback,
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
index 5a64220aaf1309832dc0ad543e353de67fe0a779..fe9a387053a8814053478c2c3af4b716d1bb368e 100644
index 5a64220aaf1309832dc0ad543e353de67fe0a779..78d126f4acff50709197f8fbdc9394e06ed02cff 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -18,6 +18,7 @@
Expand All @@ -48,7 +30,14 @@ index 5a64220aaf1309832dc0ad543e353de67fe0a779..fe9a387053a8814053478c2c3af4b716
#include "ui/gfx/native_widget_types.h"

#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
@@ -93,6 +94,9 @@ class ProcessSingleton {
@@ -88,11 +89,15 @@ class ProcessSingleton {
PROCESS_NOTIFIED = 1,
PROFILE_IN_USE = 2,
LOCK_ERROR = 3,
- LAST_VALUE = LOCK_ERROR
+ PROCESS_NOTIFIED_AWAITING_ACK = 4,
+ LAST_VALUE = PROCESS_NOTIFIED_AWAITING_ACK
};

static constexpr int kNumNotifyResults = LAST_VALUE + 1;

Expand All @@ -58,7 +47,7 @@ index 5a64220aaf1309832dc0ad543e353de67fe0a779..fe9a387053a8814053478c2c3af4b716
// 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 {
@@ -100,21 +105,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,
Expand Down Expand Up @@ -90,7 +79,7 @@ index 5a64220aaf1309832dc0ad543e353de67fe0a779..fe9a387053a8814053478c2c3af4b716
~ProcessSingleton();

// Notify another process, if available. Otherwise sets ourselves as the
@@ -177,7 +187,13 @@ class ProcessSingleton {
@@ -177,7 +188,13 @@ class ProcessSingleton {
#endif

private:
Expand All @@ -105,12 +94,11 @@ index 5a64220aaf1309832dc0ad543e353de67fe0a779..fe9a387053a8814053478c2c3af4b716

#if BUILDFLAG(IS_WIN)
bool EscapeVirtualization(const base::FilePath& user_data_dir);
@@ -185,11 +201,14 @@ class ProcessSingleton {
@@ -185,11 +202,13 @@ class ProcessSingleton {
std::string program_name_; // Used for mutexName.
bool is_app_sandboxed_; // Whether the Electron app is sandboxed.
HWND remote_window_; // The HWND_MESSAGE of another browser.
+ std::wstring ack_window_name_; // The name of the window to send the
+ // acknowledgement to.
+ std::wstring ack_window_name_; // The name of the window to send the acknowledgement to.
base::win::MessageWindow window_; // The message-only window.
bool is_virtualized_; // Stuck inside Microsoft Softricity VM environment.
HANDLE lock_file_;
Expand Down Expand Up @@ -307,7 +295,7 @@ index be2c417c07a4206fac4a9a6c03e516fd0493c942..78f74b0b21242553b6af98628dc48190
return PROCESS_NOTIFIED;
}
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d62ec6371 100644
index ec725b44296266bea1a51aea889463a0bba8449c..98e130603111896756a647e2e866bf141f43c595 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -21,6 +21,7 @@
Expand Down Expand Up @@ -368,7 +356,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
"parts separated by NULLs";
}

@@ -133,10 +146,167 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
@@ -133,11 +146,182 @@ 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);
Expand Down Expand Up @@ -396,6 +384,11 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ // Get length of the additional data.
+ const std::wstring additional_data_length_string =
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
+ if (additional_data_length_string.length() == 1) {
+ // If we get a \0, we assume no additional data was provided.
+ return true;
+ }
+
+ size_t additional_data_length = 0;
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
+ DCHECK(additional_data_length > 0);
Expand All @@ -416,11 +409,11 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ *parsed_additional_data = std::vector<uint8_t>(additional_data_bytes,
+ additional_data_bytes + additional_data_length);
+
+ return true;
+ }
+ return false;
+}
+
return true;
}
return false;
}
+bool ParseCommandLineAck(const COPYDATASTRUCT* cds,
+ std::vector<uint8_t>* parsed_additional_data) {
+ // We should have enough room for the shortest command (min_message_size)
Expand Down Expand Up @@ -461,6 +454,11 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ // Get length of the additional data.
+ const std::wstring additional_data_length_string =
+ msg.substr(first_null + 1, second_null - first_null);
+ if (additional_data_length_string.length() == 1) {
+ // If we get a \0, we assume no additional data was provided.
+ return true;
+ }
+
+ size_t additional_data_length = 0;
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
+ DCHECK(additional_data_length > 0);
Expand All @@ -481,10 +479,10 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ *parsed_additional_data = std::vector<uint8_t>(additional_data_bytes,
+ additional_data_bytes + additional_data_length);
+
return true;
}
return false;
}
+ 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(),
Expand All @@ -494,15 +492,19 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+// }
+// }
+
+void SendBackAck(const std::wstring& parsed_window_name, const base::span<const uint8_t>* ack_data) {
+ DLOG(ERROR) << "SendBackAck parsed_window_name: " << parsed_window_name;
+ HWND ack_window = chrome::FindRunningChromeWindowByName(parsed_window_name);
+void SendBackAck(const std::wstring& target_window_name, const base::span<const uint8_t>* ack_data) {
+ HWND target_window = chrome::FindRunningChromeWindowByName(target_window_name);
+ if (!target_window) {
+ // One scenario this occurs is when the user calls the SendBackAck
+ // callback without first preventing the event default.
+ DLOG(WARNING) << "Cannot find target window " << target_window_name;
+ return;
+ }
+ if (ack_data) {
+ DLOG(ERROR) << "hit SendBackAck with data to pass back to " << ack_window;
+ chrome::AttemptToNotifyRunningChrome(ack_window, nullptr, *ack_data, true);
+ chrome::AttemptToNotifyRunningChrome(target_window, L"", *ack_data, true);
+ } else {
+ std::vector<const uint8_t> empty_ack;
+ chrome::AttemptToNotifyRunningChrome(ack_window, nullptr, base::make_span(empty_ack), true);
+ chrome::AttemptToNotifyRunningChrome(target_window, L"", base::make_span(empty_ack), true);
+ }
+ // This is the first instance sending the ack back to the second instance.
+ // if (!g_write_ack_callback_called) {
Expand Down Expand Up @@ -533,10 +535,11 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ // }
+ // }
+}
+
bool ProcessLaunchNotification(
const ProcessSingleton::NotificationCallback& notification_callback,
@@ -151,16 +321,72 @@ bool ProcessLaunchNotification(
UINT message,
@@ -151,16 +335,65 @@ bool ProcessLaunchNotification(

// Handle the WM_COPYDATA message from another process.
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
Expand Down Expand Up @@ -581,39 +584,32 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result) {
+ DLOG(ERROR) << "hit ProcessAckNotification";
+ DLOG(ERROR) << "message: " << message;
+ if (message != WM_COPYDATA)
+ return false;
+
+ TRACE_EVENT0("startup", "ProcessSingleton:ProcessAckNotification");
+
+ DLOG(ERROR) << "hit ProcessAckNotification1";
+
+ // Handle the WM_COPYDATA message from another process.
+ const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
+ std::vector<uint8_t> additional_data;
+ if (!ParseCommandLineAck(cds, &additional_data)) {
+ DLOG(ERROR) << "bit";
*result = TRUE;
return true;
}

- *result = notification_callback.Run(parsed_command_line, current_directory) ?
- TRUE : FALSE;
+ if (additional_data.size()) {
+ DLOG(ERROR) << "sit";
+ base::span<const uint8_t> my_span = base::make_span(additional_data.begin(), additional_data.end());
+ notification_ack_callback.Run(&my_span);
+ } else {
+ DLOG(ERROR) << "mit";
+ notification_ack_callback.Run(nullptr);
+ }
+ *result = TRUE;
return true;
}

@@ -261,9 +487,13 @@ bool ProcessSingleton::EscapeVirtualization(
@@ -261,9 +494,13 @@ bool ProcessSingleton::EscapeVirtualization(
ProcessSingleton::ProcessSingleton(
const std::string& program_name,
const base::FilePath& user_data_dir,
Expand All @@ -628,7 +624,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
program_name_(program_name),
is_app_sandboxed_(is_app_sandboxed),
is_virtualized_(false),
@@ -278,6 +508,37 @@ ProcessSingleton::~ProcessSingleton() {
@@ -278,6 +515,37 @@ ProcessSingleton::~ProcessSingleton() {
::CloseHandle(lock_file_);
}

Expand Down Expand Up @@ -666,19 +662,30 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
// Code roughly based on Mozilla.
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
TRACE_EVENT0("startup", "ProcessSingleton::NotifyOtherProcess");
@@ -290,8 +551,10 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
@@ -290,9 +558,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
return PROCESS_NONE;
}

- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, ack_window_name_, additional_data_, false)) {
case chrome::NOTIFY_SUCCESS:
+ DLOG(ERROR) << "NotifyOtherProcess: Notify success";
+ // ReadAck(notification_ack_callback_);
return PROCESS_NOTIFIED;
- return PROCESS_NOTIFIED;
+ return PROCESS_NOTIFIED_AWAITING_ACK;
case chrome::NOTIFY_FAILED:
remote_window_ = NULL;
@@ -391,64 +654,98 @@ bool ProcessSingleton::Create() {
internal::SendRemoteProcessInteractionResultHistogram(
@@ -356,8 +624,8 @@ ProcessSingleton::NotifyOtherProcessOrCreate() {
return PROCESS_NONE; // This is the single browser process.
}
ProcessSingleton::NotifyResult result = NotifyOtherProcess();
- if (result == PROCESS_NOTIFIED || result == LOCK_ERROR) {
- if (result == PROCESS_NOTIFIED) {
+ if (result == PROCESS_NOTIFIED || result == LOCK_ERROR || result == PROCESS_NOTIFIED_AWAITING_ACK) {
+ if (result == PROCESS_NOTIFIED || result == PROCESS_NOTIFIED_AWAITING_ACK) {
UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify",
base::TimeTicks::Now() - begin_ticks);
} else {
@@ -391,64 +659,99 @@ bool ProcessSingleton::Create() {
std::wstring mutexName = base::UTF8ToWide("Local\\" + program_name_ + "ProcessSingletonStartup");

remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
Expand Down Expand Up @@ -819,19 +826,20 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d
+ if (create_ack_window) {
+ // The first instance exists, but we still want to create a window.
+ DWORD process_id = ::GetCurrentProcessId();
+ std::wstring window_name = user_data_dir_.value() + L"-ack-window-" + base::NumberToWString(process_id);
+ DWORD thread_id = ::GetCurrentThreadId();
+ std::wstring window_name = user_data_dir_.value() + L"-ack-window-" + base::NumberToWString(process_id) + L"-" + base::NumberToWString(thread_id);
+ bool result =
+ window_.CreateNamed(base::BindRepeating(&ProcessAckNotification,
+ notification_ack_callback_),
+ window_name);
+ DLOG(ERROR) << "create_ack_window with name " << window_name;
+ CHECK(result && window_.hwnd());
+ ack_window_name_ = window_name;
+
+ return false; // We are not the first instance.
}
}

@@ -456,6 +753,7 @@ bool ProcessSingleton::Create() {
@@ -456,6 +759,7 @@ bool ProcessSingleton::Create() {
}

void ProcessSingleton::Cleanup() {
Expand All @@ -840,7 +848,7 @@ index ec725b44296266bea1a51aea889463a0bba8449c..a193d214042996ea75ae2ac822c7d52d

void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..84e502b4e913f3c5bc60aad672db97ecb9b7b646 100644
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..4d203bd1dcc077550c4fbc5e185ecd9c35db0176 100644
--- a/chrome/browser/win/chrome_process_finder.cc
+++ b/chrome/browser/win/chrome_process_finder.cc
@@ -11,6 +11,7 @@
Expand All @@ -851,11 +859,7 @@ index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..84e502b4e913f3c5bc60aad672db97ec
#include "base/numerics/safe_conversions.h"
#include "base/process/process.h"
#include "base/strings/string_number_conversions.h"
@@ -33,12 +34,21 @@ namespace chrome {

HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
TRACE_EVENT0("startup", "FindRunningChromeWindow");
+ DLOG(ERROR) << "FindRunningChromeWindow " << user_data_dir.value();
@@ -36,9 +37,17 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
return base::win::MessageWindow::FindWindow(user_data_dir.value());
}

Expand All @@ -868,14 +872,14 @@ index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..84e502b4e913f3c5bc60aad672db97ec

+NotifyChromeResult AttemptToNotifyRunningChrome(
+ HWND remote_window,
+ const std::wstring* current_window_name,
+ const std::wstring& current_window_name,
+ const base::span<const uint8_t> additional_data,
+ bool is_ack_message) {
+ TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
DCHECK(remote_window);
DWORD process_id = 0;
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
@@ -50,19 +60,42 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
@@ -50,19 +59,41 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
}

// Send the command line to the remote chrome window.
Expand All @@ -890,7 +894,6 @@ index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..84e502b4e913f3c5bc60aad672db97ec
- return NOTIFY_FAILED;
+
+ if (!is_ack_message) {
+ DCHECK(current_window_name);
+ base::FilePath cur_dir;
+ if (!base::GetCurrentDirectory(&cur_dir)) {
+ TRACE_EVENT_INSTANT(
Expand All @@ -902,15 +905,15 @@ index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..84e502b4e913f3c5bc60aad672db97ec
+ to_send.append(
+ base::CommandLine::ForCurrentProcess()->GetCommandLineString());
+ to_send.append(L"\0", 1); // Null separator.
+ to_send.append(*current_window_name);
+ to_send.append(current_window_name);
+ 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(base::NumberToWString(additional_data_size));
+ to_send.append(L"\0", 1); // Null separator.
+
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
Expand All @@ -929,14 +932,6 @@ index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..84e502b4e913f3c5bc60aad672db97ec

// Allow the current running browser window to make itself the foreground
// window (otherwise it will just flash in the taskbar).
@@ -74,6 +107,7 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
cds.lpData = const_cast<wchar_t*>(to_send.c_str());
DWORD_PTR result = 0;
{
+ DLOG(ERROR) << "another hit";
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome:SendMessage");
if (::SendMessageTimeout(remote_window, WM_COPYDATA, NULL,
reinterpret_cast<LPARAM>(&cds), SMTO_ABORTIFHUNG,
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
index 5516673cee019f6060077091e59498bf9038cd6e..de88d01397f73f04858beef5b60e2ba3dc2b3b58 100644
--- a/chrome/browser/win/chrome_process_finder.h
Expand Down
5 changes: 5 additions & 0 deletions shell/browser/api/electron_api_app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,9 @@ void App::OnFirstInstanceAck(
}
}
Emit("first-instance-ack", data_to_send);

// Reset the singleton
process_singleton_.reset();
}

// This function handles the user calling
Expand Down Expand Up @@ -1173,6 +1176,8 @@ bool App::RequestSingleInstanceLock(gin::Arguments* args) {
process_singleton_.reset();
return false;
}
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED_AWAITING_ACK:
return false; // We reset the singleton after receiving the ack.
case ProcessSingleton::NotifyResult::PROCESS_NONE:
default: // Shouldn't be needed, but VS warns if it is not there.
return true;
Expand Down

0 comments on commit dab83ee

Please sign in to comment.