summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-19 12:19:20 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-19 12:19:20 +0000
commit7f6b60b45eee92072b5fad615e1ad760462d19bb (patch)
treee4563b7f9d1814a7010293c7352872dee2023737 /chrome/installer
parent5a8229a6ecc9199153716959e9d6b0f35d9fa673 (diff)
downloadchromium_src-7f6b60b45eee92072b5fad615e1ad760462d19bb.zip
chromium_src-7f6b60b45eee92072b5fad615e1ad760462d19bb.tar.gz
chromium_src-7f6b60b45eee92072b5fad615e1ad760462d19bb.tar.bz2
Fix diff updates for MSI installs. Also makes diff updates possible for GCF and pure multi-install updates. While in there, I also removed the old (and no longer used) "-multifail" ap value tag.
BUG=72899,89315 TEST=run a diff installer (chrome_updater.exe) and see that it works to update an MSI-based installation of Chrome. R=robertshield@chromium.org Review URL: http://codereview.chromium.org/7401014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93005 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/mini_installer.gyp2
-rw-r--r--chrome/installer/mini_installer/configuration.cc77
-rw-r--r--chrome/installer/mini_installer/configuration.h77
-rw-r--r--chrome/installer/mini_installer/configuration_test.cc118
-rw-r--r--chrome/installer/mini_installer/mini_installer.cc203
-rw-r--r--chrome/installer/mini_installer/mini_installer.h13
-rw-r--r--chrome/installer/setup/install_worker.cc29
7 files changed, 400 insertions, 119 deletions
diff --git a/chrome/installer/mini_installer.gyp b/chrome/installer/mini_installer.gyp
index 493e77c..4f870d0 100644
--- a/chrome/installer/mini_installer.gyp
+++ b/chrome/installer/mini_installer.gyp
@@ -25,6 +25,8 @@
],
'sources': [
'mini_installer/appid.h',
+ 'mini_installer/configuration.cc',
+ 'mini_installer/configuration.h',
'mini_installer/decompress.cc',
'mini_installer/decompress.h',
'mini_installer/mini_installer.cc',
diff --git a/chrome/installer/mini_installer/configuration.cc b/chrome/installer/mini_installer/configuration.cc
new file mode 100644
index 0000000..cebe50c
--- /dev/null
+++ b/chrome/installer/mini_installer/configuration.cc
@@ -0,0 +1,77 @@
+// 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/installer/mini_installer/configuration.h"
+
+#include <windows.h>
+#include <shellapi.h> // NOLINT
+
+#include "chrome/installer/mini_installer/appid.h"
+
+namespace mini_installer {
+
+Configuration::Configuration() : args_(NULL) {
+ Clear();
+}
+
+Configuration::~Configuration() {
+ Clear();
+}
+
+const wchar_t* Configuration::program() const {
+ return args_ == NULL || argument_count_ < 1 ? NULL : args_[0];
+}
+
+void Configuration::Clear() {
+ if (args_ != NULL) {
+ ::LocalFree(args_);
+ args_ = NULL;
+ }
+ chrome_app_guid_ = google_update::kAppGuid;
+ command_line_ = NULL;
+ operation_ = INSTALL_PRODUCT;
+ argument_count_ = 0;
+ has_chrome_ = false;
+ has_chrome_frame_ = false;
+ is_multi_install_ = false;
+ is_system_level_ = false;
+}
+
+bool Configuration::Initialize() {
+ return InitializeFromCommandLine(::GetCommandLine());
+}
+
+// This is its own function so that unit tests can provide their own command
+// lines. |command_line| is shared with this instance in the sense that this
+// instance may refer to it at will throughout its lifetime, yet it will
+// not release it.
+bool Configuration::InitializeFromCommandLine(const wchar_t* command_line) {
+ Clear();
+
+ command_line_ = command_line;
+ args_ = ::CommandLineToArgvW(command_line_, &argument_count_);
+ if (args_ != NULL) {
+ for (int i = 1; i < argument_count_; ++i) {
+ if (0 == ::lstrcmpi(args_[i], L"--chrome-sxs"))
+ chrome_app_guid_ = google_update::kSxSAppGuid;
+ else if (0 == ::lstrcmpi(args_[i], L"--chrome"))
+ has_chrome_ = true;
+ else if (0 == ::lstrcmpi(args_[i], L"--chrome-frame"))
+ has_chrome_frame_ = true;
+ else if (0 == ::lstrcmpi(args_[i], L"--multi-install"))
+ is_multi_install_ = true;
+ else if (0 == ::lstrcmpi(args_[i], L"--system-level"))
+ is_system_level_ = true;
+ else if (0 == ::lstrcmpi(args_[i], L"--cleanup"))
+ operation_ = CLEANUP;
+ }
+
+ // Single-install is either Chrome or Chrome Frame.
+ if (!is_multi_install_)
+ has_chrome_ = !has_chrome_frame_;
+ }
+ return args_ != NULL;
+}
+
+} // namespace mini_installer
diff --git a/chrome/installer/mini_installer/configuration.h b/chrome/installer/mini_installer/configuration.h
new file mode 100644
index 0000000..b328871
--- /dev/null
+++ b/chrome/installer/mini_installer/configuration.h
@@ -0,0 +1,77 @@
+// 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_INSTALLER_MINI_INSTALLER_CONFIGURATION_H_
+#define CHROME_INSTALLER_MINI_INSTALLER_CONFIGURATION_H_
+#pragma once
+
+namespace mini_installer {
+
+// A simple container of the mini_installer's configuration, as dictated by the
+// command line used to invoke it.
+class Configuration {
+ public:
+ enum Operation {
+ INSTALL_PRODUCT,
+ CLEANUP,
+ };
+
+ Configuration();
+ ~Configuration();
+
+ // Initializes this instance on the basis of the process's command line.
+ bool Initialize();
+
+ // Returns the desired operation dictated by the command line options.
+ Operation operation() const { return operation_; }
+
+ // Returns the program portion of the command line, or NULL if it cannot be
+ // determined (e.g., by misuse).
+ const wchar_t* program() const;
+
+ // Returns the number of arguments specified on the command line, including
+ // the program itself.
+ int argument_count() const { return argument_count_; }
+
+ // Returns the original command line.
+ const wchar_t* command_line() const { return command_line_; }
+
+ // Returns the app guid to be used for Chrome. --chrome-sxs on the command
+ // line makes this the canary's app guid.
+ const wchar_t* chrome_app_guid() const { return chrome_app_guid_; }
+
+ // Returns true if --chrome is explicitly or implicitly on the command line.
+ bool has_chrome() const { return has_chrome_; }
+
+ // Returns true if --chrome-frame is on the command line.
+ bool has_chrome_frame() const { return has_chrome_frame_; }
+
+ // Returns true if --multi-install is on the command line.
+ bool is_multi_install() const { return is_multi_install_; }
+
+ // Returns true if --system-level is on the command line.
+ bool is_system_level() const { return is_system_level_; }
+
+ protected:
+ void Clear();
+ bool InitializeFromCommandLine(const wchar_t* command_line);
+
+ wchar_t** args_;
+ const wchar_t* chrome_app_guid_;
+ const wchar_t* command_line_;
+ int argument_count_;
+ Operation operation_;
+ bool has_chrome_;
+ bool has_chrome_frame_;
+ bool is_multi_install_;
+ bool is_system_level_;
+
+ private:
+ Configuration(const Configuration&);
+ Configuration& operator=(const Configuration&);
+};
+
+} // namespace mini_installer
+
+#endif // CHROME_INSTALLER_MINI_INSTALLER_CONFIGURATION_H_
diff --git a/chrome/installer/mini_installer/configuration_test.cc b/chrome/installer/mini_installer/configuration_test.cc
new file mode 100644
index 0000000..22333a1
--- /dev/null
+++ b/chrome/installer/mini_installer/configuration_test.cc
@@ -0,0 +1,118 @@
+// 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/basictypes.h"
+#include "chrome/installer/mini_installer/appid.h"
+#include "chrome/installer/mini_installer/configuration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using mini_installer::Configuration;
+
+class TestConfiguration : public Configuration {
+ public:
+ explicit TestConfiguration(const wchar_t* command_line) : Configuration() {
+ Initialize(command_line);
+ }
+ private:
+ void Initialize(const wchar_t* command_line) {
+ ASSERT_TRUE(InitializeFromCommandLine(command_line));
+ }
+};
+
+// Test that the operation type is CLEANUP iff --cleanup is on the cmdline.
+TEST(MiniInstallerConfigurationTest, Operation) {
+ EXPECT_EQ(Configuration::INSTALL_PRODUCT,
+ TestConfiguration(L"spam.exe").operation());
+ EXPECT_EQ(Configuration::INSTALL_PRODUCT,
+ TestConfiguration(L"spam.exe --clean").operation());
+ EXPECT_EQ(Configuration::INSTALL_PRODUCT,
+ TestConfiguration(L"spam.exe --cleanupthis").operation());
+
+ EXPECT_EQ(Configuration::CLEANUP,
+ TestConfiguration(L"spam.exe --cleanup").operation());
+ EXPECT_EQ(Configuration::CLEANUP,
+ TestConfiguration(L"spam.exe --cleanup now").operation());
+}
+
+TEST(MiniInstallerConfigurationTest, Program) {
+ EXPECT_TRUE(NULL == mini_installer::Configuration().program());
+ EXPECT_TRUE(std::wstring(L"spam.exe") ==
+ TestConfiguration(L"spam.exe").program());
+ EXPECT_TRUE(std::wstring(L"spam.exe") ==
+ TestConfiguration(L"spam.exe --with args").program());
+ EXPECT_TRUE(std::wstring(L"c:\\blaz\\spam.exe") ==
+ TestConfiguration(L"c:\\blaz\\spam.exe --with args").program());
+}
+
+TEST(MiniInstallerConfigurationTest, ArgumentCount) {
+ EXPECT_EQ(1, TestConfiguration(L"spam.exe").argument_count());
+ EXPECT_EQ(2, TestConfiguration(L"spam.exe --foo").argument_count());
+ EXPECT_EQ(3, TestConfiguration(L"spam.exe --foo --bar").argument_count());
+}
+
+TEST(MiniInstallerConfigurationTest, CommandLine) {
+ static const wchar_t* const kCommandLines[] = {
+ L"",
+ L"spam.exe",
+ L"spam.exe --foo",
+ };
+ for (size_t i = 0; i < arraysize(kCommandLines); ++i) {
+ EXPECT_TRUE(std::wstring(kCommandLines[i]) ==
+ TestConfiguration(kCommandLines[i]).command_line());
+ }
+}
+
+TEST(MiniInstallerConfigurationTest, ChromeAppGuid) {
+ EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
+ TestConfiguration(L"spam.exe").chrome_app_guid());
+ EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
+ TestConfiguration(L"spam.exe --chrome").chrome_app_guid());
+ EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
+ TestConfiguration(L"spam.exe --multi-install --chrome")
+ .chrome_app_guid());
+ EXPECT_TRUE(std::wstring(google_update::kAppGuid) ==
+ TestConfiguration(L"spam.exe --chrome-frame").chrome_app_guid());
+ EXPECT_TRUE(std::wstring(google_update::kSxSAppGuid) ==
+ TestConfiguration(L"spam.exe --chrome-sxs").chrome_app_guid());
+}
+
+TEST(MiniInstallerConfigurationTest, HasChrome) {
+ EXPECT_TRUE(TestConfiguration(L"spam.exe").has_chrome());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome").has_chrome());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome")
+ .has_chrome());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome-frame").has_chrome());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install").has_chrome());
+}
+
+TEST(MiniInstallerConfigurationTest, HasChromeFrame) {
+ EXPECT_FALSE(TestConfiguration(L"spam.exe").has_chrome_frame());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").has_chrome_frame());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install --chrome")
+ .has_chrome_frame());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome-frame").has_chrome_frame());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome-frame")
+ .has_chrome_frame());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install")
+ .has_chrome_frame());
+}
+
+TEST(MiniInstallerConfigurationTest, IsMultiInstall) {
+ EXPECT_FALSE(TestConfiguration(L"spam.exe").is_multi_install());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_multi_install());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome")
+ .is_multi_install());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome-frame")
+ .is_multi_install());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome-frame")
+ .is_multi_install());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install")
+ .is_multi_install());
+}
+
+TEST(MiniInstallerConfigurationTest, IsSystemLevel) {
+ EXPECT_FALSE(TestConfiguration(L"spam.exe").is_system_level());
+ EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_system_level());
+ EXPECT_TRUE(TestConfiguration(L"spam.exe --system-level").is_system_level());
+}
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc
index c74738a..44a17e2 100644
--- a/chrome/installer/mini_installer/mini_installer.cc
+++ b/chrome/installer/mini_installer/mini_installer.cc
@@ -25,6 +25,7 @@
#include <shellapi.h>
#include "chrome/installer/mini_installer/appid.h"
+#include "chrome/installer/mini_installer/configuration.h"
#include "chrome/installer/mini_installer/decompress.h"
#include "chrome/installer/mini_installer/mini_installer.h"
#include "chrome/installer/mini_installer/mini_string.h"
@@ -152,27 +153,24 @@ bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid, REGSAM access,
(key->Open(root_key, client_state_key.get(), access) == ERROR_SUCCESS);
}
+// This function sets the flag in registry to indicate that Google Update
+// should try full installer next time. If the current installer works, this
+// flag is cleared by setup.exe at the end of install. The flag will by default
+// be written to HKCU, but if --system-level is included in the command line,
+// it will be written to HKLM instead.
// TODO(grt): Write a unit test for this that uses registry virtualization.
-void SetInstallerFlagsHelper(int args_num, const wchar_t* const* args) {
- bool multi_install = false;
+void SetInstallerFlags(const Configuration& configuration) {
RegKey key;
const REGSAM key_access = KEY_QUERY_VALUE | KEY_SET_VALUE;
- const wchar_t* app_guid = google_update::kAppGuid;
- HKEY root_key = HKEY_CURRENT_USER;
+ const HKEY root_key =
+ configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ const wchar_t* app_guid =
+ configuration.has_chrome_frame() ?
+ google_update::kChromeFrameAppGuid :
+ configuration.chrome_app_guid();
StackString<128> value;
LONG ret;
- for (int i = 1; i < args_num; ++i) {
- if (0 == ::lstrcmpi(args[i], L"--chrome-sxs"))
- app_guid = google_update::kSxSAppGuid;
- else if (0 == ::lstrcmpi(args[i], L"--chrome-frame"))
- app_guid = google_update::kChromeFrameAppGuid;
- else if (0 == ::lstrcmpi(args[i], L"--multi-install"))
- multi_install = true;
- else if (0 == ::lstrcmpi(args[i], L"--system-level"))
- root_key = HKEY_LOCAL_MACHINE;
- }
-
// When multi_install is true, we are potentially:
// 1. Performing a multi-install of some product(s) on a clean machine.
// Neither the product(s) nor the multi-installer will have a ClientState
@@ -185,9 +183,9 @@ void SetInstallerFlagsHelper(int args_num, const wchar_t* const* args) {
// exists and its "ap" value does not contain "-multi". This is case 3, so we
// modify the product's ClientState. Otherwise, we check the
// multi-installer's ClientState and modify it if it exists.
- if (multi_install) {
+ if (configuration.is_multi_install()) {
if (OpenClientStateKey(root_key, app_guid, key_access, &key)) {
- // The app is installed. See if it's a single-install.
+ // The product has a client state key. See if it's a single-install.
ret = key.ReadValue(kApRegistryValueName, value.get(), value.capacity());
if (ret != ERROR_FILE_NOT_FOUND &&
(ret != ERROR_SUCCESS ||
@@ -212,68 +210,57 @@ void SetInstallerFlagsHelper(int args_num, const wchar_t* const* args) {
}
// The conditions below are handling two cases:
- // 1. When ap key is present, we want to add the required tags only if they
- // are not present.
- // 2. When ap key is missing, we are going to create it with the required
- // tags.
+ // 1. When ap value is present, we want to add the required tag only if it is
+ // not present.
+ // 2. When ap value is missing, we are going to create it with the required
+ // tag.
if ((ret == ERROR_SUCCESS) || (ret == ERROR_FILE_NOT_FOUND)) {
if (ret == ERROR_FILE_NOT_FOUND)
value.clear();
- bool success = true;
-
- if (multi_install &&
- !FindTagInStr(value.get(), kMultifailInstallerSuffix, NULL)) {
- // We want -multifail to immediately precede -full. Chop off the latter
- // if it's already present so that we can simply do two appends.
- if (StrEndsWith(value.get(), kFullInstallerSuffix)) {
- size_t suffix_len = (arraysize(kFullInstallerSuffix) - 1);
- size_t value_len = value.length();
- value.truncate_at(value_len - suffix_len);
- }
- success = value.append(kMultifailInstallerSuffix);
- }
- if (success && !StrEndsWith(value.get(), kFullInstallerSuffix) &&
- (value.append(kFullInstallerSuffix))) {
+ if (!StrEndsWith(value.get(), kFullInstallerSuffix) &&
+ value.append(kFullInstallerSuffix)) {
key.WriteValue(kApRegistryValueName, value.get());
}
}
}
-// This function sets the flag in registry to indicate that Google Update
-// should try full installer next time. If the current installer works, this
-// flag is cleared by setup.exe at the end of install. The flag will by default
-// be written to HKCU, but if --system-level is included in the command line,
-// it will be written to HKLM instead.
-void SetInstallerFlags() {
- int args_num;
- wchar_t* cmd_line = ::GetCommandLine();
- wchar_t** args = ::CommandLineToArgvW(cmd_line, &args_num);
-
- SetInstallerFlagsHelper(args_num, args);
-
- ::LocalFree(args);
-}
-
// Gets the setup.exe path from Registry by looking the value of Uninstall
-// string, strips the arguments for uninstall and returns only the full path
-// to setup.exe. |size| is measured in wchar_t units.
-bool GetSetupExePathFromRegistry(wchar_t* path, size_t size) {
- if (!ReadValueFromRegistry(HKEY_CURRENT_USER, kUninstallRegistryKey,
- kUninstallRegistryValueName, path, size)) {
- if (!ReadValueFromRegistry(HKEY_LOCAL_MACHINE, kUninstallRegistryKey,
- kUninstallRegistryValueName, path, size)) {
- return false;
- }
+// string. |size| is measured in wchar_t units.
+bool GetSetupExePathFromRegistry(const Configuration& configuration,
+ wchar_t* path,
+ size_t size) {
+ const HKEY root_key =
+ configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ RegKey key;
+ bool succeeded = false;
+
+ // If this is a multi install, first try looking in the binaries for the path.
+ if (configuration.is_multi_install() &&
+ OpenClientStateKey(root_key, google_update::kMultiInstallAppGuid,
+ KEY_QUERY_VALUE, &key)) {
+ succeeded = (key.ReadValue(kUninstallRegistryValueName, path,
+ size) == ERROR_SUCCESS);
}
- wchar_t* tmp = const_cast<wchar_t*>(SearchStringI(path, L" --"));
- if (tmp) {
- *tmp = L'\0';
- } else {
- return false;
+
+ // Failing that, look in Chrome Frame's client state key --chrome-frame was
+ // specified.
+ if (!succeeded && configuration.has_chrome_frame() &&
+ OpenClientStateKey(root_key, google_update::kChromeFrameAppGuid,
+ KEY_QUERY_VALUE, &key)) {
+ succeeded = (key.ReadValue(kUninstallRegistryValueName, path,
+ size) == ERROR_SUCCESS);
}
- return true;
+ // Make a last-ditch effort to look in Chrome's client state key.
+ if (!succeeded &&
+ OpenClientStateKey(root_key, configuration.chrome_app_guid(),
+ KEY_QUERY_VALUE, &key)) {
+ succeeded = (key.ReadValue(kUninstallRegistryValueName, path,
+ size) == ERROR_SUCCESS);
+ }
+
+ return succeeded;
}
// Calls CreateProcess with good default parameters and waits for the process
@@ -308,7 +295,8 @@ bool RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline,
// Append any command line params passed to mini_installer to the given buffer
// so that they can be passed on to setup.exe. We do not return any error from
// this method and simply skip making any changes in case of error.
-void AppendCommandLineFlags(CommandString* buffer) {
+void AppendCommandLineFlags(const Configuration& configuration,
+ CommandString* buffer) {
PathString full_exe_path;
size_t len = ::GetModuleFileName(NULL, full_exe_path.get(),
full_exe_path.capacity());
@@ -319,26 +307,18 @@ void AppendCommandLineFlags(CommandString* buffer) {
if (exe_name == NULL)
return;
- int args_num;
- wchar_t* cmd_line = ::GetCommandLine();
- wchar_t** args = ::CommandLineToArgvW(cmd_line, &args_num);
- if (args_num <= 0)
- return;
-
const wchar_t* cmd_to_append = L"";
- if (!StrEndsWith(args[0], exe_name)) {
+ if (!StrEndsWith(configuration.program(), exe_name)) {
// Current executable name not in the command line so just append
// the whole command line.
- cmd_to_append = cmd_line;
- } else if (args_num > 1) {
- const wchar_t* tmp = SearchStringI(cmd_line, exe_name);
+ cmd_to_append = configuration.command_line();
+ } else if (configuration.argument_count() > 1) {
+ const wchar_t* tmp = SearchStringI(configuration.command_line(), exe_name);
tmp = SearchStringI(tmp, L" ");
cmd_to_append = tmp;
}
buffer->append(cmd_to_append);
-
- LocalFree(args);
}
@@ -390,8 +370,9 @@ BOOL CALLBACK OnResourceFound(HMODULE module, const wchar_t* type,
// If setup.exe is present in more than one form, the precedence order is
// BN < BL < B7
// For more details see chrome/tools/build/win/create_installer_archive.py.
-bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path,
- PathString* archive_path, PathString* setup_path) {
+bool UnpackBinaryResources(const Configuration& configuration, HMODULE module,
+ const wchar_t* base_path, PathString* archive_path,
+ PathString* setup_path) {
// Generate the setup.exe path where we patch/uncompress setup resource.
PathString setup_dest_path;
if (!setup_dest_path.assign(base_path) ||
@@ -419,7 +400,8 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path,
CommandString cmd_line;
// Get the path to setup.exe first.
bool success = true;
- if (!GetSetupExePathFromRegistry(cmd_line.get(), cmd_line.capacity()) ||
+ if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(),
+ cmd_line.capacity()) ||
!cmd_line.append(kCmdUpdateSetupExe) ||
!cmd_line.append(L"=\"") ||
!cmd_line.append(setup_path->get()) ||
@@ -435,7 +417,7 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path,
// on to setup.exe. This is important since switches such as
// --multi-install and --chrome-frame affect where setup.exe will write
// installer results for consumption by Google Update.
- AppendCommandLineFlags(&cmd_line);
+ AppendCommandLineFlags(configuration, &cmd_line);
int exit_code = 0;
if (success &&
@@ -498,8 +480,8 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path,
}
// Executes setup.exe, waits for it to finish and returns the exit code.
-bool RunSetup(const wchar_t* archive_path, const wchar_t* setup_path,
- int* exit_code) {
+bool RunSetup(const Configuration& configuration, const wchar_t* archive_path,
+ const wchar_t* setup_path, int* exit_code) {
// There could be three full paths in the command line for setup.exe (path
// to exe itself, path to archive and path to log file), so we declare
// total size as three + one additional to hold command line options.
@@ -511,7 +493,7 @@ bool RunSetup(const wchar_t* archive_path, const wchar_t* setup_path,
!cmd_line.append(setup_path) ||
!cmd_line.append(L"\""))
return false;
- } else if (!GetSetupExePathFromRegistry(cmd_line.get(),
+ } else if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(),
cmd_line.capacity())) {
return false;
}
@@ -525,7 +507,7 @@ bool RunSetup(const wchar_t* archive_path, const wchar_t* setup_path,
// Get any command line option specified for mini_installer and pass them
// on to setup.exe
- AppendCommandLineFlags(&cmd_line);
+ AppendCommandLineFlags(configuration, &cmd_line);
return RunProcessAndWait(NULL, cmd_line.get(), exit_code);
}
@@ -725,20 +707,19 @@ void DeleteOldChromeTempDirectories() {
// If the function returns true, the command line has been processed and all
// required actions taken. The installer must exit and return the returned
// |exit_code|.
-bool ProcessMiniInstallerCommandLine(int* exit_code) {
+bool ProcessNonInstallOperations(const Configuration& configuration,
+ int* exit_code) {
bool ret = false;
- const wchar_t* cmd_line = ::GetCommandLineW();
- int num_args = 0;
- wchar_t** args = ::CommandLineToArgvW(cmd_line, &num_args);
- if (args) {
- for (int i = 1; i < num_args && !ret; ++i) {
- // Currently there's only one mini installer specific switch defined.
- if (lstrcmpiW(args[i], kMiniCmdCleanup) == 0) {
- *exit_code = 0;
- ret = true;
- }
- }
- ::LocalFree(args);
+
+ switch (configuration.operation()) {
+ case Configuration::CLEANUP:
+ // Cleanup has already taken place in DeleteOldChromeTempDirectories at
+ // this point, so just tell our caller to exit early.
+ *exit_code = 0;
+ ret = true;
+ break;
+
+ default: break;
}
return ret;
@@ -772,10 +753,18 @@ int WMain(HMODULE module) {
// so this could buy us some space.
DeleteOldChromeTempDirectories();
+ // TODO(grt): Make the exit codes more granular so we know where the popular
+ // errors truly are.
+ int exit_code = 101;
+
+ // Parse the command line.
+ Configuration configuration;
+ if (!configuration.Initialize())
+ return exit_code;
+
// If the --cleanup switch was specified on the command line, then that means
// we should only do the cleanup and then exit.
- int exit_code = 101;
- if (ProcessMiniInstallerCommandLine(&exit_code))
+ if (ProcessNonInstallOperations(configuration, &exit_code))
return exit_code;
// First get a path where we can extract payload
@@ -788,21 +777,23 @@ int WMain(HMODULE module) {
// any errors here and we try to set the suffix for user level unless
// --system-level is on the command line in which case we set it for system
// level instead. This only applies to the Google Chrome distribution.
- SetInstallerFlags();
+ SetInstallerFlags(configuration);
#endif
PathString archive_path;
PathString setup_path;
- if (!UnpackBinaryResources(module, base_path.get(), &archive_path,
- &setup_path)) {
+ if (!UnpackBinaryResources(configuration, module, base_path.get(),
+ &archive_path, &setup_path)) {
exit_code = 102;
} else {
// While unpacking the binaries, we paged in a whole bunch of memory that
// we don't need anymore. Let's give it back to the pool before running
// setup.
::SetProcessWorkingSetSize(::GetCurrentProcess(), -1, -1);
- if (!RunSetup(archive_path.get(), setup_path.get(), &exit_code))
+ if (!RunSetup(configuration, archive_path.get(), setup_path.get(),
+ &exit_code)) {
exit_code = 103;
+ }
}
if (ShouldDeleteExtractedFiles())
diff --git a/chrome/installer/mini_installer/mini_installer.h b/chrome/installer/mini_installer/mini_installer.h
index 27cea55..2c2ed9e 100644
--- a/chrome/installer/mini_installer/mini_installer.h
+++ b/chrome/installer/mini_installer/mini_installer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// 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.
@@ -18,22 +18,11 @@ const wchar_t kCmdInstallArchive[] = L" --install-archive";
const wchar_t kCmdUpdateSetupExe[] = L" --update-setup-exe";
const wchar_t kCmdNewSetupExe[] = L" --new-setup-exe";
-// Command line arguments specific only to the mini installer.
-// Note that these constants differ from the kCmdXxx constants above in that
-// they do not have leading whitespace.
-// Pass --cleanup to the mini installer to delete temporary directories that
-// might be left over from previous installation and then exit (i.e. do not
-// extract and run setup.exe).
-const wchar_t kMiniCmdCleanup[] = L"--cleanup";
-
// Temp directory prefix that this process creates
const wchar_t kTempPrefix[] = L"CR_";
// Google Update will use the full installer if this suffix is found in the ap
// value.
const wchar_t kFullInstallerSuffix[] = L"-full";
-// Google Update will use the single-install configuration if this suffix is
-// found in the ap value.
-const wchar_t kMultifailInstallerSuffix[] = L"-multifail";
// ap value tag for a multi-install product.
const wchar_t kMultiInstallTag[] = L"-multi";
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index 717a869..8027f43 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -125,7 +125,9 @@ void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
// in the Google Update client state key. We do this even for non-MSI
// managed installs to avoid breaking the edge case whereby an MSI-managed
// install is updated by a non-msi installer (which would confuse the MSI
- // machinery if these strings were not also updated).
+ // machinery if these strings were not also updated). The UninstallString
+ // value placed in the client state key is also used by the mini_installer to
+ // locate the setup.exe instance used for binary patching.
// Do not quote the command line for the MSI invocation.
FilePath install_path(installer_state.target_path());
FilePath installer_path(installer_state.GetInstallerDirectory(new_version));
@@ -211,6 +213,28 @@ void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
}
}
+// Add uninstall-related work items for multi-install scenarios.
+void AddMultiUninstallWorkItems(const InstallerState& installer_state,
+ const FilePath& setup_path,
+ const Version& new_version,
+ WorkItemList* install_list) {
+ DCHECK(installer_state.is_multi_install());
+
+ // The mini_installer needs a reliable way to locate setup.exe for diff
+ // updates. For single-installs, the product's ClientState key is consulted
+ // (Chrome's or Chrome Frame's). For multi-installs, the binaries' key is
+ // used.
+ const HKEY reg_root = installer_state.root_key();
+ std::wstring binaries_state_key(
+ installer_state.multi_package_binaries_distribution()->GetStateKey());
+ FilePath installer_path(
+ installer_state.GetInstallerDirectory(new_version)
+ .Append(setup_path.BaseName()));
+ install_list->AddCreateRegKeyWorkItem(reg_root, binaries_state_key);
+ install_list->AddSetRegValueWorkItem(reg_root, binaries_state_key,
+ installer::kUninstallStringField, installer_path.value(), true);
+}
+
// Create Version key for a product (if not already present) and sets the new
// product version as the last step.
void AddVersionKeyWorkItems(HKEY root,
@@ -743,6 +767,9 @@ void AddInstallWorkItems(const InstallationState& original_state,
}
if (installer_state.is_multi_install()) {
+ AddMultiUninstallWorkItems(installer_state, setup_path, new_version,
+ install_list);
+
AddVersionKeyWorkItems(root,
installer_state.multi_package_binaries_distribution(), new_version,
install_list);