summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-24 15:59:02 +0000
committermark@chromium.org <mark@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-24 15:59:02 +0000
commitd9fcd263ece8e879e2e98a1cd7477d255abbbdb4 (patch)
treeb5bfaf81c9585881eac788b4df17e362537a95a0
parentec198d3eb81abf01a75dfb556c505cbb491ec7a0 (diff)
downloadchromium_src-d9fcd263ece8e879e2e98a1cd7477d255abbbdb4.zip
chromium_src-d9fcd263ece8e879e2e98a1cd7477d255abbbdb4.tar.gz
chromium_src-d9fcd263ece8e879e2e98a1cd7477d255abbbdb4.tar.bz2
Fix relaunches on the Mac.
Relaunches that go through browser_shutdown and relaunches after install-from-disk-image should both be improved. browser_shutdown relaunches include those that occur when Chrome detects a new version and offers to relaunch itself, and those that occur when you change about:flags and click "Relaunch Now." When relaunching Chrome in the same location via browser_shutdown, the same Dock tile should now always be reused, provided that the tile is persistent, meaning that the user has chosen "Keep in Dock" from the tile's contextual menu. The relaunched process' activation status (foreground or background application) should now track the original process' activation status. BUG=42962, 85073 TEST=1a. Change something in about:flags and hit "Relaunch Now" at the bottom. If you had a persistent Dock tile, the same tile should be used for the relaunched browser every time, without fail. 1b. Same, but once you're running a version of Chrome (including a canary) with this fix, when an update is applied and you relaunch from within the application via the "Update Google Chrome" item in the wrench menu or the "Relaunch" button in the about window, the same tile should be used for the relaunched browser every time, without fail. 2. Relaunch after install from disk image. Remove Chrome from /Applications and double-click the Chrome icon in a read-only disk image carrying a version that has this fix. (Our official or canary disk images will work). Chrome will offer to install itself. Let it. Upon completion of the installation, Chrome should launch out of /Applications properly. If you had a persistent Dock tile pointing to a copy in /Applications, it will be used. Activation status (foreground or background) in the relaunched browser will match the status in the process that had run out of the disk image at the time that the installation completed. Review URL: http://codereview.chromium.org/7215040 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90371 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/mac/mac_util.h5
-rw-r--r--base/mac/mac_util.mm20
-rw-r--r--chrome/app/chrome_main.cc13
-rw-r--r--chrome/browser/app_controller_mac.mm11
-rw-r--r--chrome/browser/browser_shutdown.cc8
-rw-r--r--chrome/browser/first_run/upgrade_util.h18
-rw-r--r--chrome/browser/first_run/upgrade_util_mac.cc14
-rw-r--r--chrome/browser/mac/install_from_dmg.mm54
-rw-r--r--chrome/browser/mac/relauncher.cc368
-rw-r--r--chrome/browser/mac/relauncher.h73
-rw-r--r--chrome/chrome_browser.gypi4
-rw-r--r--chrome/common/chrome_switches.cc8
-rw-r--r--chrome/common/chrome_switches.h2
13 files changed, 529 insertions, 69 deletions
diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h
index 7fe22c5..71d3a6f 100644
--- a/base/mac/mac_util.h
+++ b/base/mac/mac_util.h
@@ -78,6 +78,11 @@ bool ShouldWindowsMiniaturizeOnDoubleClick();
// Activates the process with the given PID.
void ActivateProcess(pid_t pid);
+// Returns true if this process is in the foreground, meaning that it's the
+// frontmost process, the one whose menu bar is shown at the top of the main
+// display.
+bool AmIForeground();
+
// Excludes the file given by |file_path| from being backed up by Time Machine.
bool SetFileBackupExclusion(const FilePath& file_path);
diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm
index d3bb5a6..0f34180 100644
--- a/base/mac/mac_util.mm
+++ b/base/mac/mac_util.mm
@@ -242,6 +242,26 @@ void ActivateProcess(pid_t pid) {
}
}
+bool AmIForeground() {
+ ProcessSerialNumber foreground_psn = { 0 };
+ OSErr err = GetFrontProcess(&foreground_psn);
+ if (err != noErr) {
+ LOG(WARNING) << "GetFrontProcess: " << err;
+ return false;
+ }
+
+ ProcessSerialNumber my_psn = { 0, kCurrentProcess };
+
+ Boolean result = FALSE;
+ err = SameProcess(&foreground_psn, &my_psn, &result);
+ if (err != noErr) {
+ LOG(WARNING) << "SameProcess: " << err;
+ return false;
+ }
+
+ return result;
+}
+
bool SetFileBackupExclusion(const FilePath& file_path) {
NSString* file_path_ns =
[NSString stringWithUTF8String:file_path.value().c_str()];
diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc
index 20c2ed0..0cacbff 100644
--- a/chrome/app/chrome_main.cc
+++ b/chrome/app/chrome_main.cc
@@ -63,6 +63,7 @@
#include "base/mac/os_crash_dumps.h"
#include "base/mach_ipc_mac.h"
#include "chrome/app/breakpad_mac.h"
+#include "chrome/browser/mac/relauncher.h"
#include "chrome/common/chrome_paths_internal.h"
#include "content/browser/mach_broker_mac.h"
#include "grit/chromium_strings.h"
@@ -305,7 +306,11 @@ bool SubprocessNeedsResourceBundle(const std::string& process_type) {
// Returns true if this process is a child of the browser process.
bool SubprocessIsBrowserChild(const std::string& process_type) {
- if (process_type.empty() || process_type == switches::kServiceProcess) {
+ if (process_type.empty() ||
+#if defined(OS_MACOSX)
+ process_type == switches::kRelauncherProcess ||
+#endif
+ process_type == switches::kServiceProcess) {
return false;
}
return true;
@@ -508,6 +513,7 @@ int RunNamedProcessTypeMain(const std::string& process_type,
#if defined(OS_MACOSX)
// TODO(port): Use OOP profile import - http://crbug.com/22142 .
{ switches::kProfileImportProcess, ProfileImportMain },
+ { switches::kRelauncherProcess, mac_relauncher::internal::RelauncherMain },
#endif
#if !defined(DISABLE_NACL)
{ switches::kNaClLoaderProcess, NaClMain },
@@ -613,8 +619,10 @@ int ChromeMain(int argc, char** argv) {
#if defined(OS_MACOSX)
SendTaskPortToParentProcess();
#endif
+ }
#if defined(OS_POSIX)
+ if (!process_type.empty()) {
// When you hit Ctrl-C in a terminal running the browser
// process, a SIGINT is delivered to the entire process group.
// When debugging the browser process via gdb, gdb catches the
@@ -628,8 +636,9 @@ int ChromeMain(int argc, char** argv) {
// TODO(evanm): move this to some shared subprocess-init function.
if (!base::debug::BeingDebugged())
signal(SIGINT, SIG_IGN);
-#endif
}
+#endif
+
SetupCRT(command_line);
#if defined(USE_NSS)
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 780461c..cdffb7b 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -212,17 +212,6 @@ void RecordLastRunAppBundlePath() {
// Set up the command updater for when there are no windows open
[self initMenuState];
-
- // Activate (bring to foreground) if asked to do so. On
- // Windows this logic isn't necessary since
- // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
- // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
- // makeKeyAndOrderFront:] which does not activate the application
- // itself.
- const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
- if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
- [NSApp activateIgnoringOtherApps:YES];
- }
}
// (NSApplicationDelegate protocol) This is the Apple-approved place to override
diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc
index c41b5ba..e359a64 100644
--- a/chrome/browser/browser_shutdown.cc
+++ b/chrome/browser/browser_shutdown.cc
@@ -231,15 +231,7 @@ void Shutdown() {
if (!new_cl->HasSwitch(switches::kRestoreLastSession))
new_cl->AppendSwitch(switches::kRestoreLastSession);
-#if defined(OS_WIN) || defined(OS_LINUX)
upgrade_util::RelaunchChromeBrowser(*new_cl.get());
-#endif // defined(OS_WIN) || defined(OS_LINUX)
-
-#if defined(OS_MACOSX)
- new_cl->AppendSwitch(switches::kActivateOnLaunch);
- base::LaunchApp(*new_cl.get(), false, false, NULL);
-#endif // defined(OS_MACOSX)
-
#else
NOTIMPLEMENTED();
#endif // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/first_run/upgrade_util.h b/chrome/browser/first_run/upgrade_util.h
index 89302b2..8a87c89 100644
--- a/chrome/browser/first_run/upgrade_util.h
+++ b/chrome/browser/first_run/upgrade_util.h
@@ -6,20 +6,26 @@
#define CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_H_
#pragma once
+#include "build/build_config.h"
+
+#if !defined(OS_CHROMEOS)
+
class CommandLine;
namespace upgrade_util {
+// Launches Chrome again simulating a "user" launch. If Chrome could not be
+// launched, returns false.
+bool RelaunchChromeBrowser(const CommandLine& command_line);
+
+#if !defined(OS_MACOSX)
+
void SetNewCommandLine(CommandLine* new_command_line);
// Launches a new instance of the browser if the current instance in persistent
// mode an upgrade is detected.
void RelaunchChromeBrowserWithNewCommandLineIfNeeded();
-// Launches chrome again simulating a 'user' launch. If chrome could not be
-// launched the return is false.
-bool RelaunchChromeBrowser(const CommandLine& command_line);
-
// Windows:
// Checks if chrome_new.exe is present in the current instance's install.
// Linux:
@@ -27,6 +33,10 @@ bool RelaunchChromeBrowser(const CommandLine& command_line);
// running instance.
bool IsUpdatePendingRestart();
+#endif // !defined(OS_MACOSX)
+
} // namespace upgrade_util
+#endif // !defined(OS_CHROMEOS)
+
#endif // CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_H_
diff --git a/chrome/browser/first_run/upgrade_util_mac.cc b/chrome/browser/first_run/upgrade_util_mac.cc
new file mode 100644
index 0000000..0176fb0
--- /dev/null
+++ b/chrome/browser/first_run/upgrade_util_mac.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2011 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 "base/command_line.h"
+#include "chrome/browser/mac/relauncher.h"
+
+namespace upgrade_util {
+
+bool RelaunchChromeBrowser(const CommandLine& command_line) {
+ return mac_relauncher::RelaunchApp(command_line.argv());
+}
+
+} // namespace upgrade_util
diff --git a/chrome/browser/mac/install_from_dmg.mm b/chrome/browser/mac/install_from_dmg.mm
index 4361f5f..85d1a7f 100644
--- a/chrome/browser/mac/install_from_dmg.mm
+++ b/chrome/browser/mac/install_from_dmg.mm
@@ -15,12 +15,15 @@
#include "base/basictypes.h"
#include "base/command_line.h"
+#include "base/file_path.h"
#include "base/logging.h"
#import "base/mac/mac_util.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "chrome/browser/mac/authorization_util.h"
#include "chrome/browser/mac/scoped_authorizationref.h"
#import "chrome/browser/mac/keystone_glue.h"
+#include "chrome/browser/mac/relauncher.h"
+#include "chrome/common/chrome_constants.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
@@ -307,47 +310,22 @@ bool InstallFromDiskImage(AuthorizationRef authorization_arg,
return true;
}
-// Launches the application at app_path. The arguments passed to app_path
-// will be the same as the arguments used to invoke this process, except any
-// arguments beginning with -psn_ will be stripped.
-bool LaunchInstalledApp(NSString* app_path) {
- const UInt8* app_path_c =
- reinterpret_cast<const UInt8*>([app_path fileSystemRepresentation]);
- FSRef app_fsref;
- OSStatus err = FSPathMakeRef(app_path_c, &app_fsref, NULL);
- if (err != noErr) {
- LOG(ERROR) << "FSPathMakeRef: " << err;
- return false;
- }
-
- const std::vector<std::string>& argv =
- CommandLine::ForCurrentProcess()->argv();
- NSMutableArray* arguments =
- [NSMutableArray arrayWithCapacity:argv.size() - 1];
- // Start at argv[1]. LSOpenApplication adds its own argv[0] as the path of
- // the launched executable.
- for (size_t index = 1; index < argv.size(); ++index) {
- std::string argument = argv[index];
- const char psn_flag[] = "-psn_";
- const int psn_flag_length = arraysize(psn_flag) - 1;
- if (argument.compare(0, psn_flag_length, psn_flag) != 0) {
- // Strip any -psn_ arguments, as they apply to a specific process.
- [arguments addObject:[NSString stringWithUTF8String:argument.c_str()]];
- }
- }
+// Launches the application at installed_path. The helper application
+// contained within install_path will be used for the relauncher process. This
+// keeps Launch Services from ever having to see or think about the helper
+// application on the disk image.
+bool LaunchInstalledApp(NSString* installed_path) {
+ FilePath browser_path([installed_path fileSystemRepresentation]);
- struct LSApplicationParameters parameters = {0};
- parameters.flags = kLSLaunchDefaults;
- parameters.application = &app_fsref;
- parameters.argv = base::mac::NSToCFCast(arguments);
+ FilePath helper_path = browser_path.Append("Contents/Versions");
+ helper_path = helper_path.Append(chrome::kChromeVersion);
+ helper_path = helper_path.Append(chrome::kHelperProcessExecutablePath);
- err = LSOpenApplication(&parameters, NULL);
- if (err != noErr) {
- LOG(ERROR) << "LSOpenApplication: " << err;
- return false;
- }
+ std::vector<std::string> args =
+ CommandLine::ForCurrentProcess()->argv();
+ args[0] = browser_path.value();
- return true;
+ return mac_relauncher::RelaunchAppWithHelper(helper_path.value(), args);
}
void ShowErrorDialog() {
diff --git a/chrome/browser/mac/relauncher.cc b/chrome/browser/mac/relauncher.cc
new file mode 100644
index 0000000..afa99fe
--- /dev/null
+++ b/chrome/browser/mac/relauncher.cc
@@ -0,0 +1,368 @@
+// Copyright (c) 2011 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/mac/relauncher.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <AvailabilityMacros.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/stringprintf.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/common/chrome_switches.h"
+#include "content/common/content_paths.h"
+#include "content/common/content_switches.h"
+#include "content/common/main_function_params.h"
+
+// RTLD_MAIN_ONLY is supported as of Mac OS X 10.5, but <dlfcn.h> does not
+// define it in the 10.5 SDK. It is present in the 10.6 SDK and is documented
+// as working on 10.5 and later. The source code for the version of dyld that
+// shipped in 10.5, dyld-95.3/src/dyldAPIs.cpp, confirms that this feature is
+// supported. Provide a fallback definition here.
+#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5 // 10.5 SDK
+#define RTLD_MAIN_ONLY ((void*)-5) // Search main executable only.
+#endif
+
+namespace mac_relauncher {
+
+namespace {
+
+// The "magic" file descriptor that the relauncher process' write side of the
+// pipe shows up on. Chosen to avoid conflicting with stdin, stdout, and
+// stderr.
+const int kRelauncherSyncFD = STDERR_FILENO + 1;
+
+// The argument separating arguments intended for the relauncher process from
+// those intended for the relaunched process. "---" is chosen instead of "--"
+// because CommandLine interprets "--" as meaning "end of switches", but
+// for many purposes, the relauncher process' CommandLine ought to interpret
+// arguments intended for the relaunched process, to get the correct settings
+// for such things as logging and the user-data-dir in case it affects crash
+// reporting.
+const char kRelauncherArgSeparator[] = "---";
+
+// When this argument is supplied to the relauncher process, it will launch
+// the relaunched process without bringing it to the foreground.
+const char kRelauncherBackgroundArg[] = "--background";
+
+// The beginning of the "process serial number" argument that Launch Services
+// sometimes inserts into command lines. A process serial number is only valid
+// for a single process, so any PSN arguments will be stripped from command
+// lines during relaunch to avoid confusion.
+const char kPSNArg[] = "-psn_";
+
+// Returns the "type" argument identifying a relauncher process
+// ("--type=relauncher").
+std::string RelauncherTypeArg() {
+ return base::StringPrintf("--%s=%s",
+ switches::kProcessType,
+ switches::kRelauncherProcess);
+}
+
+} // namespace
+
+bool RelaunchApp(const std::vector<std::string>& args) {
+ // Use the currently-running application's helper process. The automatic
+ // update feature is careful to leave the currently-running version alone,
+ // so this is safe even if the relaunch is the result of an update having
+ // been applied. In fact, it's safer than using the updated version of the
+ // helper process, because there's no guarantee that the updated version's
+ // relauncher implementation will be compatible with the running version's.
+ FilePath child_path;
+ if (!PathService::Get(content::CHILD_PROCESS_EXE, &child_path)) {
+ LOG(ERROR) << "No CHILD_PROCESS_EXE";
+ return false;
+ }
+
+ return RelaunchAppWithHelper(child_path.value(), args);
+}
+
+bool RelaunchAppWithHelper(const std::string& helper,
+ const std::vector<std::string>& args) {
+ std::vector<std::string> relaunch_args;
+ relaunch_args.push_back(helper);
+ relaunch_args.push_back(RelauncherTypeArg());
+
+ // If this application isn't in the foreground, the relaunched one shouldn't
+ // be either.
+ if (!base::mac::AmIForeground()) {
+ relaunch_args.push_back(kRelauncherBackgroundArg);
+ }
+
+ relaunch_args.push_back(kRelauncherArgSeparator);
+
+ // When using the CommandLine interface, -psn_ may have been rewritten as
+ // --psn_. Look for both.
+ const char alt_psn_arg[] = "--psn_";
+ for (size_t index = 0; index < args.size(); ++index) {
+ // Strip any -psn_ arguments, as they apply to a specific process.
+ if (args[index].compare(0, strlen(kPSNArg), kPSNArg) != 0 &&
+ args[index].compare(0, strlen(alt_psn_arg), alt_psn_arg) != 0) {
+ relaunch_args.push_back(args[index]);
+ }
+ }
+
+ int pipe_fds[2];
+ if (HANDLE_EINTR(pipe(pipe_fds)) != 0) {
+ PLOG(ERROR) << "pipe";
+ return false;
+ }
+
+ // The parent process will only use pipe_read_fd as the read side of the
+ // pipe. It can close the write side as soon as the relauncher process has
+ // forked off. The relauncher process will only use pipe_write_fd as the
+ // write side of the pipe. In that process, the read side will be closed by
+ // base::LaunchApp because it won't be present in fd_map, and the write side
+ // will be remapped to kRelauncherSyncFD by fd_map.
+ file_util::ScopedFD pipe_read_fd(&pipe_fds[0]);
+ file_util::ScopedFD pipe_write_fd(&pipe_fds[1]);
+
+ // Make sure kRelauncherSyncFD is a safe value. base::LaunchApp will
+ // preserve these three FDs in forked processes, so kRelauncherSyncFD should
+ // not conflict with them.
+ COMPILE_ASSERT(kRelauncherSyncFD != STDIN_FILENO &&
+ kRelauncherSyncFD != STDOUT_FILENO &&
+ kRelauncherSyncFD != STDERR_FILENO,
+ kRelauncherSyncFD_must_not_conflict_with_stdio_fds);
+
+ base::file_handle_mapping_vector fd_map;
+ fd_map.push_back(std::make_pair(*pipe_write_fd, kRelauncherSyncFD));
+
+ if (!base::LaunchApp(relaunch_args, fd_map, false, NULL)) {
+ LOG(ERROR) << "base::LaunchApp failed";
+ return false;
+ }
+
+ // The relauncher process is now starting up, or has started up. The
+ // original parent process continues.
+
+ pipe_write_fd.reset(); // close(pipe_fds[1]);
+
+ // Synchronize with the relauncher process.
+ char read_char;
+ int read_result = HANDLE_EINTR(read(*pipe_read_fd, &read_char, 1));
+ if (read_result != 1) {
+ if (read_result < 0) {
+ PLOG(ERROR) << "read";
+ } else {
+ LOG(ERROR) << "read: unexpected result " << read_result;
+ }
+ return false;
+ }
+
+ // Since a byte has been successfully read from the relauncher process, it's
+ // guaranteed to have set up its kqueue monitoring this process for exit.
+ // It's safe to exit now.
+ return true;
+}
+
+namespace {
+
+// In the relauncher process, performs the necessary synchronization steps
+// with the parent by setting up a kqueue to watch for it to exit, writing a
+// byte to the pipe, and then waiting for the exit notification on the kqueue.
+// If anything fails, this logs a message and returns immediately. In those
+// situations, it can be assumed that something went wrong with the parent
+// process and the best recovery approach is to attempt relaunch anyway.
+void RelauncherSynchronizeWithParent() {
+ // file_util::ScopedFD needs something non-const to operate on.
+ int relauncher_sync_fd = kRelauncherSyncFD;
+ file_util::ScopedFD relauncher_sync_fd_closer(&relauncher_sync_fd);
+
+ int parent_pid = getppid();
+
+ // PID 1 identifies init. launchd, that is. launchd never starts the
+ // relauncher process directly, having this parent_pid means that the parent
+ // already exited and launchd "inherited" the relauncher as its child.
+ // There's no reason to synchronize with launchd.
+ if (parent_pid == 1) {
+ LOG(ERROR) << "unexpected parent_pid";
+ return;
+ }
+
+ // Set up a kqueue to monitor the parent process for exit.
+ int kq = kqueue();
+ if (kq < 0) {
+ PLOG(ERROR) << "kqueue";
+ return;
+ }
+ file_util::ScopedFD kq_closer(&kq);
+
+ struct kevent change = { 0 };
+ EV_SET(&change, parent_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
+ if (kevent(kq, &change, 1, NULL, 0, NULL) == -1) {
+ PLOG(ERROR) << "kevent (add)";
+ return;
+ }
+
+ // Write a '\0' character to the pipe.
+ if (HANDLE_EINTR(write(relauncher_sync_fd, "", 1)) != 1) {
+ PLOG(ERROR) << "write";
+ return;
+ }
+
+ // Up until now, the parent process was blocked in a read waiting for the
+ // write above to complete. The parent process is now free to exit. Wait for
+ // that to happen.
+ struct kevent event;
+ int events = kevent(kq, NULL, 0, &event, 1, NULL);
+ if (events != 1) {
+ if (events < 0) {
+ PLOG(ERROR) << "kevent (monitor)";
+ } else {
+ LOG(ERROR) << "kevent (monitor): unexpected result " << events;
+ }
+ return;
+ }
+
+ if (event.filter != EVFILT_PROC ||
+ event.fflags != NOTE_EXIT ||
+ event.ident != static_cast<uintptr_t>(parent_pid)) {
+ LOG(ERROR) << "kevent (monitor): unexpected event, filter " << event.filter
+ << ", fflags " << event.fflags << ", ident " << event.ident;
+ return;
+ }
+}
+
+} // namespace
+
+namespace internal {
+
+int RelauncherMain(const MainFunctionParams& main_parameters) {
+ // CommandLine rearranges the order of the arguments returned by
+ // main_parameters.argv(), rendering it impossible to determine which
+ // arguments originally came before kRelauncherArgSeparator and which came
+ // after. It's crucial to distinguish between these because only those
+ // after the separator should be given to the relaunched process; it's also
+ // important to not treat the path to the relaunched process as a "loose"
+ // argument. NXArgc and NXArgv are pointers to the original argc and argv as
+ // passed to main(), so use those. The typical mechanism to do this is to
+ // provide "extern" declarations to access these, but they're only present
+ // in the crt1.o start file. This function will be linked into the framework
+ // dylib, having no direct access to anything in crt1.o. dlsym to the
+ // rescue.
+ const int* argcp = static_cast<const int*>(dlsym(RTLD_MAIN_ONLY, "NXArgc"));
+ if (!argcp) {
+ LOG(ERROR) << "dlsym NXArgc: " << dlerror();
+ return 1;
+ }
+ int argc = *argcp;
+
+ const char* const** argvp =
+ static_cast<const char* const**>(dlsym(RTLD_MAIN_ONLY, "NXArgv"));
+ if (!argvp) {
+ LOG(ERROR) << "dlsym NXArgv: " << dlerror();
+ return 1;
+ }
+ const char* const* argv = *argvp;
+
+ if (argc < 4 || RelauncherTypeArg() != argv[1]) {
+ LOG(ERROR) << "relauncher process invoked with unexpected arguments";
+ return 1;
+ }
+
+ RelauncherSynchronizeWithParent();
+
+ // The capacity for relaunch_args is 4 less than argc, because it
+ // won't contain the argv[0] of the relauncher process, the
+ // RelauncherTypeArg() at argv[1], kRelauncherArgSeparator, or the
+ // executable path of the process to be launched.
+ base::mac::ScopedCFTypeRef<CFMutableArrayRef> relaunch_args(
+ CFArrayCreateMutable(NULL, argc - 4, &kCFTypeArrayCallBacks));
+ if (!relaunch_args) {
+ LOG(ERROR) << "CFArrayCreateMutable";
+ return 1;
+ }
+
+ // Figure out what to execute, what arguments to pass it, and whether to
+ // start it in the background.
+ bool background = false;
+ bool in_relaunch_args = false;
+ bool seen_relaunch_executable = false;
+ std::string relaunch_executable;
+ const std::string relauncher_arg_separator(kRelauncherArgSeparator);
+ for (int argv_index = 2; argv_index < argc; ++argv_index) {
+ const std::string arg(argv[argv_index]);
+
+ // Strip any -psn_ arguments, as they apply to a specific process.
+ if (arg.compare(0, strlen(kPSNArg), kPSNArg) == 0) {
+ continue;
+ }
+
+ if (!in_relaunch_args) {
+ if (arg == relauncher_arg_separator) {
+ in_relaunch_args = true;
+ } else if (arg == kRelauncherBackgroundArg) {
+ background = true;
+ }
+ } else {
+ if (!seen_relaunch_executable) {
+ // The first argument after kRelauncherBackgroundArg is the path to
+ // the executable file or .app bundle directory. The Launch Services
+ // interface wants this separate from the rest of the arguments. In
+ // the relaunched process, this path will still be visible at argv[0].
+ relaunch_executable.assign(arg);
+ seen_relaunch_executable = true;
+ } else {
+ base::mac::ScopedCFTypeRef<CFStringRef> arg_cf(
+ base::SysUTF8ToCFStringRef(arg));
+ if (!arg_cf) {
+ LOG(ERROR) << "base::SysUTF8ToCFStringRef failed for " << arg;
+ return 1;
+ }
+ CFArrayAppendValue(relaunch_args, arg_cf);
+ }
+ }
+ }
+
+ if (!seen_relaunch_executable) {
+ LOG(ERROR) << "nothing to relaunch";
+ return 1;
+ }
+
+ FSRef app_fsref;
+ if (!base::mac::FSRefFromPath(relaunch_executable, &app_fsref)) {
+ LOG(ERROR) << "base::mac::FSRefFromPath failed for " << relaunch_executable;
+ return 1;
+ }
+
+ LSApplicationParameters ls_parameters = {
+ 0, // version
+ kLSLaunchDefaults | kLSLaunchAndDisplayErrors | kLSLaunchNewInstance |
+ (background ? kLSLaunchDontSwitch : 0),
+ &app_fsref,
+ NULL, // asyncLaunchRefCon
+ NULL, // environment
+ relaunch_args,
+ NULL // initialEvent
+ };
+
+ OSStatus err = LSOpenApplication(&ls_parameters, NULL);
+ if (err != noErr) {
+ LOG(ERROR) << "LSOpenApplication: " << err;
+ return 1;
+ }
+
+ return 0;
+}
+
+} // namespace internal
+
+} // namespace mac_relauncher
diff --git a/chrome/browser/mac/relauncher.h b/chrome/browser/mac/relauncher.h
new file mode 100644
index 0000000..3986c07
--- /dev/null
+++ b/chrome/browser/mac/relauncher.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 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_MAC_RELAUNCHER_H_
+#define CHROME_BROWSER_MAC_RELAUNCHER_H_
+#pragma once
+
+// mac_relauncher implements main browser application relaunches on the Mac.
+// When a browser wants to relaunch itself, it can't simply fork off a new
+// process and exec a new browser from within. That leaves open a window
+// during which two browser applications might be running concurrently. If
+// that happens, each will wind up with a distinct Dock icon, which is
+// especially bad if the user expected the Dock icon to be persistent by
+// choosing Keep in Dock from the icon's contextual menu.
+//
+// mac_relauncher approaches this problem by introducing an intermediate
+// process (the "relauncher") in between the original browser ("parent") and
+// replacement browser ("relaunched"). The helper executable is used for the
+// relauncher process; because it's an LSUIElement, it doesn't get a Dock
+// icon and isn't visible as a running application at all. The parent will
+// start a relauncher process, giving it the "writer" side of a pipe that it
+// retains the "reader" end of. When the relauncher starts up, it will
+// establish a kqueue to wait for the parent to exit, and will then write to
+// the pipe. The parent, upon reading from the pipe, is free to exit. When the
+// relauncher is notified via its kqueue that the parent has exited, it
+// proceeds, launching the relaunched process. The handshake to synchronize
+// the parent with the relauncher is necessary to avoid races: the relauncher
+// needs to be sure that it's monitoring the parent and not some other process
+// in light of PID reuse, so the parent must remain alive long enough for the
+// relauncher to set up its kqueue.
+
+#include <string>
+#include <vector>
+
+struct MainFunctionParams;
+
+namespace mac_relauncher {
+
+// Relaunches the application using the helper application associated with the
+// currently running instance of Chrome in the parent browser process as the
+// executable for the relauncher process. |args| is an argv-style vector of
+// command line arguments of the form normally passed to execv. args[0] is
+// also the path to the relaunched process. Because the relauncher process
+// will ultimately launch the relaunched process via Launch Services, args[0]
+// may be either a pathname to an executable file or a pathname to an .app
+// bundle directory. The caller should exit soon after RelaunchApp returns
+// successfully. Returns true on success, although some failures can occur
+// after this function returns true if, for example, they occur within the
+// relauncher process. Returns false when the relaunch definitely failed.
+bool RelaunchApp(const std::vector<std::string>& args);
+
+// Identical to RelaunchApp, but uses |helper| as the path to the relauncher
+// process. Unlike args[0], |helper| must be a pathname to an executable file.
+// The helper path given must be from the same version of Chrome as the
+// running parent browser process, as there are no guarantees that the parent
+// and relauncher processes from different versions will be able to
+// communicate with one another. This variant can be useful to relaunch the
+// same version of Chrome from another location, using that location's helper.
+bool RelaunchAppWithHelper(const std::string& helper,
+ const std::vector<std::string>& args);
+
+namespace internal {
+
+// The entry point from ChromeMain into the relauncher process. This is not a
+// user API. Don't call it if your name isn't ChromeMain.
+int RelauncherMain(const MainFunctionParams& main_parameters);
+
+} // namespace internal
+
+} // namespace mac_relauncher
+
+#endif // CHROME_BROWSER_MAC_RELAUNCHER_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 5df1cc3..5b22575 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1135,6 +1135,7 @@
'browser/first_run/upgrade_util.h',
'browser/first_run/upgrade_util_linux.cc',
'browser/first_run/upgrade_util_linux.h',
+ 'browser/first_run/upgrade_util_mac.cc',
'browser/first_run/upgrade_util_win.cc',
'browser/first_run/upgrade_util_win.h',
'browser/first_run/try_chrome_dialog_view.cc',
@@ -1352,6 +1353,8 @@
'browser/mac/keystone_glue.mm',
'browser/mac/keystone_registration.h',
'browser/mac/keystone_registration.mm',
+ 'browser/mac/relauncher.cc',
+ 'browser/mac/relauncher.h',
'browser/mac/scoped_authorizationref.h',
'browser/memory_details.cc',
'browser/memory_details.h',
@@ -3806,7 +3809,6 @@
'browser/bookmarks/bookmark_context_menu.cc',
'browser/bookmarks/bookmark_drop_info.cc',
'browser/first_run/upgrade_util.cc',
- 'browser/first_run/upgrade_util.h',
'browser/importer/nss_decryptor_system_nss.cc',
'browser/importer/nss_decryptor_system_nss.h',
'browser/jankometer.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index c66fb023..0bb26f1 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -14,10 +14,6 @@ namespace switches {
// base/base_switches.cc instead.
// -----------------------------------------------------------------------------
-// Activate (make foreground) myself on launch. Helpful when Chrome
-// is launched on the command line (e.g. by Selenium). Only needed on Mac.
-const char kActivateOnLaunch[] = "activate-on-launch";
-
// Allow third party content included on a page to prompt for a HTTP
// basic auth username/password pair.
const char kAllowCrossOriginAuthPrompt[] = "allow-cross-origin-auth-prompt";
@@ -1111,6 +1107,10 @@ const char kPasswordStore[] = "password-store";
#if defined(OS_MACOSX)
// Enables the tabs expose feature ( http://crbug.com/50307 ).
const char kEnableExposeForTabs[] = "enable-expose-for-tabs";
+
+// A process type (switches::kProcessType) that relaunches the browser. See
+// chrome/browser/mac/relauncher.h.
+const char kRelauncherProcess[] = "relauncher";
#endif
#if !defined(OS_MACOSX)
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index bf4a3fdf..7e27d61 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -23,7 +23,6 @@ namespace switches {
// All switches in alphabetical order. The switches should be documented
// alongside the definition of their values in the .cc file.
-extern const char kActivateOnLaunch[];
extern const char kAllowCrossOriginAuthPrompt[];
extern const char kAllowFileAccess[];
extern const char kAllowOutdatedPlugins[];
@@ -317,6 +316,7 @@ extern const char kPasswordStore[];
#if defined(OS_MACOSX)
extern const char kEnableExposeForTabs[];
+extern const char kRelauncherProcess[];
#endif
#if !defined(OS_MACOSX)