blob: 59d4f0821b92e58d7ee7c4a1c639b38e02b5926e [file] [log] [blame]
// 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/first_run/upgrade_util.h"
#include <algorithm>
#include <string>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process_util.h"
#include "base/string_util.h"
#include "base/win/registry.h"
#include "base/win/scoped_comptr.h"
#include "chrome/browser/first_run/upgrade_util_win.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/shell_util.h"
#include "chrome/installer/util/util_constants.h"
#include "google_update_idl.h"
namespace {
bool GetNewerChromeFile(FilePath* path) {
if (!PathService::Get(base::DIR_EXE, path))
return false;
*path = path->Append(installer::kChromeNewExe);
return true;
}
bool InvokeGoogleUpdateForRename() {
base::win::ScopedComPtr<IProcessLauncher> ipl;
if (!FAILED(ipl.CreateInstance(__uuidof(ProcessLauncherClass)))) {
ULONG_PTR phandle = NULL;
DWORD id = GetCurrentProcessId();
BrowserDistribution* dist = BrowserDistribution::GetDistribution();
if (!FAILED(ipl->LaunchCmdElevated(dist->GetAppGuid().c_str(),
google_update::kRegRenameCmdField,
id,
&phandle))) {
HANDLE handle = HANDLE(phandle);
WaitForSingleObject(handle, INFINITE);
DWORD exit_code;
::GetExitCodeProcess(handle, &exit_code);
::CloseHandle(handle);
if (exit_code == installer::RENAME_SUCCESSFUL)
return true;
}
}
return false;
}
} // namespace
namespace upgrade_util {
bool RelaunchChromeBrowser(const CommandLine& command_line) {
scoped_ptr<base::Environment> env(base::Environment::Create());
env->UnSetVar(chrome::kChromeVersionEnvVar);
return base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
}
bool IsUpdatePendingRestart() {
FilePath new_chrome_exe;
if (!GetNewerChromeFile(&new_chrome_exe))
return false;
return file_util::PathExists(new_chrome_exe);
}
bool SwapNewChromeExeIfPresent() {
FilePath new_chrome_exe;
if (!GetNewerChromeFile(&new_chrome_exe))
return false;
if (!file_util::PathExists(new_chrome_exe))
return false;
FilePath cur_chrome_exe;
if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe))
return false;
// Open up the registry key containing current version and rename information.
bool user_install =
InstallUtil::IsPerUserInstall(cur_chrome_exe.value().c_str());
HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
BrowserDistribution *dist = BrowserDistribution::GetDistribution();
base::win::RegKey key;
if (key.Open(reg_root, dist->GetVersionKey().c_str(),
KEY_QUERY_VALUE) == ERROR_SUCCESS) {
// Having just ascertained that we can swap, now check that we should: if
// we are given an explicit --chrome-version flag, don't rename unless the
// specified version matches the "pv" value. In practice, this is used to
// defer Chrome Frame updates until the current version of the Chrome Frame
// DLL component is loaded.
const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
if (cmd_line.HasSwitch(switches::kChromeVersion)) {
std::string version_string =
cmd_line.GetSwitchValueASCII(switches::kChromeVersion);
scoped_ptr<Version> cmd_version(
Version::GetVersionFromString(version_string));
std::wstring pv_value;
if (key.ReadValue(google_update::kRegVersionField,
&pv_value) == ERROR_SUCCESS) {
scoped_ptr<Version> pv_version(
Version::GetVersionFromString(WideToASCII(pv_value)));
if (cmd_version.get() && pv_version.get() &&
!cmd_version->Equals(*pv_version.get())) {
return false;
}
}
}
// First try to rename exe by launching rename command ourselves.
std::wstring rename_cmd;
if (key.ReadValue(google_update::kRegRenameCmdField,
&rename_cmd) == ERROR_SUCCESS) {
base::ProcessHandle handle;
base::LaunchOptions options;
options.wait = true;
options.start_hidden = true;
if (base::LaunchProcess(rename_cmd, options, &handle)) {
DWORD exit_code;
::GetExitCodeProcess(handle, &exit_code);
::CloseHandle(handle);
if (exit_code == installer::RENAME_SUCCESSFUL)
return true;
}
}
}
// Rename didn't work so try to rename by calling Google Update
return InvokeGoogleUpdateForRename();
}
bool DoUpgradeTasks(const CommandLine& command_line) {
if (!SwapNewChromeExeIfPresent())
return false;
// At this point the chrome.exe has been swapped with the new one.
if (!RelaunchChromeBrowser(command_line)) {
// The re-launch fails. Feel free to panic now.
NOTREACHED();
}
return true;
}
} // namespace upgrade_util