Browse Source

refactor: Convert ProcessSingleton changes to patch (#30594)

* Convert ProcessSingleton changes to patch

* Update patch

* Polish

* Add sandbox check to patch

* Add missing includes

* Fix linking error

* Fix compile error

* Apply PR feedback

* Fix compile fails

* Fix tests

* Remove extra patch

* Update test
Raymond Zhao 3 years ago
parent
commit
e6f781f403

+ 7 - 0
chromium_src/BUILD.gn

@@ -47,6 +47,7 @@ static_library("chrome") {
     "//chrome/browser/predictors/proxy_lookup_client_impl.h",
     "//chrome/browser/predictors/resolve_host_client_impl.cc",
     "//chrome/browser/predictors/resolve_host_client_impl.h",
+    "//chrome/browser/process_singleton.h",
     "//chrome/browser/ui/views/autofill/autofill_popup_view_utils.cc",
     "//chrome/browser/ui/views/autofill/autofill_popup_view_utils.h",
     "//chrome/browser/ui/views/eye_dropper/eye_dropper.cc",
@@ -57,6 +58,10 @@ static_library("chrome") {
     "//extensions/browser/app_window/size_constraints.h",
   ]
 
+  if (is_posix) {
+    sources += [ "//chrome/browser/process_singleton_posix.cc" ]
+  }
+
   if (is_mac) {
     sources += [
       "//chrome/browser/extensions/global_shortcut_listener_mac.h",
@@ -65,6 +70,7 @@ static_library("chrome") {
       "//chrome/browser/media/webrtc/system_media_capture_permissions_mac.h",
       "//chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm",
       "//chrome/browser/media/webrtc/window_icon_util_mac.mm",
+      "//chrome/browser/process_singleton_mac.mm",
       "//chrome/browser/ui/views/eye_dropper/eye_dropper_view_mac.h",
       "//chrome/browser/ui/views/eye_dropper/eye_dropper_view_mac.mm",
     ]
@@ -76,6 +82,7 @@ static_library("chrome") {
       "//chrome/browser/extensions/global_shortcut_listener_win.h",
       "//chrome/browser/icon_loader_win.cc",
       "//chrome/browser/media/webrtc/window_icon_util_win.cc",
+      "//chrome/browser/process_singleton_win.cc",
       "//chrome/browser/ui/frame/window_frame_util.h",
       "//chrome/browser/ui/view_ids.h",
       "//chrome/browser/win/chrome_process_finder.cc",

+ 0 - 185
chromium_src/chrome/browser/process_singleton.h

@@ -1,185 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_PROCESS_SINGLETON_H_
-#define CHROME_BROWSER_PROCESS_SINGLETON_H_
-
-#if defined(OS_WIN)
-#include <windows.h>
-#endif  // defined(OS_WIN)
-
-#include "base/callback.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/process/process.h"
-#include "base/sequence_checker.h"
-#include "ui/gfx/native_widget_types.h"
-
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-#include "base/files/scoped_temp_dir.h"
-#endif
-
-#if defined(OS_WIN)
-#include "base/win/message_window.h"
-#endif  // defined(OS_WIN)
-
-namespace base {
-class CommandLine;
-}
-
-// ProcessSingleton ----------------------------------------------------------
-//
-// This class allows different browser processes to communicate with
-// each other.  It is named according to the user data directory, so
-// we can be sure that no more than one copy of the application can be
-// running at once with a given data directory.
-//
-// Implementation notes:
-// - the Windows implementation uses an invisible global message window;
-// - the Linux implementation uses a Unix domain socket in the user data dir.
-
-class ProcessSingleton {
- public:
-  enum NotifyResult {
-    PROCESS_NONE,
-    PROCESS_NOTIFIED,
-    PROFILE_IN_USE,
-    LOCK_ERROR,
-  };
-
-  // 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
-  // handled within the current browser instance or false if the remote process
-  // should handle it (i.e., because the current process is shutting down).
-  using NotificationCallback = base::RepeatingCallback<bool(
-      const base::CommandLine::StringVector& command_line,
-      const base::FilePath& current_directory)>;
-
-  ProcessSingleton(const base::FilePath& user_data_dir,
-                   const NotificationCallback& notification_callback);
-  ~ProcessSingleton();
-
-  // Notify another process, if available. Otherwise sets ourselves as the
-  // singleton instance. Returns PROCESS_NONE if we became the singleton
-  // instance. Callers are guaranteed to either have notified an existing
-  // process or have grabbed the singleton (unless the profile is locked by an
-  // unreachable process).
-  // TODO(brettw): Make the implementation of this method non-platform-specific
-  // by making Linux re-use the Windows implementation.
-  NotifyResult NotifyOtherProcessOrCreate();
-  void StartListeningOnSocket();
-  void OnBrowserReady();
-
-  // Sets ourself up as the singleton instance.  Returns true on success.  If
-  // false is returned, we are not the singleton instance and the caller must
-  // exit.
-  // NOTE: Most callers should generally prefer NotifyOtherProcessOrCreate() to
-  // this method, only callers for whom failure is preferred to notifying
-  // another process should call this directly.
-  bool Create();
-
-  // Clear any lock state during shutdown.
-  void Cleanup();
-
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-  static void DisablePromptForTesting();
-#endif
-#if defined(OS_WIN)
-  // Called to query whether to kill a hung browser process that has visible
-  // windows. Return true to allow killing the hung process.
-  using ShouldKillRemoteProcessCallback = base::RepeatingCallback<bool()>;
-  void OverrideShouldKillRemoteProcessCallbackForTesting(
-      const ShouldKillRemoteProcessCallback& display_dialog_callback);
-#endif
-
- protected:
-  // Notify another process, if available.
-  // Returns true if another process was found and notified, false if we should
-  // continue with the current process.
-  // On Windows, Create() has to be called before this.
-  NotifyResult NotifyOtherProcess();
-
-#if defined(OS_POSIX) && !defined(OS_ANDROID)
-  // Exposed for testing.  We use a timeout on Linux, and in tests we want
-  // this timeout to be short.
-  NotifyResult NotifyOtherProcessWithTimeout(
-      const base::CommandLine& command_line,
-      int retry_attempts,
-      const base::TimeDelta& timeout,
-      bool kill_unresponsive);
-  NotifyResult NotifyOtherProcessWithTimeoutOrCreate(
-      const base::CommandLine& command_line,
-      int retry_attempts,
-      const base::TimeDelta& timeout);
-  void OverrideCurrentPidForTesting(base::ProcessId pid);
-  void OverrideKillCallbackForTesting(
-      const base::RepeatingCallback<void(int)>& callback);
-#endif
-
- private:
-  NotificationCallback notification_callback_;  // Handler for notifications.
-
-#if defined(OS_WIN)
-  HWND remote_window_ = nullptr;     // The HWND_MESSAGE of another browser.
-  base::win::MessageWindow window_;  // The message-only window.
-  bool is_virtualized_ =
-      false;  // Stuck inside Microsoft Softricity VM environment.
-  HANDLE lock_file_ = INVALID_HANDLE_VALUE;
-  base::FilePath user_data_dir_;
-  ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
-  // Start listening to the socket.
-  void StartListening(int sock);
-
-  // 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
-  // instance.
-  bool IsSameChromeInstance(pid_t pid);
-
-  // Extract the process's pid from a symbol link path and if it is on
-  // the same host, kill the process, unlink the lock file and return true.
-  // If the process is part of the same chrome instance, unlink the lock file
-  // and return true without killing it.
-  // If the process is on a different host, return false.
-  bool KillProcessByLockPath();
-
-  // Default function to kill a process, overridable by tests.
-  void KillProcess(int pid);
-
-  // Allow overriding for tests.
-  base::ProcessId current_pid_;
-
-  // Function to call when the other process is hung and needs to be killed.
-  // Allows overriding for tests.
-  base::RepeatingCallback<void(int)> kill_callback_;
-
-  // Path in file system to the socket.
-  base::FilePath socket_path_;
-
-  // Path in file system to the lock.
-  base::FilePath lock_path_;
-
-  // Path in file system to the cookie file.
-  base::FilePath cookie_path_;
-
-  // Temporary directory to hold the socket.
-  base::ScopedTempDir socket_dir_;
-
-  // Helper class for linux specific messages.  LinuxWatcher is ref counted
-  // because it posts messages between threads.
-  class LinuxWatcher;
-  scoped_refptr<LinuxWatcher> watcher_;
-  int sock_ = -1;
-  bool listen_on_ready_ = false;
-#endif
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(ProcessSingleton);
-};
-
-#endif  // CHROME_BROWSER_PROCESS_SINGLETON_H_

+ 0 - 1100
chromium_src/chrome/browser/process_singleton_posix.cc

@@ -1,1100 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// On Linux, when the user tries to launch a second copy of chrome, we check
-// for a socket in the user's profile directory.  If the socket file is open we
-// send a message to the first chrome browser process with the current
-// directory and second process command line flags.  The second process then
-// exits.
-//
-// Because many networked filesystem implementations do not support unix domain
-// sockets, we create the socket in a temporary directory and create a symlink
-// in the profile. This temporary directory is no longer bound to the profile,
-// and may disappear across a reboot or login to a separate session. To bind
-// them, we store a unique cookie in the profile directory, which must also be
-// present in the remote directory to connect. The cookie is checked both before
-// and after the connection. /tmp is sticky, and different Chrome sessions use
-// different cookies. Thus, a matching cookie before and after means the
-// connection was to a directory with a valid cookie.
-//
-// We also have a lock file, which is a symlink to a non-existent destination.
-// The destination is a string containing the hostname and process id of
-// chrome's browser process, eg. "SingletonLock -> example.com-9156".  When the
-// first copy of chrome exits it will delete the lock file on shutdown, so that
-// a different instance on a different host may then use the profile directory.
-//
-// If writing to the socket fails, the hostname in the lock is checked to see if
-// another instance is running a different host using a shared filesystem (nfs,
-// etc.) If the hostname differs an error is displayed and the second process
-// exits.  Otherwise the first process (if any) is killed and the second process
-// starts as normal.
-//
-// When the second process sends the current directory and command line flags to
-// the first process, it waits for an ACK message back from the first process
-// for a certain time. If there is no ACK message back in time, then the first
-// process will be considered as hung for some reason. The second process then
-// retrieves the process id from the symbol link and kills it by sending
-// SIGKILL. Then the second process starts as normal.
-
-#include "chrome/browser/process_singleton.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cstring>
-#include <memory>
-#include <set>
-#include <string>
-
-#include <stddef.h>
-
-#include "shell/browser/browser.h"
-#include "shell/common/electron_command_line.h"
-
-#include "base/base_paths.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_descriptor_watcher_posix.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/path_service.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/posix/safe_strerror.h"
-#include "base/rand_util.h"
-#include "base/sequenced_task_runner_helpers.h"
-#include "base/single_thread_task_runner.h"
-#include "base/stl_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
-#include "base/threading/platform_thread.h"
-#include "base/threading/thread_restrictions.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "build/build_config.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "net/base/network_interfaces.h"
-
-#if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
-#include "ui/views/linux_ui/linux_ui.h"
-#endif
-
-using content::BrowserThread;
-
-namespace {
-
-// Timeout for the current browser process to respond. 20 seconds should be
-// enough.
-const int kTimeoutInSeconds = 20;
-// Number of retries to notify the browser. 20 retries over 20 seconds = 1 try
-// per second.
-const int kRetryAttempts = 20;
-static bool g_disable_prompt;
-const char kStartToken[] = "START";
-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 char kLockDelimiter = '-';
-
-const base::FilePath::CharType kSingletonCookieFilename[] =
-    FILE_PATH_LITERAL("SingletonCookie");
-
-const base::FilePath::CharType kSingletonLockFilename[] =
-    FILE_PATH_LITERAL("SingletonLock");
-const base::FilePath::CharType kSingletonSocketFilename[] =
-    FILE_PATH_LITERAL("SS");
-
-// Set the close-on-exec bit on a file descriptor.
-// Returns 0 on success, -1 on failure.
-int SetCloseOnExec(int fd) {
-  int flags = fcntl(fd, F_GETFD, 0);
-  if (-1 == flags)
-    return flags;
-  if (flags & FD_CLOEXEC)
-    return 0;
-  return fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
-}
-
-// Close a socket and check return value.
-void CloseSocket(int fd) {
-  int rv = IGNORE_EINTR(close(fd));
-  DCHECK_EQ(0, rv) << "Error closing socket: " << base::safe_strerror(errno);
-}
-
-// Write a message to a socket fd.
-bool WriteToSocket(int fd, const char* message, size_t length) {
-  DCHECK(message);
-  DCHECK(length);
-  size_t bytes_written = 0;
-  do {
-    ssize_t rv = HANDLE_EINTR(
-        write(fd, message + bytes_written, length - bytes_written));
-    if (rv < 0) {
-      if (errno == EAGAIN || errno == EWOULDBLOCK) {
-        // The socket shouldn't block, we're sending so little data.  Just give
-        // up here, since NotifyOtherProcess() doesn't have an asynchronous api.
-        LOG(ERROR) << "ProcessSingleton would block on write(), so it gave up.";
-        return false;
-      }
-      PLOG(ERROR) << "write() failed";
-      return false;
-    }
-    bytes_written += rv;
-  } while (bytes_written < length);
-
-  return true;
-}
-
-struct timeval TimeDeltaToTimeVal(const base::TimeDelta& delta) {
-  struct timeval result;
-  result.tv_sec = delta.InSeconds();
-  result.tv_usec = delta.InMicroseconds() % base::Time::kMicrosecondsPerSecond;
-  return result;
-}
-
-// Wait a socket for read for a certain timeout.
-// Returns -1 if error occurred, 0 if timeout reached, > 0 if the socket is
-// ready for read.
-int WaitSocketForRead(int fd, const base::TimeDelta& timeout) {
-  fd_set read_fds;
-  struct timeval tv = TimeDeltaToTimeVal(timeout);
-
-  FD_ZERO(&read_fds);
-  FD_SET(fd, &read_fds);
-
-  return HANDLE_EINTR(select(fd + 1, &read_fds, nullptr, nullptr, &tv));
-}
-
-// Read a message from a socket fd, with an optional timeout.
-// If |timeout| <= 0 then read immediately.
-// Return number of bytes actually read, or -1 on error.
-ssize_t ReadFromSocket(int fd,
-                       char* buf,
-                       size_t bufsize,
-                       const base::TimeDelta& timeout) {
-  if (timeout > base::TimeDelta()) {
-    int rv = WaitSocketForRead(fd, timeout);
-    if (rv <= 0)
-      return rv;
-  }
-
-  size_t bytes_read = 0;
-  do {
-    ssize_t rv = HANDLE_EINTR(read(fd, buf + bytes_read, bufsize - bytes_read));
-    if (rv < 0) {
-      if (errno != EAGAIN && errno != EWOULDBLOCK) {
-        PLOG(ERROR) << "read() failed";
-        return rv;
-      } else {
-        // It would block, so we just return what has been read.
-        return bytes_read;
-      }
-    } else if (!rv) {
-      // No more data to read.
-      return bytes_read;
-    } else {
-      bytes_read += rv;
-    }
-  } while (bytes_read < bufsize);
-
-  return bytes_read;
-}
-
-// Set up a sockaddr appropriate for messaging.
-void SetupSockAddr(const std::string& path, struct sockaddr_un* addr) {
-  addr->sun_family = AF_UNIX;
-  CHECK(path.length() < base::size(addr->sun_path))
-      << "Socket path too long: " << path;
-  base::strlcpy(addr->sun_path, path.c_str(), base::size(addr->sun_path));
-}
-
-// Set up a socket appropriate for messaging.
-int SetupSocketOnly() {
-  int sock = socket(PF_UNIX, SOCK_STREAM, 0);
-  PCHECK(sock >= 0) << "socket() failed";
-
-  DCHECK(base::SetNonBlocking(sock)) << "Failed to make non-blocking socket.";
-  int rv = SetCloseOnExec(sock);
-  DCHECK_EQ(0, rv) << "Failed to set CLOEXEC on socket.";
-
-  return sock;
-}
-
-// Set up a socket and sockaddr appropriate for messaging.
-void SetupSocket(const std::string& path, int* sock, struct sockaddr_un* addr) {
-  *sock = SetupSocketOnly();
-  SetupSockAddr(path, addr);
-}
-
-// Read a symbolic link, return empty string if given path is not a symbol link.
-base::FilePath ReadLink(const base::FilePath& path) {
-  base::FilePath target;
-  if (!base::ReadSymbolicLink(path, &target)) {
-    // The only errno that should occur is ENOENT.
-    if (errno != 0 && errno != ENOENT)
-      PLOG(ERROR) << "readlink(" << path.value() << ") failed";
-  }
-  return target;
-}
-
-// Unlink a path. Return true on success.
-bool UnlinkPath(const base::FilePath& path) {
-  int rv = unlink(path.value().c_str());
-  if (rv < 0 && errno != ENOENT)
-    PLOG(ERROR) << "Failed to unlink " << path.value();
-
-  return rv == 0;
-}
-
-// Create a symlink. Returns true on success.
-bool SymlinkPath(const base::FilePath& target, const base::FilePath& path) {
-  if (!base::CreateSymbolicLink(target, path)) {
-    // Double check the value in case symlink suceeded but we got an incorrect
-    // failure due to NFS packet loss & retry.
-    int saved_errno = errno;
-    if (ReadLink(path) != target) {
-      // If we failed to create the lock, most likely another instance won the
-      // startup race.
-      errno = saved_errno;
-      PLOG(ERROR) << "Failed to create " << path.value();
-      return false;
-    }
-  }
-  return true;
-}
-
-// Extract the hostname and pid from the lock symlink.
-// Returns true if the lock existed.
-bool ParseLockPath(const base::FilePath& path,
-                   std::string* hostname,
-                   int* pid) {
-  std::string real_path = ReadLink(path).value();
-  if (real_path.empty())
-    return false;
-
-  std::string::size_type pos = real_path.rfind(kLockDelimiter);
-
-  // If the path is not a symbolic link, or doesn't contain what we expect,
-  // bail.
-  if (pos == std::string::npos) {
-    *hostname = "";
-    *pid = -1;
-    return true;
-  }
-
-  *hostname = real_path.substr(0, pos);
-
-  const std::string& pid_str = real_path.substr(pos + 1);
-  if (!base::StringToInt(pid_str, pid))
-    *pid = -1;
-
-  return true;
-}
-
-// Returns true if the user opted to unlock the profile.
-bool DisplayProfileInUseError(const base::FilePath& lock_path,
-                              const std::string& hostname,
-                              int pid) {
-  return true;
-}
-
-bool IsChromeProcess(pid_t pid) {
-  base::FilePath other_chrome_path(base::GetProcessExecutablePath(pid));
-
-  auto* command_line = base::CommandLine::ForCurrentProcess();
-  base::FilePath exec_path(command_line->GetProgram());
-  base::PathService::Get(base::FILE_EXE, &exec_path);
-
-  return (!other_chrome_path.empty() &&
-          other_chrome_path.BaseName() == exec_path.BaseName());
-}
-
-// A helper class to hold onto a socket.
-class ScopedSocket {
- public:
-  ScopedSocket() { Reset(); }
-  ~ScopedSocket() { Close(); }
-  int fd() { return fd_; }
-  void Reset() {
-    Close();
-    fd_ = SetupSocketOnly();
-  }
-  void Close() {
-    if (fd_ >= 0)
-      CloseSocket(fd_);
-    fd_ = -1;
-  }
-
- private:
-  int fd_ = -1;
-};
-
-// Returns a random string for uniquifying profile connections.
-std::string GenerateCookie() {
-  return base::NumberToString(base::RandUint64());
-}
-
-bool CheckCookie(const base::FilePath& path, const base::FilePath& cookie) {
-  return (cookie == ReadLink(path));
-}
-
-bool IsAppSandboxed() {
-#if defined(OS_MAC)
-  // NB: There is no sane API for this, we have to just guess by
-  // reading tea leaves
-  base::FilePath home_dir;
-  if (!base::PathService::Get(base::DIR_HOME, &home_dir)) {
-    return false;
-  }
-
-  return home_dir.value().find("Library/Containers") != std::string::npos;
-#else
-  return false;
-#endif  // defined(OS_MAC)
-}
-
-bool ConnectSocket(ScopedSocket* socket,
-                   const base::FilePath& socket_path,
-                   const base::FilePath& cookie_path) {
-  base::FilePath socket_target;
-  if (base::ReadSymbolicLink(socket_path, &socket_target)) {
-    // It's a symlink. Read the cookie.
-    base::FilePath cookie = ReadLink(cookie_path);
-    if (cookie.empty())
-      return false;
-    base::FilePath remote_cookie =
-        socket_target.DirName().Append(kSingletonCookieFilename);
-    // Verify the cookie before connecting.
-    if (!CheckCookie(remote_cookie, cookie))
-      return false;
-    // Now we know the directory was (at that point) created by the profile
-    // owner. Try to connect.
-    sockaddr_un addr;
-    SetupSockAddr(socket_target.value(), &addr);
-    int ret = HANDLE_EINTR(connect(
-        socket->fd(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)));
-    if (ret != 0)
-      return false;
-    // Check the cookie again. We only link in /tmp, which is sticky, so, if the
-    // directory is still correct, it must have been correct in-between when we
-    // connected. POSIX, sadly, lacks a connectat().
-    if (!CheckCookie(remote_cookie, cookie)) {
-      socket->Reset();
-      return false;
-    }
-    // Success!
-    return true;
-  } else if (errno == EINVAL) {
-    // It exists, but is not a symlink (or some other error we detect
-    // later). Just connect to it directly; this is an older version of Chrome.
-    sockaddr_un addr;
-    SetupSockAddr(socket_path.value(), &addr);
-    int ret = HANDLE_EINTR(connect(
-        socket->fd(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)));
-    return (ret == 0);
-  } else {
-    // File is missing, or other error.
-    if (errno != ENOENT)
-      PLOG(ERROR) << "readlink failed";
-    return false;
-  }
-}
-
-#if defined(OS_MAC)
-bool ReplaceOldSingletonLock(const base::FilePath& symlink_content,
-                             const base::FilePath& lock_path) {
-  // Try taking an flock(2) on the file. Failure means the lock is taken so we
-  // should quit.
-  base::ScopedFD lock_fd(HANDLE_EINTR(
-      open(lock_path.value().c_str(), O_RDWR | O_CREAT | O_SYMLINK, 0644)));
-  if (!lock_fd.is_valid()) {
-    PLOG(ERROR) << "Could not open singleton lock";
-    return false;
-  }
-
-  int rc = HANDLE_EINTR(flock(lock_fd.get(), LOCK_EX | LOCK_NB));
-  if (rc == -1) {
-    if (errno == EWOULDBLOCK) {
-      LOG(ERROR) << "Singleton lock held by old process.";
-    } else {
-      PLOG(ERROR) << "Error locking singleton lock";
-    }
-    return false;
-  }
-
-  // Successfully taking the lock means we can replace it with the a new symlink
-  // lock. We never flock() the lock file from now on. I.e. we assume that an
-  // old version of Chrome will not run with the same user data dir after this
-  // version has run.
-  if (!base::DeleteFile(lock_path)) {
-    PLOG(ERROR) << "Could not delete old singleton lock.";
-    return false;
-  }
-
-  return SymlinkPath(symlink_content, lock_path);
-}
-#endif  // defined(OS_MAC)
-
-}  // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-// ProcessSingleton::LinuxWatcher
-// A helper class for a Linux specific implementation of the process singleton.
-// This class sets up a listener on the singleton socket and handles parsing
-// messages that come in on the singleton socket.
-class ProcessSingleton::LinuxWatcher
-    : public base::RefCountedThreadSafe<ProcessSingleton::LinuxWatcher,
-                                        BrowserThread::DeleteOnIOThread> {
- public:
-  // A helper class to read message from an established socket.
-  class SocketReader {
-   public:
-    SocketReader(ProcessSingleton::LinuxWatcher* parent,
-                 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
-                 int fd)
-        : parent_(parent), ui_task_runner_(ui_task_runner), fd_(fd) {
-      DCHECK_CURRENTLY_ON(BrowserThread::IO);
-      // Wait for reads.
-      fd_watch_controller_ = base::FileDescriptorWatcher::WatchReadable(
-          fd, base::BindRepeating(&SocketReader::OnSocketCanReadWithoutBlocking,
-                                  base::Unretained(this)));
-      // If we haven't completed in a reasonable amount of time, give up.
-      timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kTimeoutInSeconds),
-                   this, &SocketReader::CleanupAndDeleteSelf);
-    }
-
-    ~SocketReader() { CloseSocket(fd_); }
-
-    // Finish handling the incoming message by optionally sending back an ACK
-    // message and removing this SocketReader.
-    void FinishWithACK(const char* message, size_t length);
-
-   private:
-    void OnSocketCanReadWithoutBlocking();
-
-    void CleanupAndDeleteSelf() {
-      DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-      parent_->RemoveSocketReader(this);
-      // We're deleted beyond this point.
-    }
-
-    // Controls watching |fd_|.
-    std::unique_ptr<base::FileDescriptorWatcher::Controller>
-        fd_watch_controller_;
-
-    // The ProcessSingleton::LinuxWatcher that owns us.
-    ProcessSingleton::LinuxWatcher* const parent_ = nullptr;
-
-    // A reference to the UI task runner.
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
-
-    // The file descriptor we're reading.
-    const int fd_ = -1;
-
-    // Store the message in this buffer.
-    char buf_[kMaxMessageLength];
-
-    // Tracks the number of bytes we've read in case we're getting partial
-    // reads.
-    size_t bytes_read_ = 0;
-
-    base::OneShotTimer timer_;
-
-    DISALLOW_COPY_AND_ASSIGN(SocketReader);
-  };
-
-  // We expect to only be constructed on the UI thread.
-  explicit LinuxWatcher(ProcessSingleton* parent)
-      : ui_task_runner_(base::ThreadTaskRunnerHandle::Get()), parent_(parent) {}
-
-  // Start listening for connections on the socket.  This method should be
-  // called from the IO thread.
-  void StartListening(int socket);
-
-  // This method determines if we should use the same process and if we should,
-  // opens a new browser tab.  This runs on the UI thread.
-  // |reader| is for sending back ACK message.
-  void HandleMessage(const std::string& current_dir,
-                     const std::vector<std::string>& argv,
-                     SocketReader* reader);
-
- private:
-  friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
-  friend class base::DeleteHelper<ProcessSingleton::LinuxWatcher>;
-
-  ~LinuxWatcher() { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
-
-  void OnSocketCanReadWithoutBlocking(int socket);
-
-  // Removes and deletes the SocketReader.
-  void RemoveSocketReader(SocketReader* reader);
-
-  std::unique_ptr<base::FileDescriptorWatcher::Controller> socket_watcher_;
-
-  // A reference to the UI message loop (i.e., the message loop we were
-  // constructed on).
-  scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
-
-  // The ProcessSingleton that owns us.
-  ProcessSingleton* const parent_;
-
-  std::set<std::unique_ptr<SocketReader>> readers_;
-
-  DISALLOW_COPY_AND_ASSIGN(LinuxWatcher);
-};
-
-void ProcessSingleton::LinuxWatcher::OnSocketCanReadWithoutBlocking(
-    int socket) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // Accepting incoming client.
-  sockaddr_un from;
-  socklen_t from_len = sizeof(from);
-  int connection_socket = HANDLE_EINTR(
-      accept(socket, reinterpret_cast<sockaddr*>(&from), &from_len));
-  if (-1 == connection_socket) {
-    PLOG(ERROR) << "accept() failed";
-    return;
-  }
-  DCHECK(base::SetNonBlocking(connection_socket))
-      << "Failed to make non-blocking socket.";
-  readers_.insert(
-      std::make_unique<SocketReader>(this, ui_task_runner_, connection_socket));
-}
-
-void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // Watch for client connections on this socket.
-  socket_watcher_ = base::FileDescriptorWatcher::WatchReadable(
-      socket, base::BindRepeating(&LinuxWatcher::OnSocketCanReadWithoutBlocking,
-                                  base::Unretained(this), socket));
-}
-
-void ProcessSingleton::LinuxWatcher::HandleMessage(
-    const std::string& current_dir,
-    const std::vector<std::string>& argv,
-    SocketReader* reader) {
-  DCHECK(ui_task_runner_->BelongsToCurrentThread());
-  DCHECK(reader);
-
-  if (parent_->notification_callback_.Run(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 {
-    LOG(WARNING) << "Not handling interprocess notification as browser"
-                    " is shutting down";
-    // Send back "SHUTDOWN" message, so that the client process can start up
-    // without killing this process.
-    reader->FinishWithACK(kShutdownToken, base::size(kShutdownToken) - 1);
-    return;
-  }
-}
-
-void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(reader);
-  auto it = std::find_if(readers_.begin(), readers_.end(),
-                         [reader](const std::unique_ptr<SocketReader>& ptr) {
-                           return ptr.get() == reader;
-                         });
-  readers_.erase(it);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// ProcessSingleton::LinuxWatcher::SocketReader
-//
-
-void ProcessSingleton::LinuxWatcher::SocketReader::
-    OnSocketCanReadWithoutBlocking() {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  while (bytes_read_ < sizeof(buf_)) {
-    ssize_t rv =
-        HANDLE_EINTR(read(fd_, buf_ + bytes_read_, sizeof(buf_) - bytes_read_));
-    if (rv < 0) {
-      if (errno != EAGAIN && errno != EWOULDBLOCK) {
-        PLOG(ERROR) << "read() failed";
-        CloseSocket(fd_);
-        return;
-      } else {
-        // It would block, so we just return and continue to watch for the next
-        // opportunity to read.
-        return;
-      }
-    } else if (!rv) {
-      // No more data to read.  It's time to process the message.
-      break;
-    } else {
-      bytes_read_ += rv;
-    }
-  }
-
-  // Validate the message.  The shortest message is kStartToken\0x\0x
-  const size_t kMinMessageLength = base::size(kStartToken) + 4;
-  if (bytes_read_ < kMinMessageLength) {
-    buf_[bytes_read_] = 0;
-    LOG(ERROR) << "Invalid socket message (wrong length):" << buf_;
-    CleanupAndDeleteSelf();
-    return;
-  }
-
-  std::string str(buf_, bytes_read_);
-  std::vector<std::string> tokens =
-      base::SplitString(str, std::string(1, kTokenDelimiter),
-                        base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  if (tokens.size() < 3 || tokens[0] != kStartToken) {
-    LOG(ERROR) << "Wrong message format: " << str;
-    CleanupAndDeleteSelf();
-    return;
-  }
-
-  // Stop the expiration timer to prevent this SocketReader object from being
-  // terminated unexpectly.
-  timer_.Stop();
-
-  std::string current_dir = tokens[1];
-  // Remove the first two tokens.  The remaining tokens should be the command
-  // line argv array.
-  tokens.erase(tokens.begin());
-  tokens.erase(tokens.begin());
-
-  // 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));
-  fd_watch_controller_.reset();
-
-  // LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
-  // object by invoking SocketReader::FinishWithACK().
-}
-
-void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
-    const char* message,
-    size_t length) {
-  if (message && length) {
-    // Not necessary to care about the return value.
-    WriteToSocket(fd_, message, length);
-  }
-
-  if (shutdown(fd_, SHUT_WR) < 0)
-    PLOG(ERROR) << "shutdown() failed";
-
-  base::PostTask(
-      FROM_HERE, {BrowserThread::IO},
-      base::BindOnce(&ProcessSingleton::LinuxWatcher::RemoveSocketReader,
-                     parent_, this));
-  // We will be deleted once the posted RemoveSocketReader task runs.
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// ProcessSingleton
-//
-ProcessSingleton::ProcessSingleton(
-    const base::FilePath& user_data_dir,
-    const NotificationCallback& notification_callback)
-    : notification_callback_(notification_callback),
-      current_pid_(base::GetCurrentProcId()) {
-  // The user_data_dir may have not been created yet.
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  base::CreateDirectoryAndGetError(user_data_dir, nullptr);
-
-  socket_path_ = user_data_dir.Append(kSingletonSocketFilename);
-  lock_path_ = user_data_dir.Append(kSingletonLockFilename);
-  cookie_path_ = user_data_dir.Append(kSingletonCookieFilename);
-
-  kill_callback_ = base::BindRepeating(&ProcessSingleton::KillProcess,
-                                       base::Unretained(this));
-}
-
-ProcessSingleton::~ProcessSingleton() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Manually free resources with IO explicitly allowed.
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  watcher_ = nullptr;
-  ignore_result(socket_dir_.Delete());
-}
-
-ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
-  return NotifyOtherProcessWithTimeout(
-      *base::CommandLine::ForCurrentProcess(), kRetryAttempts,
-      base::TimeDelta::FromSeconds(kTimeoutInSeconds), true);
-}
-
-ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
-    const base::CommandLine& cmd_line,
-    int retry_attempts,
-    const base::TimeDelta& timeout,
-    bool kill_unresponsive) {
-  DCHECK_GE(retry_attempts, 0);
-  DCHECK_GE(timeout.InMicroseconds(), 0);
-
-  base::TimeDelta sleep_interval = timeout / retry_attempts;
-
-  ScopedSocket socket;
-  for (int retries = 0; retries <= retry_attempts; ++retries) {
-    // Try to connect to the socket.
-    if (ConnectSocket(&socket, socket_path_, cookie_path_))
-      break;
-
-    // If we're in a race with another process, they may be in Create() and have
-    // created the lock but not attached to the socket.  So we check if the
-    // process with the pid from the lockfile is currently running and is a
-    // chrome browser.  If so, we loop and try again for |timeout|.
-
-    std::string hostname;
-    int pid;
-    if (!ParseLockPath(lock_path_, &hostname, &pid)) {
-      // No lockfile exists.
-      return PROCESS_NONE;
-    }
-
-    if (hostname.empty()) {
-      // Invalid lockfile.
-      UnlinkPath(lock_path_);
-      return PROCESS_NONE;
-    }
-
-    if (hostname != net::GetHostName() && !IsChromeProcess(pid)) {
-      // Locked by process on another host. If the user selected to unlock
-      // the profile, try to continue; otherwise quit.
-      if (DisplayProfileInUseError(lock_path_, hostname, pid)) {
-        UnlinkPath(lock_path_);
-        return PROCESS_NONE;
-      }
-      return PROFILE_IN_USE;
-    }
-
-    if (!IsChromeProcess(pid)) {
-      // Orphaned lockfile (no process with pid, or non-chrome process.)
-      UnlinkPath(lock_path_);
-      return PROCESS_NONE;
-    }
-
-    if (IsSameChromeInstance(pid)) {
-      // Orphaned lockfile (pid is part of same chrome instance we are, even
-      // though we haven't tried to create a lockfile yet).
-      UnlinkPath(lock_path_);
-      return PROCESS_NONE;
-    }
-
-    if (retries == retry_attempts) {
-      // Retries failed.  Kill the unresponsive chrome process and continue.
-      if (!kill_unresponsive || !KillProcessByLockPath())
-        return PROFILE_IN_USE;
-      return PROCESS_NONE;
-    }
-
-    base::PlatformThread::Sleep(sleep_interval);
-  }
-
-  timeval socket_timeout = TimeDeltaToTimeVal(timeout);
-  setsockopt(socket.fd(), SOL_SOCKET, SO_SNDTIMEO, &socket_timeout,
-             sizeof(socket_timeout));
-
-  // Found another process, prepare our command line
-  // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
-  std::string to_send(kStartToken);
-  to_send.push_back(kTokenDelimiter);
-
-  base::FilePath current_dir;
-  if (!base::PathService::Get(base::DIR_CURRENT, &current_dir))
-    return PROCESS_NONE;
-  to_send.append(current_dir.value());
-
-  const std::vector<std::string>& argv = electron::ElectronCommandLine::argv();
-  for (const auto& arg : argv) {
-    to_send.push_back(kTokenDelimiter);
-    to_send.append(arg);
-  }
-
-  // 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.
-    if (!kill_unresponsive || !KillProcessByLockPath())
-      return PROFILE_IN_USE;
-    return PROCESS_NONE;
-  }
-
-  if (shutdown(socket.fd(), SHUT_WR) < 0)
-    PLOG(ERROR) << "shutdown() failed";
-
-  // Read ACK message from the other process. It might be blocked for a certain
-  // timeout, to make sure the other process has enough time to return ACK.
-  char buf[kMaxACKMessageLength + 1];
-  ssize_t len = ReadFromSocket(socket.fd(), buf, kMaxACKMessageLength, timeout);
-
-  // Failed to read ACK, the other process might have been frozen.
-  if (len <= 0) {
-    if (!kill_unresponsive || !KillProcessByLockPath())
-      return PROFILE_IN_USE;
-    return PROCESS_NONE;
-  }
-
-  buf[len] = '\0';
-  if (strncmp(buf, kShutdownToken, base::size(kShutdownToken) - 1) == 0) {
-    // The other process is shutting down, it's safe to start a new process.
-    return PROCESS_NONE;
-  } else if (strncmp(buf, kACKToken, base::size(kACKToken) - 1) == 0) {
-#if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) && !defined(OS_CHROMEOS)
-    // Likely NULL in unit tests.
-    views::LinuxUI* linux_ui = views::LinuxUI::instance();
-    if (linux_ui)
-      linux_ui->NotifyWindowManagerStartupComplete();
-#endif
-
-    // Assume the other process is handling the request.
-    return PROCESS_NOTIFIED;
-  }
-
-  NOTREACHED() << "The other process returned unknown message: " << buf;
-  return PROCESS_NOTIFIED;
-}
-
-ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
-  return NotifyOtherProcessWithTimeoutOrCreate(
-      *base::CommandLine::ForCurrentProcess(), kRetryAttempts,
-      base::TimeDelta::FromSeconds(kTimeoutInSeconds));
-}
-
-void ProcessSingleton::StartListeningOnSocket() {
-  watcher_ = base::MakeRefCounted<LinuxWatcher>(this);
-  base::PostTask(FROM_HERE, {BrowserThread::IO},
-                 base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
-                                watcher_, sock_));
-}
-
-void ProcessSingleton::OnBrowserReady() {
-  if (listen_on_ready_) {
-    StartListeningOnSocket();
-    listen_on_ready_ = false;
-  }
-}
-
-ProcessSingleton::NotifyResult
-ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
-    const base::CommandLine& command_line,
-    int retry_attempts,
-    const base::TimeDelta& timeout) {
-  const base::TimeTicks begin_ticks = base::TimeTicks::Now();
-  NotifyResult result = NotifyOtherProcessWithTimeout(
-      command_line, retry_attempts, timeout, true);
-  if (result != PROCESS_NONE) {
-    if (result == PROCESS_NOTIFIED) {
-      UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify",
-                                 base::TimeTicks::Now() - begin_ticks);
-    } else {
-      UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure",
-                                 base::TimeTicks::Now() - begin_ticks);
-    }
-    return result;
-  }
-
-  if (Create()) {
-    UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate",
-                               base::TimeTicks::Now() - begin_ticks);
-    return PROCESS_NONE;
-  }
-
-  // If the Create() failed, try again to notify. (It could be that another
-  // instance was starting at the same time and managed to grab the lock before
-  // we did.)
-  // This time, we don't want to kill anything if we aren't successful, since we
-  // aren't going to try to take over the lock ourselves.
-  result = NotifyOtherProcessWithTimeout(command_line, retry_attempts, timeout,
-                                         false);
-
-  if (result == PROCESS_NOTIFIED) {
-    UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify",
-                               base::TimeTicks::Now() - begin_ticks);
-  } else {
-    UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure",
-                               base::TimeTicks::Now() - begin_ticks);
-  }
-
-  if (result != PROCESS_NONE)
-    return result;
-
-  return LOCK_ERROR;
-}
-
-void ProcessSingleton::OverrideCurrentPidForTesting(base::ProcessId pid) {
-  current_pid_ = pid;
-}
-
-void ProcessSingleton::OverrideKillCallbackForTesting(
-    const base::RepeatingCallback<void(int)>& callback) {
-  kill_callback_ = callback;
-}
-
-void ProcessSingleton::DisablePromptForTesting() {
-  g_disable_prompt = true;
-}
-
-bool ProcessSingleton::Create() {
-  base::ThreadRestrictions::ScopedAllowIO allow_io;
-  int sock;
-  sockaddr_un addr;
-
-  // The symlink lock is pointed to the hostname and process id, so other
-  // processes can find it out.
-  base::FilePath symlink_content(base::StringPrintf(
-      "%s%c%u", net::GetHostName().c_str(), kLockDelimiter, current_pid_));
-
-  // Create symbol link before binding the socket, to ensure only one instance
-  // can have the socket open.
-  if (!SymlinkPath(symlink_content, lock_path_)) {
-    // TODO(jackhou): Remove this case once this code is stable on Mac.
-    // http://crbug.com/367612
-#if defined(OS_MAC)
-    // On Mac, an existing non-symlink lock file means the lock could be held by
-    // the old process singleton code. If we can successfully replace the lock,
-    // continue as normal.
-    if (base::IsLink(lock_path_) ||
-        !ReplaceOldSingletonLock(symlink_content, lock_path_)) {
-      return false;
-    }
-#else
-    // If we failed to create the lock, most likely another instance won the
-    // startup race.
-    return false;
-#endif
-  }
-
-  if (IsAppSandboxed()) {
-    // For sandboxed applications, the tmp dir could be too long to fit
-    // addr->sun_path, so we need to make it as short as possible.
-    base::FilePath tmp_dir;
-    if (!base::GetTempDir(&tmp_dir)) {
-      LOG(ERROR) << "Failed to get temporary directory.";
-      return false;
-    }
-    if (!socket_dir_.Set(tmp_dir.Append("S"))) {
-      LOG(ERROR) << "Failed to set socket directory.";
-      return false;
-    }
-  } else {
-    // Create the socket file somewhere in /tmp which is usually mounted as a
-    // normal filesystem. Some network filesystems (notably AFS) are screwy and
-    // do not support Unix domain sockets.
-    if (!socket_dir_.CreateUniqueTempDir()) {
-      LOG(ERROR) << "Failed to create socket directory.";
-      return false;
-    }
-  }
-
-  // Check that the directory was created with the correct permissions.
-  int dir_mode = 0;
-  CHECK(base::GetPosixFilePermissions(socket_dir_.GetPath(), &dir_mode) &&
-        dir_mode == base::FILE_PERMISSION_USER_MASK)
-      << "Temp directory mode is not 700: " << std::oct << dir_mode;
-
-  // Setup the socket symlink and the two cookies.
-  base::FilePath socket_target_path =
-      socket_dir_.GetPath().Append(kSingletonSocketFilename);
-  base::FilePath cookie(GenerateCookie());
-  base::FilePath remote_cookie_path =
-      socket_dir_.GetPath().Append(kSingletonCookieFilename);
-  UnlinkPath(socket_path_);
-  UnlinkPath(cookie_path_);
-  if (!SymlinkPath(socket_target_path, socket_path_) ||
-      !SymlinkPath(cookie, cookie_path_) ||
-      !SymlinkPath(cookie, remote_cookie_path)) {
-    // We've already locked things, so we can't have lost the startup race,
-    // but something doesn't like us.
-    LOG(ERROR) << "Failed to create symlinks.";
-    if (!socket_dir_.Delete())
-      LOG(ERROR) << "Encountered a problem when deleting socket directory.";
-    return false;
-  }
-
-  SetupSocket(socket_target_path.value(), &sock, &addr);
-
-  if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
-    PLOG(ERROR) << "Failed to bind() " << socket_target_path.value();
-    CloseSocket(sock);
-    return false;
-  }
-
-  if (listen(sock, 5) < 0)
-    NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
-
-  sock_ = sock;
-
-  if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
-    StartListeningOnSocket();
-  } else {
-    listen_on_ready_ = true;
-  }
-
-  return true;
-}
-
-void ProcessSingleton::Cleanup() {
-  UnlinkPath(socket_path_);
-  UnlinkPath(cookie_path_);
-  UnlinkPath(lock_path_);
-}
-
-bool ProcessSingleton::IsSameChromeInstance(pid_t pid) {
-  pid_t cur_pid = current_pid_;
-  while (pid != cur_pid) {
-    pid = base::GetParentProcessId(pid);
-    if (pid < 0)
-      return false;
-    if (!IsChromeProcess(pid))
-      return false;
-  }
-  return true;
-}
-
-bool ProcessSingleton::KillProcessByLockPath() {
-  std::string hostname;
-  int pid;
-  ParseLockPath(lock_path_, &hostname, &pid);
-
-  if (!hostname.empty() && hostname != net::GetHostName()) {
-    return DisplayProfileInUseError(lock_path_, hostname, pid);
-  }
-  UnlinkPath(lock_path_);
-
-  if (IsSameChromeInstance(pid))
-    return true;
-
-  if (pid > 0) {
-    kill_callback_.Run(pid);
-    return true;
-  }
-
-  LOG(ERROR) << "Failed to extract pid from path: " << lock_path_.value();
-  return true;
-}
-
-void ProcessSingleton::KillProcess(int pid) {
-  // TODO([email protected]): Is SIGKILL ok?
-  int rv = kill(static_cast<base::ProcessHandle>(pid), SIGKILL);
-  // ESRCH = No Such Process (can happen if the other process is already in
-  // progress of shutting down and finishes before we try to kill it).
-  DCHECK(rv == 0 || errno == ESRCH)
-      << "Error killing process: " << base::safe_strerror(errno);
-}

+ 0 - 315
chromium_src/chrome/browser/process_singleton_win.cc

@@ -1,315 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/process_singleton.h"
-
-#include <windows.h>
-#include <shellapi.h>
-
-#include "base/base_paths.h"
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/process/process.h"
-#include "base/process/process_info.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "base/win/registry.h"
-#include "base/win/scoped_handle.h"
-#include "base/win/windows_version.h"
-#include "chrome/browser/win/chrome_process_finder.h"
-#include "content/public/common/result_codes.h"
-#include "net/base/escape.h"
-#include "ui/gfx/win/hwnd_util.h"
-
-namespace {
-
-const char kLockfile[] = "lockfile";
-
-// A helper class that acquires the given |mutex| while the AutoLockMutex is in
-// scope.
-class AutoLockMutex {
- public:
-  explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
-    DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
-    DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
-  }
-
-  ~AutoLockMutex() {
-    BOOL released = ::ReleaseMutex(mutex_);
-    DPCHECK(released);
-  }
-
- private:
-  HANDLE mutex_;
-  DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
-};
-
-// A helper class that releases the given |mutex| while the AutoUnlockMutex is
-// in scope and immediately re-acquires it when going out of scope.
-class AutoUnlockMutex {
- public:
-  explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
-    BOOL released = ::ReleaseMutex(mutex_);
-    DPCHECK(released);
-  }
-
-  ~AutoUnlockMutex() {
-    DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
-    DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
-  }
-
- private:
-  HANDLE mutex_;
-  DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
-};
-
-// Checks the visibility of the enumerated window and signals once a visible
-// window has been found.
-BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
-  bool* result = reinterpret_cast<bool*>(param);
-  *result = ::IsWindowVisible(window) != 0;
-  // Stops enumeration if a visible window has been found.
-  return !*result;
-}
-
-bool ParseCommandLine(const COPYDATASTRUCT* cds,
-                      base::CommandLine::StringVector* parsed_command_line,
-                      base::FilePath* current_directory) {
-  // 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).
-  static const int min_message_size = 7;
-  if (cds->cbData < min_message_size * sizeof(wchar_t) ||
-      cds->cbData % sizeof(wchar_t) != 0) {
-    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
-    return false;
-  }
-
-  // We split the string into 4 parts on NULLs.
-  DCHECK(cds->lpData);
-  const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
-                         cds->cbData / sizeof(wchar_t));
-  const std::wstring::size_type first_null = msg.find_first_of(L'\0');
-  if (first_null == 0 || first_null == std::wstring::npos) {
-    // no NULL byte, don't know what to do
-    LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length()
-                 << ", first null = " << first_null;
-    return false;
-  }
-
-  // Decode the command, which is everything until the first NULL.
-  if (msg.substr(0, first_null) == L"START") {
-    // Another instance is starting parse the command line & do what it would
-    // have done.
-    VLOG(1) << "Handling STARTUP request from another process";
-    const std::wstring::size_type second_null =
-        msg.find_first_of(L'\0', first_null + 1);
-    if (second_null == std::wstring::npos || first_null == msg.length() - 1 ||
-        second_null == msg.length()) {
-      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
-                      "parts separated by NULLs";
-      return false;
-    }
-
-    // Get current directory.
-    *current_directory =
-        base::FilePath(msg.substr(first_null + 1, second_null - first_null));
-
-    const std::wstring::size_type third_null =
-        msg.find_first_of(L'\0', second_null + 1);
-    if (third_null == std::wstring::npos || third_null == msg.length()) {
-      LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
-                      "parts separated by NULLs";
-    }
-
-    // Get command line.
-    const std::wstring cmd_line =
-        msg.substr(second_null + 1, third_null - second_null);
-    *parsed_command_line = base::CommandLine::FromString(cmd_line).argv();
-    return true;
-  }
-  return false;
-}
-
-bool ProcessLaunchNotification(
-    const ProcessSingleton::NotificationCallback& notification_callback,
-    UINT message,
-    WPARAM wparam,
-    LPARAM lparam,
-    LRESULT* result) {
-  if (message != WM_COPYDATA)
-    return false;
-
-  // Handle the WM_COPYDATA message from another process.
-  const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
-
-  base::CommandLine::StringVector parsed_command_line;
-  base::FilePath current_directory;
-  if (!ParseCommandLine(cds, &parsed_command_line, &current_directory)) {
-    *result = TRUE;
-    return true;
-  }
-
-  *result = notification_callback.Run(parsed_command_line, current_directory)
-                ? TRUE
-                : FALSE;
-  return true;
-}
-
-bool TerminateAppWithError() {
-  // TODO: This is called when the secondary process can't ping the primary
-  // process. Need to find out what to do here.
-  return false;
-}
-
-}  // namespace
-
-ProcessSingleton::ProcessSingleton(
-    const base::FilePath& user_data_dir,
-    const NotificationCallback& notification_callback)
-    : notification_callback_(notification_callback),
-      user_data_dir_(user_data_dir),
-      should_kill_remote_process_callback_(
-          base::BindRepeating(&TerminateAppWithError)) {
-  // The user_data_dir may have not been created yet.
-  base::CreateDirectoryAndGetError(user_data_dir, nullptr);
-}
-
-ProcessSingleton::~ProcessSingleton() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (lock_file_ != INVALID_HANDLE_VALUE)
-    ::CloseHandle(lock_file_);
-}
-
-// Code roughly based on Mozilla.
-ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
-  if (is_virtualized_)
-    return PROCESS_NOTIFIED;  // We already spawned the process in this case.
-  if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
-    return LOCK_ERROR;
-  } else if (!remote_window_) {
-    return PROCESS_NONE;
-  }
-
-  switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
-    case chrome::NOTIFY_SUCCESS:
-      return PROCESS_NOTIFIED;
-    case chrome::NOTIFY_FAILED:
-      remote_window_ = NULL;
-      return PROCESS_NONE;
-    case chrome::NOTIFY_WINDOW_HUNG:
-      // Fall through and potentially terminate the hung browser.
-      break;
-  }
-
-  DWORD process_id = 0;
-  DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
-  if (!thread_id || !process_id) {
-    remote_window_ = NULL;
-    return PROCESS_NONE;
-  }
-  base::Process process = base::Process::Open(process_id);
-
-  // The window is hung. Scan for every window to find a visible one.
-  bool visible_window = false;
-  ::EnumThreadWindows(thread_id, &BrowserWindowEnumeration,
-                      reinterpret_cast<LPARAM>(&visible_window));
-
-  // If there is a visible browser window, ask the user before killing it.
-  if (visible_window && !should_kill_remote_process_callback_.Run()) {
-    // The user denied. Quit silently.
-    return PROCESS_NOTIFIED;
-  }
-
-  // Time to take action. Kill the browser process.
-  process.Terminate(content::RESULT_CODE_HUNG, true);
-  remote_window_ = NULL;
-  return PROCESS_NONE;
-}
-
-ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
-  ProcessSingleton::NotifyResult result = PROCESS_NONE;
-  if (!Create()) {
-    result = NotifyOtherProcess();
-    if (result == PROCESS_NONE)
-      result = PROFILE_IN_USE;
-  }
-  return result;
-}
-
-void ProcessSingleton::StartListeningOnSocket() {}
-void ProcessSingleton::OnBrowserReady() {}
-
-// Look for a Chrome instance that uses the same profile directory. If there
-// isn't one, create a message window with its title set to the profile
-// directory path.
-bool ProcessSingleton::Create() {
-  static const wchar_t kMutexName[] = L"Local\\AtomProcessSingletonStartup!";
-
-  remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
-  if (!remote_window_) {
-    // Make sure we will be the one and only process creating the window.
-    // We use a named Mutex since we are protecting against multi-process
-    // access. As documented, it's clearer to NOT request ownership on creation
-    // since it isn't guaranteed we will get it. It is better to create it
-    // without ownership and explicitly get the ownership afterward.
-    base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
-    if (!only_me.IsValid()) {
-      DPLOG(FATAL) << "CreateMutex failed";
-      return false;
-    }
-
-    AutoLockMutex auto_lock_only_me(only_me.Get());
-
-    // We now own the mutex so we are the only process that can create the
-    // window at this time, but we must still check if someone created it
-    // between the time where we looked for it above and the time the mutex
-    // was given to us.
-    remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
-    if (!remote_window_) {
-      // We have to make sure there is no Chrome instance running on another
-      // machine that uses the same profile.
-      base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
-      lock_file_ =
-          ::CreateFile(lock_file_path.value().c_str(), GENERIC_WRITE,
-                       FILE_SHARE_READ, NULL, CREATE_ALWAYS,
-                       FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
-      DWORD error = ::GetLastError();
-      LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
-                          error == ERROR_ALREADY_EXISTS)
-          << "Lock file exists but is writable.";
-      LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
-          << "Lock file can not be created! Error code: " << error;
-
-      if (lock_file_ != INVALID_HANDLE_VALUE) {
-        // 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.
-        bool result =
-            window_.CreateNamed(base::BindRepeating(&ProcessLaunchNotification,
-                                                    notification_callback_),
-                                user_data_dir_.value());
-
-        // NB: Ensure that if the primary app gets started as elevated
-        // admin inadvertently, secondary windows running not as elevated
-        // will still be able to send messages
-        ::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW,
-                                      NULL);
-        CHECK(result && window_.hwnd());
-      }
-    }
-  }
-
-  return window_.hwnd() != NULL;
-}
-
-void ProcessSingleton::Cleanup() {}
-
-void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
-    const ShouldKillRemoteProcessCallback& display_dialog_callback) {
-  should_kill_remote_process_callback_ = display_dialog_callback;
-}

+ 1 - 6
filenames.gni

@@ -56,13 +56,9 @@ filenames = {
     "shell/browser/ui/x/x_window_utils.h",
   ]
 
-  lib_sources_posix = [
-    "chromium_src/chrome/browser/process_singleton_posix.cc",
-    "shell/browser/electron_browser_main_parts_posix.cc",
-  ]
+  lib_sources_posix = [ "shell/browser/electron_browser_main_parts_posix.cc" ]
 
   lib_sources_win = [
-    "chromium_src/chrome/browser/process_singleton_win.cc",
     "shell/browser/api/electron_api_power_monitor_win.cc",
     "shell/browser/api/electron_api_system_preferences_win.cc",
     "shell/browser/browser_win.cc",
@@ -236,7 +232,6 @@ filenames = {
   ]
 
   lib_sources = [
-    "chromium_src/chrome/browser/process_singleton.h",
     "shell/app/command_line_args.cc",
     "shell/app/command_line_args.h",
     "shell/app/electron_content_client.cc",

+ 1 - 0
patches/chromium/.patches

@@ -107,3 +107,4 @@ fix_media_key_usage_with_globalshortcuts.patch
 feat_expose_raw_response_headers_from_urlloader.patch
 chore_do_not_use_chrome_windows_in_cryptotoken_webrequestsender.patch
 fix_chrome_root_store_codegen_for_cross-compile_builds.patch
+process_singleton.patch

+ 307 - 0
patches/chromium/process_singleton.patch

@@ -0,0 +1,307 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Raymond Zhao <[email protected]>
+Date: Wed, 18 Aug 2021 08:24:10 -0700
+Subject: Convert Electron ProcessSingleton changes to patch
+
+This patch applies Electron ProcessSingleton changes
+onto the Chromium files, so that Electron doesn't need to maintain
+separate copies of those files.
+
+This patch adds a few changes to the Chromium code:
+1. It adds a parameter `program_name` to the Windows constructor, making
+   the generated mutex name on the Windows-side program-dependent,
+   rather than shared between all Electron applications.
+2. It adds an `IsAppSandboxed` check for macOS so that
+   sandboxed applications generate shorter temp paths.
+3. It adds a `ChangeWindowMessageFilterEx` call to the Windows
+   implementation, along with a parameter `is_app_sandboxed` in the
+   constructor, to handle the case when the primary app is run with
+   admin permissions.
+4. It adds an `OnBrowserReady` function to allow
+   `requestSingleInstanceLock` to start listening to the socket later
+   in the posix implementation. This function is necessary, because
+   otherwise, users calling `requestSingleInstanceLock` create a
+   `ProcessSingleton` instance that tries to connect to the socket
+   before the browser thread is ready.
+
+diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
+index 0291db0f919aa7868d345f4f0712c42c6ad7ee17..9418bbacb11094628c4468d0a7b735a0f742e5f2 100644
+--- a/chrome/browser/process_singleton.h
++++ b/chrome/browser/process_singleton.h
+@@ -103,8 +103,15 @@ class ProcessSingleton {
+       base::RepeatingCallback<bool(const base::CommandLine& command_line,
+                                    const base::FilePath& current_directory)>;
+ 
++#if defined(OS_WIN)
++  ProcessSingleton(const std::string& program_name,
++                   const base::FilePath& user_data_dir,
++                   bool is_sandboxed,
++                   const NotificationCallback& notification_callback);
++#else
+   ProcessSingleton(const base::FilePath& user_data_dir,
+                    const NotificationCallback& notification_callback);
++#endif
+   ~ProcessSingleton();
+ 
+   // Notify another process, if available. Otherwise sets ourselves as the
+@@ -115,6 +122,8 @@ class ProcessSingleton {
+   // TODO(brettw): Make the implementation of this method non-platform-specific
+   // by making Linux re-use the Windows implementation.
+   NotifyResult NotifyOtherProcessOrCreate();
++  void StartListeningOnSocket();
++  void OnBrowserReady();
+ 
+   // Sets ourself up as the singleton instance.  Returns true on success.  If
+   // false is returned, we are not the singleton instance and the caller must
+@@ -170,6 +179,8 @@ class ProcessSingleton {
+ #if defined(OS_WIN)
+   bool EscapeVirtualization(const base::FilePath& user_data_dir);
+ 
++  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.
+   base::win::MessageWindow window_;  // The message-only window.
+   bool is_virtualized_;  // Stuck inside Microsoft Softricity VM environment.
+@@ -219,6 +230,8 @@ class ProcessSingleton {
+   // because it posts messages between threads.
+   class LinuxWatcher;
+   scoped_refptr<LinuxWatcher> watcher_;
++  int sock_ = -1;
++  bool listen_on_ready_ = false;
+ #endif
+ 
+ #if defined(OS_MAC)
+diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
+index dc9c1b76a1c7c8b3fa83fc83788eef36d2cfa4a5..31b387ca3cae53c94d8a7baefaeb17f3dbffe5e0 100644
+--- a/chrome/browser/process_singleton_posix.cc
++++ b/chrome/browser/process_singleton_posix.cc
+@@ -80,6 +80,7 @@
+ #include "base/strings/stringprintf.h"
+ #include "base/strings/sys_string_conversions.h"
+ #include "base/strings/utf_string_conversions.h"
++#include "base/task/post_task.h"
+ #include "base/threading/platform_thread.h"
+ #include "base/threading/thread_task_runner_handle.h"
+ #include "base/time/time.h"
+@@ -95,9 +96,11 @@
+ #include "net/base/network_interfaces.h"
+ #include "ui/base/l10n/l10n_util.h"
+ 
++#if 0
+ #if defined(OS_LINUX) || defined(OS_CHROMEOS)
+ #include "chrome/browser/ui/process_singleton_dialog_linux.h"
+ #endif
++#endif
+ 
+ #if defined(TOOLKIT_VIEWS) && \
+     (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+@@ -289,6 +292,9 @@ bool SymlinkPath(const base::FilePath& target, const base::FilePath& path) {
+ bool DisplayProfileInUseError(const base::FilePath& lock_path,
+                               const std::string& hostname,
+                               int pid) {
++  return true;
++
++#if 0
+   std::u16string error = l10n_util::GetStringFUTF16(
+       IDS_PROFILE_IN_USE_POSIX, base::NumberToString16(pid),
+       base::ASCIIToUTF16(hostname));
+@@ -308,6 +314,7 @@ bool DisplayProfileInUseError(const base::FilePath& lock_path,
+ 
+   NOTREACHED();
+   return false;
++#endif
+ }
+ 
+ bool IsChromeProcess(pid_t pid) {
+@@ -348,6 +355,21 @@ bool CheckCookie(const base::FilePath& path, const base::FilePath& cookie) {
+   return (cookie == ReadLink(path));
+ }
+ 
++bool IsAppSandboxed() {
++#if defined(OS_MAC)
++  // NB: There is no sane API for this, we have to just guess by
++  // reading tea leaves
++  base::FilePath home_dir;
++  if (!base::PathService::Get(base::DIR_HOME, &home_dir)) {
++    return false;
++  }
++
++  return home_dir.value().find("Library/Containers") != std::string::npos;
++#else
++  return false;
++#endif  // defined(OS_MAC)
++}
++
+ bool ConnectSocket(ScopedSocket* socket,
+                    const base::FilePath& socket_path,
+                    const base::FilePath& cookie_path) {
+@@ -727,6 +749,10 @@ ProcessSingleton::ProcessSingleton(
+ 
+ ProcessSingleton::~ProcessSingleton() {
+   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
++  // Manually free resources with IO explicitly allowed.
++  base::ThreadRestrictions::ScopedAllowIO allow_io;
++  watcher_ = nullptr;
++  ignore_result(socket_dir_.Delete());
+ }
+ 
+ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
+@@ -895,6 +921,20 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
+       base::TimeDelta::FromSeconds(kTimeoutInSeconds));
+ }
+ 
++void ProcessSingleton::StartListeningOnSocket() {
++  watcher_ = base::MakeRefCounted<LinuxWatcher>(this);
++  base::PostTask(FROM_HERE, {BrowserThread::IO},
++                 base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
++                                watcher_, sock_));
++}
++
++void ProcessSingleton::OnBrowserReady() {
++  if (listen_on_ready_) {
++    StartListeningOnSocket();
++    listen_on_ready_ = false;
++  }
++}
++
+ ProcessSingleton::NotifyResult
+ ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
+     const base::CommandLine& command_line,
+@@ -997,12 +1037,26 @@ bool ProcessSingleton::Create() {
+ #endif
+   }
+ 
+-  // Create the socket file somewhere in /tmp which is usually mounted as a
+-  // normal filesystem. Some network filesystems (notably AFS) are screwy and
+-  // do not support Unix domain sockets.
+-  if (!socket_dir_.CreateUniqueTempDir()) {
+-    LOG(ERROR) << "Failed to create socket directory.";
+-    return false;
++  if (IsAppSandboxed()) {
++    // For sandboxed applications, the tmp dir could be too long to fit
++    // addr->sun_path, so we need to make it as short as possible.
++    base::FilePath tmp_dir;
++    if (!base::GetTempDir(&tmp_dir)) {
++      LOG(ERROR) << "Failed to get temporary directory.";
++      return false;
++    }
++    if (!socket_dir_.Set(tmp_dir.Append("S"))) {
++      LOG(ERROR) << "Failed to set socket directory.";
++      return false;
++    }
++  } else {
++    // Create the socket file somewhere in /tmp which is usually mounted as a
++    // normal filesystem. Some network filesystems (notably AFS) are screwy and
++    // do not support Unix domain sockets.
++    if (!socket_dir_.CreateUniqueTempDir()) {
++      LOG(ERROR) << "Failed to create socket directory.";
++      return false;
++    }
+   }
+ 
+   // Check that the directory was created with the correct permissions.
+@@ -1044,10 +1098,13 @@ bool ProcessSingleton::Create() {
+   if (listen(sock, 5) < 0)
+     NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
+ 
+-  DCHECK(BrowserThread::IsThreadInitialized(BrowserThread::IO));
+-  content::GetIOThreadTaskRunner({})->PostTask(
+-      FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
+-                                watcher_, sock));
++  sock_ = sock;
++
++  if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
++    StartListeningOnSocket();
++  } else {
++    listen_on_ready_ = true;
++  }
+ 
+   return true;
+ }
+diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
+index f1732f042c3bfd9d5d95a4208664f9252b2aab73..875269776e45c96ac43a3430768f1406c9608dd3 100644
+--- a/chrome/browser/process_singleton_win.cc
++++ b/chrome/browser/process_singleton_win.cc
+@@ -27,7 +27,9 @@
+ #include "base/win/windows_version.h"
+ #include "base/win/wmi.h"
+ #include "chrome/browser/shell_integration.h"
++#if 0
+ #include "chrome/browser/ui/simple_message_box.h"
++#endif
+ #include "chrome/browser/win/chrome_process_finder.h"
+ #include "chrome/common/chrome_constants.h"
+ #include "chrome/common/chrome_paths.h"
+@@ -175,10 +177,15 @@ bool ProcessLaunchNotification(
+ }
+ 
+ bool DisplayShouldKillMessageBox() {
++#if 0
+   return chrome::ShowQuestionMessageBoxSync(
+              NULL, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
+              l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE)) !=
+          chrome::MESSAGE_BOX_RESULT_NO;
++#endif
++  // This is called when the secondary process can't ping the primary
++  // process.
++  return false;
+ }
+ 
+ void SendRemoteProcessInteractionResultHistogram(
+@@ -260,9 +267,13 @@ bool ProcessSingleton::EscapeVirtualization(
+ }
+ 
+ ProcessSingleton::ProcessSingleton(
++    const std::string& program_name,
+     const base::FilePath& user_data_dir,
++    bool is_app_sandboxed,
+     const NotificationCallback& notification_callback)
+     : notification_callback_(notification_callback),
++      program_name_(program_name),
++      is_app_sandboxed_(is_app_sandboxed),
+       is_virtualized_(false),
+       lock_file_(INVALID_HANDLE_VALUE),
+       user_data_dir_(user_data_dir),
+@@ -366,11 +377,14 @@ ProcessSingleton::NotifyOtherProcessOrCreate() {
+   return PROFILE_IN_USE;
+ }
+ 
++void ProcessSingleton::StartListeningOnSocket() {}
++void ProcessSingleton::OnBrowserReady() {}
++
+ // Look for a Chrome instance that uses the same profile directory. If there
+ // isn't one, create a message window with its title set to the profile
+ // directory path.
+ bool ProcessSingleton::Create() {
+-  static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
++  std::wstring mutexName = base::UTF8ToWide("Local\\" + program_name_ + "ProcessSingletonStartup");
+ 
+   remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
+   if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
+@@ -379,7 +393,7 @@ bool ProcessSingleton::Create() {
+     // access. As documented, it's clearer to NOT request ownership on creation
+     // since it isn't guaranteed we will get it. It is better to create it
+     // without ownership and explicitly get the ownership afterward.
+-    base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
++    base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, mutexName.c_str()));
+     if (!only_me.IsValid()) {
+       DPLOG(FATAL) << "CreateMutex failed";
+       return false;
+@@ -418,6 +432,17 @@ bool ProcessSingleton::Create() {
+             window_.CreateNamed(base::BindRepeating(&ProcessLaunchNotification,
+                                                     notification_callback_),
+                                 user_data_dir_.value());
++
++        // When the app is sandboxed, firstly, the app should not be in
++        // admin mode, and even if it somehow is, messages from an unelevated
++        // instance should not be able to be sent to it.
++        if (!is_app_sandboxed_) {
++          // NB: Ensure that if the primary app gets started as elevated
++          // admin inadvertently, secondary windows running not as elevated
++          // will still be able to send messages.
++          ::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW,
++                                        NULL);
++        }
+         CHECK(result && window_.hwnd());
+       }
+     }

+ 9 - 0
shell/app/command_line_args.cc

@@ -3,8 +3,12 @@
 // found in the LICENSE file.
 
 #include "shell/app/command_line_args.h"
+
 #include <locale>
 
+#include "sandbox/policy/switches.h"
+#include "shell/common/options_switches.h"
+
 namespace {
 
 bool IsUrlArg(const base::CommandLine::CharType* arg) {
@@ -51,4 +55,9 @@ bool CheckCommandLineArguments(int argc, base::CommandLine::CharType** argv) {
   return true;
 }
 
+bool IsSandboxEnabled(base::CommandLine* command_line) {
+  return command_line->HasSwitch(switches::kEnableSandbox) ||
+         !command_line->HasSwitch(sandbox::policy::switches::kNoSandbox);
+}
+
 }  // namespace electron

+ 1 - 0
shell/app/command_line_args.h

@@ -10,6 +10,7 @@
 namespace electron {
 
 bool CheckCommandLineArguments(int argc, base::CommandLine::CharType** argv);
+bool IsSandboxEnabled(base::CommandLine* command_line);
 
 }  // namespace electron
 

+ 1 - 5
shell/app/electron_main_delegate.cc

@@ -25,6 +25,7 @@
 #include "ipc/ipc_buildflags.h"
 #include "sandbox/policy/switches.h"
 #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
+#include "shell/app/command_line_args.h"
 #include "shell/app/electron_content_client.h"
 #include "shell/browser/electron_browser_client.h"
 #include "shell/browser/electron_gpu_client.h"
@@ -82,11 +83,6 @@ bool IsBrowserProcess(base::CommandLine* cmd) {
   return process_type.empty();
 }
 
-bool IsSandboxEnabled(base::CommandLine* command_line) {
-  return command_line->HasSwitch(switches::kEnableSandbox) ||
-         !command_line->HasSwitch(sandbox::policy::switches::kNoSandbox);
-}
-
 // Returns true if this subprocess type needs the ResourceBundle initialized
 // and resources loaded.
 bool SubprocessNeedsResourceBundle(const std::string& process_type) {

+ 15 - 4
shell/browser/api/electron_api_app.cc

@@ -38,6 +38,7 @@
 #include "net/ssl/ssl_private_key.h"
 #include "sandbox/policy/switches.h"
 #include "services/network/network_service.h"
+#include "shell/app/command_line_args.h"
 #include "shell/browser/api/electron_api_menu.h"
 #include "shell/browser/api/electron_api_session.h"
 #include "shell/browser/api/electron_api_web_contents.h"
@@ -511,9 +512,9 @@ int GetPathConstant(const std::string& name) {
 
 bool NotificationCallbackWrapper(
     const base::RepeatingCallback<
-        void(const base::CommandLine::StringVector& command_line,
+        void(const base::CommandLine& command_line,
              const base::FilePath& current_directory)>& callback,
-    const base::CommandLine::StringVector& cmd,
+    const base::CommandLine& cmd,
     const base::FilePath& cwd) {
   // Make sure the callback is called after app gets ready.
   if (Browser::Get()->is_ready()) {
@@ -1067,9 +1068,9 @@ std::string App::GetLocaleCountryCode() {
   return region.size() == 2 ? region : std::string();
 }
 
-void App::OnSecondInstance(const base::CommandLine::StringVector& cmd,
+void App::OnSecondInstance(const base::CommandLine& cmd,
                            const base::FilePath& cwd) {
-  Emit("second-instance", cmd, cwd);
+  Emit("second-instance", cmd.argv(), cwd);
 }
 
 bool App::HasSingleInstanceLock() const {
@@ -1082,13 +1083,23 @@ bool App::RequestSingleInstanceLock() {
   if (HasSingleInstanceLock())
     return true;
 
+  std::string program_name = electron::Browser::Get()->GetName();
+
   base::FilePath user_dir;
   base::PathService::Get(chrome::DIR_USER_DATA, &user_dir);
 
   auto cb = base::BindRepeating(&App::OnSecondInstance, base::Unretained(this));
 
+#if defined(OS_WIN)
+  bool app_is_sandboxed =
+      IsSandboxEnabled(base::CommandLine::ForCurrentProcess());
+  process_singleton_ = std::make_unique<ProcessSingleton>(
+      program_name, user_dir, app_is_sandboxed,
+      base::BindRepeating(NotificationCallbackWrapper, cb));
+#else
   process_singleton_ = std::make_unique<ProcessSingleton>(
       user_dir, base::BindRepeating(NotificationCallbackWrapper, cb));
+#endif
 
   switch (process_singleton_->NotifyOtherProcessOrCreate()) {
     case ProcessSingleton::NotifyResult::LOCK_ERROR:

+ 1 - 1
shell/browser/api/electron_api_app.h

@@ -188,7 +188,7 @@ class App : public ElectronBrowserClient::Delegate,
   void SetDesktopName(const std::string& desktop_name);
   std::string GetLocale();
   std::string GetLocaleCountryCode();
-  void OnSecondInstance(const base::CommandLine::StringVector& cmd,
+  void OnSecondInstance(const base::CommandLine& cmd,
                         const base::FilePath& cwd);
   bool HasSingleInstanceLock() const;
   bool RequestSingleInstanceLock();

+ 6 - 5
spec-main/api-app-spec.ts

@@ -240,11 +240,12 @@ describe('app module', () => {
       expect(code1).to.equal(0);
       const data2 = (await data2Promise)[0].toString('ascii');
       const secondInstanceArgsReceived: string[] = JSON.parse(data2.toString('ascii'));
-      const expected = process.platform === 'win32'
-        ? [process.execPath, '--some-switch', '--allow-file-access-from-files', appPath, 'some-arg']
-        : secondInstanceArgs;
-      expect(secondInstanceArgsReceived).to.eql(expected,
-        `expected ${JSON.stringify(expected)} but got ${data2.toString('ascii')}`);
+
+      // Ensure secondInstanceArgs is a subset of secondInstanceArgsReceived
+      for (const arg of secondInstanceArgs) {
+        expect(secondInstanceArgsReceived).to.include(arg,
+          `argument ${arg} is missing from received second args`);
+      }
     });
   });