Update to use Chromium sources
This commit is contained in:
parent
4bc54ac5e3
commit
f759471e01
9 changed files with 3 additions and 1787 deletions
|
@ -9,8 +9,8 @@
|
|||
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "atom/browser/browser_observer.h"
|
||||
#include "atom/browser/process_singleton.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "chrome/browser/process_singleton.h"
|
||||
#include "content/public/browser/gpu_data_manager_observer.h"
|
||||
#include "native_mate/handle.h"
|
||||
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
// Copyright 2013 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 "atom/browser/atom_process_finder_win.h"
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <string>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.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/win/message_window.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/windows_version.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
int timeout_in_milliseconds = 20 * 1000;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace atom {
|
||||
|
||||
HWND FindRunningAtomWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
NotifyChromeResult AttemptToNotifyRunningAtom(HWND remote_window,
|
||||
bool fast_start) {
|
||||
DCHECK(remote_window);
|
||||
DWORD process_id = 0;
|
||||
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
||||
if (!thread_id || !process_id)
|
||||
return NOTIFY_FAILED;
|
||||
|
||||
base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
|
||||
|
||||
// Send the command line to the remote chrome window.
|
||||
// Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||
base::FilePath cur_dir;
|
||||
if (!base::GetCurrentDirectory(&cur_dir))
|
||||
return NOTIFY_FAILED;
|
||||
to_send.append(cur_dir.value());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
to_send.append(command_line.GetCommandLineString());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
|
||||
// Allow the current running browser window to make itself the foreground
|
||||
// window (otherwise it will just flash in the taskbar).
|
||||
::AllowSetForegroundWindow(process_id);
|
||||
|
||||
COPYDATASTRUCT cds;
|
||||
cds.dwData = 0;
|
||||
cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
|
||||
cds.lpData = const_cast<wchar_t*>(to_send.c_str());
|
||||
DWORD_PTR result = 0;
|
||||
if (::SendMessageTimeout(remote_window, WM_COPYDATA, NULL,
|
||||
reinterpret_cast<LPARAM>(&cds), SMTO_ABORTIFHUNG,
|
||||
timeout_in_milliseconds, &result)) {
|
||||
return result ? NOTIFY_SUCCESS : NOTIFY_FAILED;
|
||||
}
|
||||
|
||||
// It is possible that the process owning this window may have died by now.
|
||||
if (!::IsWindow(remote_window))
|
||||
return NOTIFY_FAILED;
|
||||
|
||||
// If the window couldn't be notified but still exists, assume it is hung.
|
||||
return NOTIFY_WINDOW_HUNG;
|
||||
}
|
||||
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout) {
|
||||
base::TimeDelta old_timeout =
|
||||
base::TimeDelta::FromMilliseconds(timeout_in_milliseconds);
|
||||
timeout_in_milliseconds = new_timeout.InMilliseconds();
|
||||
return old_timeout;
|
||||
}
|
||||
|
||||
} // namespace chrome
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2013 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 ATOM_BROWSER_ATOM_PROCESS_FINDER_WIN_H_
|
||||
#define ATOM_BROWSER_ATOM_PROCESS_FINDER_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
enum NotifyChromeResult {
|
||||
NOTIFY_SUCCESS,
|
||||
NOTIFY_FAILED,
|
||||
NOTIFY_WINDOW_HUNG,
|
||||
};
|
||||
|
||||
// Finds an already running Chrome window if it exists.
|
||||
HWND FindRunningAtomWindow(const base::FilePath& user_data_dir);
|
||||
|
||||
// Attempts to send the current command line to an already running instance of
|
||||
// Chrome via a WM_COPYDATA message.
|
||||
// Returns true if a running Chrome is found and successfully notified.
|
||||
// |fast_start| is true when this is being called on the window fast start path.
|
||||
NotifyChromeResult AttemptToNotifyRunningAtom(HWND remote_window,
|
||||
bool fast_start);
|
||||
|
||||
// Changes the notification timeout to |new_timeout|, returns the old timeout.
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|
||||
|
||||
} // namespace chrome
|
||||
|
||||
#endif // ATOM_BROWSER_ATOM_PROCESS_FINDER_WIN_H_
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "atom/browser/process_singleton.h"
|
||||
#include "atom/browser/process_singleton_startup_lock.h"
|
||||
#include "chrome/browser/process_singleton.h"
|
||||
#include "chrome/browser/process_singleton_startup_lock.h"
|
||||
|
||||
// Composes a basic ProcessSingleton with ProcessSingletonStartupLock
|
||||
class AtomProcessSingleton {
|
||||
|
|
|
@ -1,181 +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 <set>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#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/threading/non_thread_safe.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 base::NonThreadSafe {
|
||||
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::Callback<bool(const base::CommandLine& 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();
|
||||
|
||||
// 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::Callback<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::Callback<void(int)>& callback);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NotificationCallback notification_callback_; // Handler for notifications.
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool EscapeVirtualization(const base::FilePath& user_data_dir);
|
||||
|
||||
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.
|
||||
HANDLE lock_file_;
|
||||
base::FilePath user_data_dir_;
|
||||
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
||||
#elif defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
// Return true if the given pid is one of our child processes.
|
||||
// Assumes that the current pid is the root of all pids of the current
|
||||
// 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::Callback<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_;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessSingleton);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_PROCESS_SINGLETON_H_
|
File diff suppressed because it is too large
Load diff
|
@ -1,53 +0,0 @@
|
|||
// Copyright (c) 2013 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 "atom/browser/process_singleton_startup_lock.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
ProcessSingletonStartupLock::ProcessSingletonStartupLock(
|
||||
const ProcessSingleton::NotificationCallback& original_callback)
|
||||
: locked_(true),
|
||||
original_callback_(original_callback) {}
|
||||
|
||||
ProcessSingletonStartupLock::~ProcessSingletonStartupLock() {}
|
||||
|
||||
ProcessSingleton::NotificationCallback
|
||||
ProcessSingletonStartupLock::AsNotificationCallback() {
|
||||
return base::Bind(&ProcessSingletonStartupLock::NotificationCallbackImpl,
|
||||
base::Unretained(this));
|
||||
}
|
||||
|
||||
void ProcessSingletonStartupLock::Unlock() {
|
||||
DCHECK(CalledOnValidThread());
|
||||
locked_ = false;
|
||||
|
||||
// Replay the command lines of the messages which were received while the
|
||||
// ProcessSingleton was locked. Only replay each message once.
|
||||
std::set<DelayedStartupMessage> replayed_messages;
|
||||
for (std::vector<DelayedStartupMessage>::const_iterator it =
|
||||
saved_startup_messages_.begin();
|
||||
it != saved_startup_messages_.end(); ++it) {
|
||||
if (replayed_messages.find(*it) != replayed_messages.end())
|
||||
continue;
|
||||
original_callback_.Run(base::CommandLine(it->first), it->second);
|
||||
replayed_messages.insert(*it);
|
||||
}
|
||||
saved_startup_messages_.clear();
|
||||
}
|
||||
|
||||
bool ProcessSingletonStartupLock::NotificationCallbackImpl(
|
||||
const base::CommandLine& command_line,
|
||||
const base::FilePath& current_directory) {
|
||||
if (locked_) {
|
||||
// If locked, it means we are not ready to process this message because
|
||||
// we are probably in a first run critical phase.
|
||||
saved_startup_messages_.push_back(
|
||||
std::make_pair(command_line.argv(), current_directory));
|
||||
return true;
|
||||
} else {
|
||||
return original_callback_.Run(command_line, current_directory);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2013 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 ATOM_BROWSER_PROCESS_SINGLETON_STARTUP_LOCK_H_
|
||||
#define ATOM_BROWSER_PROCESS_SINGLETON_STARTUP_LOCK_H_
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/threading/non_thread_safe.h"
|
||||
#include "atom/browser/process_singleton.h"
|
||||
|
||||
// Provides a ProcessSingleton::NotificationCallback that can queue up
|
||||
// command-line invocations during startup and execute them when startup
|
||||
// completes.
|
||||
//
|
||||
// The object starts in a locked state. |Unlock()| must be called
|
||||
// when the process is prepared to handle command-line invocations.
|
||||
//
|
||||
// Once unlocked, notifications are forwarded to a wrapped NotificationCallback.
|
||||
class ProcessSingletonStartupLock : public base::NonThreadSafe {
|
||||
public:
|
||||
explicit ProcessSingletonStartupLock(
|
||||
const ProcessSingleton::NotificationCallback& original_callback);
|
||||
~ProcessSingletonStartupLock();
|
||||
|
||||
// Returns the ProcessSingleton::NotificationCallback.
|
||||
// The callback is only valid during the lifetime of the
|
||||
// ProcessSingletonStartupLock instance.
|
||||
ProcessSingleton::NotificationCallback AsNotificationCallback();
|
||||
|
||||
// Executes previously queued command-line invocations and allows future
|
||||
// invocations to be executed immediately.
|
||||
void Unlock();
|
||||
|
||||
bool locked() { return locked_; }
|
||||
|
||||
private:
|
||||
typedef std::pair<base::CommandLine::StringVector, base::FilePath>
|
||||
DelayedStartupMessage;
|
||||
|
||||
bool NotificationCallbackImpl(const base::CommandLine& command_line,
|
||||
const base::FilePath& current_directory);
|
||||
|
||||
bool locked_;
|
||||
std::vector<DelayedStartupMessage> saved_startup_messages_;
|
||||
ProcessSingleton::NotificationCallback original_callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessSingletonStartupLock);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_PROCESS_SINGLETON_STARTUP_LOCK_H_
|
|
@ -1,317 +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 "atom/browser/process_singleton.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/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/metro.h"
|
||||
#include "base/win/registry.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/windows_version.h"
|
||||
//#include "chrome/browser/browser_process.h"
|
||||
//#include "chrome/browser/browser_process_platform_part.h"
|
||||
#include "atom/browser/atom_process_finder_win.h"
|
||||
//#include "chrome/browser/shell_integration.h" // TODO: Maybe pull this in?
|
||||
#include "content/public/common/result_codes.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "ui/base/l10n/l10n_util.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* 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);
|
||||
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 parsed_command_line(base::CommandLine::NO_PROGRAM);
|
||||
base::FilePath current_directory;
|
||||
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_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),
|
||||
is_virtualized_(false),
|
||||
lock_file_(INVALID_HANDLE_VALUE),
|
||||
user_data_dir_(user_data_dir),
|
||||
should_kill_remote_process_callback_(
|
||||
base::Bind(&TerminateAppWithError)) {
|
||||
}
|
||||
|
||||
ProcessSingleton::~ProcessSingleton() {
|
||||
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 (atom::AttemptToNotifyRunningAtom(remote_window_, false)) {
|
||||
case atom::NOTIFY_SUCCESS:
|
||||
return PROCESS_NOTIFIED;
|
||||
case atom::NOTIFY_FAILED:
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
case atom::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;
|
||||
} else {
|
||||
// TODO: Figure out how to implement this
|
||||
//g_browser_process->platform_part()->PlatformSpecificCommandLineProcessing(
|
||||
// *base::CommandLine::ForCurrentProcess());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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_ = atom::FindRunningAtomWindow(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_ = atom::FindRunningAtomWindow(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::Bind(&ProcessLaunchNotification, notification_callback_),
|
||||
user_data_dir_.value());
|
||||
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;
|
||||
}
|
Loading…
Reference in a new issue