summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-14 19:47:56 +0000
committerrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-14 19:47:56 +0000
commita3bb1a6c553b17964e4e8ee9041b7dff7191364e (patch)
tree855480c56d0e96e29911001d76521ed6c6c3ab66
parent83c86b6790ca8f58c910d7f68ae257a34b9a3206 (diff)
downloadchromium_src-a3bb1a6c553b17964e4e8ee9041b7dff7191364e.zip
chromium_src-a3bb1a6c553b17964e4e8ee9041b7dff7191364e.tar.gz
chromium_src-a3bb1a6c553b17964e4e8ee9041b7dff7191364e.tar.bz2
Refactor out of install.cc the set of functions that operate on a work item list for better testability.
Write preliminary test framework for testing these functions. BUG=61609 TEST=setup_unittests.exe Review URL: http://codereview.chromium.org/6153003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71473 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_installer.gypi11
-rw-r--r--chrome/installer/setup/chrome_frame_ready_mode.cc1
-rw-r--r--chrome/installer/setup/install.cc694
-rw-r--r--chrome/installer/setup/install.h33
-rw-r--r--chrome/installer/setup/install_worker.cc872
-rw-r--r--chrome/installer/setup/install_worker.h98
-rw-r--r--chrome/installer/setup/install_worker_unittest.cc205
-rw-r--r--chrome/installer/setup/uninstall.cc1
-rw-r--r--chrome/installer/util/installation_state.cc16
-rw-r--r--chrome/installer/util/installation_state.h14
-rw-r--r--chrome/installer/util/installer_state.cc1
-rw-r--r--chrome/installer/util/installer_state.h8
-rw-r--r--chrome/installer/util/work_item_list.h64
13 files changed, 1255 insertions, 763 deletions
diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi
index 1433ec5..9db6089 100644
--- a/chrome/chrome_installer.gypi
+++ b/chrome/chrome_installer.gypi
@@ -208,6 +208,8 @@
'installer/setup/chrome_frame_ready_mode.h',
'installer/setup/install.cc',
'installer/setup/install.h',
+ 'installer/setup/install_worker.cc',
+ 'installer/setup/install_worker.h',
'installer/setup/setup_main.cc',
'installer/setup/setup.ico',
'installer/setup/setup.rc',
@@ -311,13 +313,22 @@
'<(DEPTH)/base/base.gyp:base_i18n',
'<(DEPTH)/base/base.gyp:test_support_base',
'<(DEPTH)/build/temp_gyp/googleurl.gyp:googleurl',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
'<(DEPTH)/testing/gtest.gyp:gtest',
],
'include_dirs': [
'<(DEPTH)',
],
+ # TODO(robertshield): Move the items marked with "Move to lib"
+ # below into a separate lib and then link both setup.exe and
+ # setup_unittests.exe against that.
'sources': [
+ 'installer/setup/install_worker.cc', # Move to lib
+ 'installer/setup/install_worker.h', # Move to lib
+ 'installer/setup/install_worker_unittest.cc',
'installer/setup/run_all_unittests.cc',
+ 'installer/setup/setup_constants.cc', # Move to lib
+ 'installer/setup/setup_constants.h', # Move to lib
'installer/setup/setup_util.cc',
'installer/setup/setup_util_unittest.cc',
],
diff --git a/chrome/installer/setup/chrome_frame_ready_mode.cc b/chrome/installer/setup/chrome_frame_ready_mode.cc
index 3523237..6e980bb 100644
--- a/chrome/installer/setup/chrome_frame_ready_mode.cc
+++ b/chrome/installer/setup/chrome_frame_ready_mode.cc
@@ -13,6 +13,7 @@
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/installer/setup/install.h"
+#include "chrome/installer/setup/install_worker.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/helper.h"
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index dc545a7..0df4e69 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -18,6 +18,7 @@
#include "base/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/installer/setup/setup_constants.h"
+#include "chrome/installer/setup/install_worker.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
#include "chrome/installer/util/chrome_frame_distribution.h"
@@ -92,35 +93,6 @@ void AddInstallerCopyTasks(const FilePath& setup_path,
}
}
-void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd,
- const Product& product) {
- DCHECK(uninstall_cmd);
-
- uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
-
- // Append the product-specific uninstall flags.
- product.distribution()->AppendUninstallCommandLineFlags(uninstall_cmd);
- if (product.IsMsi()) {
- uninstall_cmd->AppendSwitch(installer::switches::kMsi);
- // See comment in uninstall.cc where we check for the kDeleteProfile switch.
- if (product.is_chrome_frame()) {
- uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile);
- }
- }
- if (product.system_level())
- uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
-
- // Propagate switches obtained from preferences as well.
- const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
- if (prefs.is_multi_install()) {
- uninstall_cmd->AppendSwitch(installer::switches::kMultiInstall);
- }
- bool value = false;
- if (prefs.GetBool(installer::master_preferences::kVerboseLogging,
- &value) && value)
- uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
-}
-
// Adds work items that make registry adjustments for Google Update. When a
// product is installed (including overinstall), Google Update will write the
// channel ("ap") value into either Chrome or Chrome Frame's ClientState key.
@@ -395,204 +367,6 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path,
return ret;
}
-// Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
-// products managed by a given package.
-void AddRegisterComDllWorkItemsForPackage(const Package& package,
- const Version* old_version,
- const Version& new_version,
- WorkItemList* work_item_list) {
- // First collect the list of DLLs to be registered from each product.
- const Products& products = package.products();
- Products::const_iterator product_iter(products.begin());
- std::vector<FilePath> com_dll_list;
- for (; product_iter != products.end(); ++product_iter) {
- BrowserDistribution* dist = product_iter->get()->distribution();
- std::vector<FilePath> dist_dll_list(dist->GetComDllList());
- com_dll_list.insert(com_dll_list.end(), dist_dll_list.begin(),
- dist_dll_list.end());
- }
-
- // Then, if we got some, attempt to unregister the DLLs from the old
- // version directory and then re-register them in the new one.
- // Note that if we are migrating the install directory then we will not
- // successfully unregister the old DLLs.
- // TODO(robertshield): See whether we need to fix the migration case.
- // TODO(robertshield): If we ever remove a DLL from a product, this will
- // not unregister it on update. We should build the unregistration list from
- // saved state instead of assuming it is the same as the registration list.
- if (!com_dll_list.empty()) {
- if (old_version) {
- FilePath old_dll_path(
- package.path().Append(UTF8ToWide(old_version->GetString())));
-
- installer::AddRegisterComDllWorkItems(old_dll_path,
- com_dll_list,
- package.system_level(),
- false, // Unregister
- true, // May fail
- work_item_list);
- }
-
- FilePath dll_path(
- package.path().Append(UTF8ToWide(new_version.GetString())));
- installer::AddRegisterComDllWorkItems(dll_path,
- com_dll_list,
- package.system_level(),
- true, // Register
- false, // Must succeed.
- work_item_list);
- }
-}
-
-// After a successful copying of all the files, this function is called to
-// do a few post install tasks:
-// - Handle the case of in-use-update by updating "opv" (old version) key or
-// deleting it if not required.
-// - Register any new dlls and unregister old dlls.
-// - If this is an MSI install, ensures that the MSI marker is set, and sets
-// it if not.
-// If these operations are successful, the function returns true, otherwise
-// false.
-bool AppendPostInstallTasks(bool multi_install,
- const FilePath& setup_path,
- const FilePath& new_chrome_exe,
- const Version* current_version,
- const Version& new_version,
- const Package& package,
- WorkItemList* post_install_task_list) {
- DCHECK(post_install_task_list);
- HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE :
- HKEY_CURRENT_USER;
- const Products& products = package.products();
-
-
- // Append work items that will only be executed if this was an update.
- // We update the 'opv' key with the current version that is active and 'cmd'
- // key with the rename command to run.
- {
- scoped_ptr<WorkItemList> in_use_update_work_items(
- WorkItem::CreateConditionalWorkItemList(
- new ConditionRunIfFileExists(new_chrome_exe)));
- in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
-
- FilePath installer_path(package.GetInstallerDirectory(new_version)
- .Append(setup_path.BaseName()));
-
- CommandLine rename(installer_path);
- rename.AppendSwitch(installer::switches::kRenameChromeExe);
- if (package.system_level())
- rename.AppendSwitch(installer::switches::kSystemLevel);
-
- if (InstallUtil::IsChromeSxSProcess())
- rename.AppendSwitch(installer::switches::kChromeSxS);
-
- if (multi_install)
- rename.AppendSwitch(installer::switches::kMultiInstall);
-
- std::wstring version_key;
- for (size_t i = 0; i < products.size(); ++i) {
- BrowserDistribution* dist = products[i]->distribution();
- version_key = dist->GetVersionKey();
-
- if (current_version != NULL) {
- in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegOldVersionField,
- UTF8ToWide(current_version->GetString()), true);
- }
-
- // Adding this registry entry for all products is overkill.
- // However, as it stands, we don't have a way to know which distribution
- // will check the key and run the command, so we add it for all.
- // After the first run, the subsequent runs should just be noops.
- // (see Upgrade::SwapNewChromeExeIfPresent).
- in_use_update_work_items->AddSetRegValueWorkItem(
- root,
- version_key,
- google_update::kRegRenameCmdField,
- rename.command_line_string(),
- true);
- }
-
- if (multi_install) {
- PackageProperties* props = package.properties();
- if (props->ReceivesUpdates() && current_version != NULL) {
- in_use_update_work_items->AddSetRegValueWorkItem(
- root,
- props->GetVersionKey(),
- google_update::kRegOldVersionField,
- UTF8ToWide(current_version->GetString()),
- true);
- // TODO(tommi): We should move the rename command here. We also need to
- // update Upgrade::SwapNewChromeExeIfPresent.
- }
- }
-
- post_install_task_list->AddWorkItem(in_use_update_work_items.release());
- }
-
-
- // Append work items that will be executed if this was NOT an in-use update.
- {
- scoped_ptr<WorkItemList> regular_update_work_items(
- WorkItem::CreateConditionalWorkItemList(
- new Not(new ConditionRunIfFileExists(new_chrome_exe))));
- regular_update_work_items->set_log_message(
- "RegularUpdateWorkItemList");
-
- // Since this was not an in-use-update, delete 'opv' and 'cmd' keys.
- for (size_t i = 0; i < products.size(); ++i) {
- BrowserDistribution* dist = products[i]->distribution();
- std::wstring version_key(dist->GetVersionKey());
- regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
- google_update::kRegOldVersionField,
- REG_SZ);
- regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
- google_update::kRegRenameCmdField,
- REG_SZ);
- }
-
- post_install_task_list->AddWorkItem(regular_update_work_items.release());
- }
-
- AddRegisterComDllWorkItemsForPackage(package, current_version, new_version,
- post_install_task_list);
-
- for (size_t i = 0; i < products.size(); ++i) {
- const Product* product = products[i];
- // If we're told that we're an MSI install, make sure to set the marker
- // in the client state key so that future updates do the right thing.
- if (product->IsMsi()) {
- AddSetMsiMarkerWorkItem(*product, true, post_install_task_list);
-
- // We want MSI installs to take over the Add/Remove Programs shortcut.
- // Make a best-effort attempt to delete any shortcuts left over from
- // previous non-MSI installations for the same type of install (system or
- // per user).
- AddDeleteUninstallShortcutsForMSIWorkItems(*product,
- post_install_task_list);
- }
- }
-
- return true;
-}
-
-// This method tells if we are running on 64 bit platform so that we can copy
-// one extra exe. If the API call to determine 64 bit fails, we play it safe
-// and return true anyway so that the executable can be copied.
-bool Is64bit() {
- typedef BOOL (WINAPI *WOW_FUNC)(HANDLE, PBOOL);
- BOOL is64 = FALSE;
-
- HANDLE handle = GetCurrentProcess();
- HMODULE module = GetModuleHandle(L"kernel32.dll");
- WOW_FUNC p = reinterpret_cast<WOW_FUNC>(GetProcAddress(module,
- "IsWow64Process"));
- if ((p != NULL) && (!(p)(handle, &is64) || (is64 != FALSE))) {
- return true;
- }
-
- return false;
-}
void RegisterChromeOnMachine(const Product& product,
bool make_chrome_default) {
@@ -620,44 +394,6 @@ void RegisterChromeOnMachine(const Product& product,
}
}
-// Create Version key for a product (if not already present) and sets the new
-// product version as the last step.
-void AddVersionKeyWorkItems(HKEY root,
- const Product& product,
- const Version& new_version,
- WorkItemList* list) {
- // Create Version key for each distribution (if not already present) and set
- // the new product version as the last step.
- std::wstring version_key(product.distribution()->GetVersionKey());
- list->AddCreateRegKeyWorkItem(root, version_key);
-
- std::wstring product_name(product.distribution()->GetAppShortCutName());
- list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
- product_name, true); // overwrite name also
- list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegOopcrashesField,
- static_cast<DWORD>(1),
- false); // set during first install
- list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegVersionField,
- UTF8ToWide(new_version.GetString()),
- true); // overwrite version
-}
-
-void AddProductSpecificWorkItems(bool install,
- const FilePath& setup_path,
- const Version& new_version,
- const Package& package,
- WorkItemList* list) {
- const Products& products = package.products();
- for (size_t i = 0; i < products.size(); ++i) {
- const Product& p = *products[i];
- if (p.is_chrome_frame()) {
- AddChromeFrameWorkItems(install, setup_path, new_version, p, list);
- }
- }
-}
-
// This function installs a new version of Chrome to the specified location.
//
// setup_path: Path to the executable (setup.exe) as it will be copied
@@ -669,7 +405,7 @@ void AddProductSpecificWorkItems(bool install,
// temp_dir: the path of working directory used during installation. This path
// does not need to exist.
// new_version: new Chrome version that needs to be installed
-// oldest_installed_version: returns the oldest active version (if any)
+// current_version: returns the current active version (if any)
//
// This function makes best effort to do installation in a transactional
// manner. If failed it tries to rollback all changes on the file system
@@ -691,119 +427,28 @@ installer::InstallStatus InstallNewVersion(
const Package& package) {
DCHECK(current_version);
- const Products& products = package.products();
- DCHECK(products.size());
-
- if (FindProduct(products, BrowserDistribution::CHROME_FRAME)) {
- // Make sure that we don't end up deleting installed files on next reboot.
- if (!RemoveFromMovesPendingReboot(package.path().value().c_str())) {
- LOG(ERROR) << "Error accessing pending moves value.";
- }
- }
-
current_version->reset(package.GetCurrentVersion());
-
scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
- // A temp directory that work items need and the actual install directory.
- install_list->AddCreateDirWorkItem(temp_dir);
- install_list->AddCreateDirWorkItem(package.path());
- // Delete any new_chrome.exe if present (we will end up creating a new one
- // if required) and then copy chrome.exe
+ AddInstallWorkItems(original_state,
+ installer_state,
+ multi_install,
+ setup_path,
+ archive_path,
+ src_path,
+ temp_dir,
+ new_version,
+ current_version,
+ package,
+ install_list.get());
+
FilePath new_chrome_exe(
package.path().Append(installer::kChromeNewExe));
- install_list->AddDeleteTreeWorkItem(new_chrome_exe);
- install_list->AddCopyTreeWorkItem(
- src_path.Append(installer::kChromeExe).value(),
- package.path().Append(installer::kChromeExe).value(),
- temp_dir.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value());
-
- // Extra executable for 64 bit systems.
- if (Is64bit()) {
- install_list->AddCopyTreeWorkItem(
- src_path.Append(installer::kWowHelperExe).value(),
- package.path().Append(installer::kWowHelperExe).value(),
- temp_dir.value(), WorkItem::ALWAYS);
- }
-
- // If it is system level install copy the version folder (since we want to
- // take the permissions of %ProgramFiles% folder) otherwise just move it.
- if (package.system_level()) {
- install_list->AddCopyTreeWorkItem(
- src_path.Append(UTF8ToWide(new_version.GetString())).value(),
- package.path().Append(UTF8ToWide(new_version.GetString())).value(),
- temp_dir.value(), WorkItem::ALWAYS);
- } else {
- install_list->AddMoveTreeWorkItem(
- src_path.Append(UTF8ToWide(new_version.GetString())).value(),
- package.path().Append(UTF8ToWide(new_version.GetString())).value(),
- temp_dir.value());
- }
-
- // Copy the default Dictionaries only if the folder doesn't exist already.
- install_list->AddCopyTreeWorkItem(
- src_path.Append(installer::kDictionaries).value(),
- package.path().Append(installer::kDictionaries).value(),
- temp_dir.value(), WorkItem::IF_NOT_PRESENT);
-
- // Delete any old_chrome.exe if present.
- install_list->AddDeleteTreeWorkItem(
- package.path().Append(installer::kChromeOldExe));
-
- // Copy installer in install directory and
- // add shortcut in Control Panel->Add/Remove Programs.
- AddInstallerCopyTasks(setup_path, archive_path, temp_dir, new_version,
- install_list.get(), package);
-
- HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-
- for (size_t i = 0; i < products.size(); ++i) {
- const Product* product = products[i];
-
- AddUninstallShortcutWorkItems(setup_path, new_version, install_list.get(),
- *product);
-
- AddVersionKeyWorkItems(root, *product, new_version, install_list.get());
- }
-
- if (multi_install) {
- PackageProperties* props = package.properties();
- if (props->ReceivesUpdates()) {
- std::wstring version_key(props->GetVersionKey());
- install_list->AddCreateRegKeyWorkItem(root, version_key);
- install_list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegVersionField,
- UTF8ToWide(new_version.GetString()),
- true); // overwrite version
- install_list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegNameField,
- ASCIIToWide(installer::PackageProperties::kPackageProductName),
- true); // overwrite name also
- }
- }
-
- // Add any remaining work items that involve special settings for
- // each product.
- AddProductSpecificWorkItems(true, setup_path, new_version, package,
- install_list.get());
-
- AddGoogleUpdateWorkItems(original_state, installer_state, package,
- install_list.get());
-
- // Append the tasks that run after the installation.
- AppendPostInstallTasks(multi_install,
- setup_path,
- new_chrome_exe,
- current_version->get(),
- new_version,
- package,
- install_list.get());
-
if (!install_list->Do()) {
installer::InstallStatus result =
file_util::PathExists(new_chrome_exe) && current_version->get() &&
- new_version.Equals(**current_version) ?
+ new_version.Equals(*current_version->get()) ?
installer::SAME_VERSION_REPAIR_FAILED :
installer::INSTALL_FAILED;
LOG(ERROR) << "Install failed, rolling back... result: " << result;
@@ -853,11 +498,24 @@ installer::InstallStatus InstallOrUpdateProduct(
FilePath src_path(install_temp_path);
src_path = src_path.Append(kInstallSourceDir).Append(kInstallSourceChromeDir);
+ // TODO(robertshield): Removing the pending on-reboot moves should be done
+ // elsewhere.
+ const Products& products = install.products();
+ DCHECK(products.size());
+ if (FindProduct(products, BrowserDistribution::CHROME_FRAME)) {
+ // Make sure that we don't end up deleting installed files on next reboot.
+ if (!RemoveFromMovesPendingReboot(install.path().value().c_str())) {
+ LOG(ERROR) << "Error accessing pending moves value.";
+ }
+ }
+
scoped_ptr<Version> existing_version;
installer::InstallStatus result = InstallNewVersion(original_state,
installer_state, prefs.is_multi_install(), setup_path, archive_path,
src_path, install_temp_path, new_version, &existing_version, install);
+ // TODO(robertshield): Everything below this line should instead be captured
+ // by WorkItems.
if (!InstallUtil::GetInstallReturnCode(result)) {
if (result == installer::FIRST_INSTALL_SUCCESS && !prefs_path.empty())
CopyPreferenceFileForFirstRun(install, prefs_path);
@@ -910,300 +568,4 @@ installer::InstallStatus InstallOrUpdateProduct(
return result;
}
-void AddRegisterComDllWorkItems(const FilePath& dll_folder,
- const std::vector<FilePath>& dll_list,
- bool system_level,
- bool do_register,
- bool ignore_failures,
- WorkItemList* work_item_list) {
- DCHECK(work_item_list);
- if (dll_list.empty()) {
- VLOG(1) << "No COM DLLs to register";
- } else {
- std::vector<FilePath>::const_iterator dll_iter(dll_list.begin());
- for (; dll_iter != dll_list.end(); ++dll_iter) {
- FilePath dll_path = dll_folder.Append(*dll_iter);
- WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
- dll_path.value(), do_register, !system_level);
- DCHECK(work_item);
- work_item->set_ignore_failure(ignore_failures);
- }
- }
-}
-
-void AddSetMsiMarkerWorkItem(const Product& product,
- bool set,
- WorkItemList* work_item_list) {
- DCHECK(work_item_list);
- BrowserDistribution* dist = product.distribution();
- HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
- HKEY_CURRENT_USER;
- DWORD msi_value = set ? 1 : 0;
- WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
- reg_root, dist->GetStateKey(), google_update::kRegMSIField,
- msi_value, true);
- DCHECK(set_msi_work_item);
- set_msi_work_item->set_ignore_failure(true);
- set_msi_work_item->set_log_message("Could not write MSI marker!");
-}
-
-void AddUninstallShortcutWorkItems(const FilePath& setup_path,
- const Version& new_version,
- WorkItemList* install_list,
- const Product& product) {
- HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
- HKEY_CURRENT_USER;
- BrowserDistribution* browser_dist = product.distribution();
- DCHECK(browser_dist);
-
- // When we are installed via an MSI, we need to store our uninstall strings
- // 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).
- // Do not quote the command line for the MSI invocation.
- FilePath install_path(product.package().path());
- FilePath installer_path(
- product.package().GetInstallerDirectory(new_version));
- installer_path = installer_path.Append(setup_path.BaseName());
-
- CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
- AppendUninstallCommandLineFlags(&uninstall_arguments, product);
-
- if (product.is_chrome()) {
- // The Chrome uninstallation command serves as the master uninstall
- // command for Chrome + all other products (i.e. Chrome Frame) that do
- // not have an uninstall entry in the Add/Remove Programs dialog.
- const Products& products = product.package().products();
- for (size_t i = 0; i < products.size(); ++i) {
- const Product& p = *products[i];
- if (!p.is_chrome() && !p.ShouldCreateUninstallEntry()) {
- p.distribution()->AppendUninstallCommandLineFlags(&uninstall_arguments);
- }
- }
- }
-
- std::wstring update_state_key(browser_dist->GetStateKey());
- install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
- install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
- installer::kUninstallStringField, installer_path.value(), true);
- install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
- installer::kUninstallArgumentsField,
- uninstall_arguments.command_line_string(), true);
-
- if (product.ShouldCreateUninstallEntry()) {
- // We need to quote the command line for the Add/Remove Programs dialog.
- CommandLine quoted_uninstall_cmd(installer_path);
- DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"');
- quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
-
- std::wstring uninstall_reg = browser_dist->GetUninstallRegPath();
- install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- installer::kUninstallDisplayNameField,
- browser_dist->GetAppShortCutName(), true);
- install_list->AddSetRegValueWorkItem(reg_root,
- uninstall_reg, installer::kUninstallStringField,
- quoted_uninstall_cmd.command_line_string(), true);
- install_list->AddSetRegValueWorkItem(reg_root,
- uninstall_reg,
- L"InstallLocation",
- install_path.value(),
- true);
-
- // DisplayIcon, NoModify and NoRepair
- FilePath chrome_icon(install_path.Append(installer::kChromeExe));
- ShellUtil::GetChromeIcon(product.distribution(), chrome_icon.value());
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"DisplayIcon", chrome_icon.value(),
- true);
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"NoModify", static_cast<DWORD>(1),
- true);
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"NoRepair", static_cast<DWORD>(1),
- true);
-
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"Publisher",
- browser_dist->GetPublisherName(),
- true);
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"Version",
- UTF8ToWide(new_version.GetString()),
- true);
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"DisplayVersion",
- UTF8ToWide(new_version.GetString()),
- true);
- time_t rawtime = time(NULL);
- struct tm timeinfo = {0};
- localtime_s(&timeinfo, &rawtime);
- wchar_t buffer[9];
- if (wcsftime(buffer, 9, L"%Y%m%d", &timeinfo) == 8) {
- install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
- L"InstallDate",
- buffer, false);
- }
- }
-}
-
-void AddChromeFrameWorkItems(bool install,
- const FilePath& setup_path,
- const Version& new_version,
- const Product& product,
- WorkItemList* list) {
- DCHECK(product.is_chrome_frame());
- if (!product.package().multi_install()) {
- VLOG(1) << "Not adding GCF specific work items for single install.";
- return;
- }
-
- const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
-
- BrowserDistribution* cf = BrowserDistribution::GetSpecificDistribution(
- BrowserDistribution::CHROME_FRAME, prefs);
- std::wstring version_key(cf->GetVersionKey());
-
- // TODO(tommi): This assumes we know exactly how ShouldCreateUninstallEntry
- // is implemented. Since there is logic in ChromeFrameDistribution for how
- // to determine when this is enabled, this is how we have to figure out if
- // this feature is enabled right now, but it's a hack and we need a cleaner
- // way to figure this out.
- // Note that we cannot just check the master preferences for
- // kChromeFrameReadyMode, since there are other things that need to be correct
- // in the environment in order to enable this feature.
- bool ready_mode = !product.distribution()->ShouldCreateUninstallEntry();
-
- HKEY root = product.package().system_level() ? HKEY_LOCAL_MACHINE :
- HKEY_CURRENT_USER;
- bool update_chrome_uninstall_command = false;
- if (ready_mode) {
- // If GCF is being installed in ready mode, we write an entry to the
- // multi-install state key. If the value already exists, we will not
- // overwrite it since the user might have opted out.
- list->AddCreateRegKeyWorkItem(root,
- product.package().properties()->GetStateKey());
- list->AddSetRegValueWorkItem(root,
- product.package().properties()->GetStateKey(),
- installer::kChromeFrameReadyModeField,
- static_cast<int64>(install ? 1 : 0), // The value we want to set.
- install ? false : true); // Overwrite existing value.
- if (install) {
- FilePath installer_path(product.package()
- .GetInstallerDirectory(new_version).Append(setup_path.BaseName()));
-
- CommandLine basic_cl(installer_path);
- basic_cl.AppendSwitch(installer::switches::kChromeFrame);
- basic_cl.AppendSwitch(installer::switches::kMultiInstall);
-
- if (product.package().system_level())
- basic_cl.AppendSwitch(installer::switches::kSystemLevel);
-
- if (InstallUtil::IsChromeSxSProcess())
- basic_cl.AppendSwitch(installer::switches::kChromeSxS);
-
- CommandLine temp_opt_out(basic_cl);
- temp_opt_out.AppendSwitch(
- installer::switches::kChromeFrameReadyModeTempOptOut);
-
- CommandLine end_temp_opt_out(basic_cl);
- end_temp_opt_out.AppendSwitch(
- installer::switches::kChromeFrameReadyModeEndTempOptOut);
-
- CommandLine opt_out(installer_path);
- AppendUninstallCommandLineFlags(&opt_out, product);
- // Force Uninstall silences the prompt to reboot to complete uninstall.
- opt_out.AppendSwitch(installer::switches::kForceUninstall);
-
- CommandLine opt_in(basic_cl);
- opt_in.AppendSwitch(
- installer::switches::kChromeFrameReadyModeOptIn);
-
- list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegCFTempOptOutCmdField,
- temp_opt_out.command_line_string(), true);
- list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegCFEndTempOptOutCmdField,
- end_temp_opt_out.command_line_string(),
- true);
- list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegCFOptOutCmdField,
- opt_out.command_line_string(), true);
- list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegCFOptInCmdField,
- opt_in.command_line_string(), true);
- } else {
- // If Chrome is not also being uninstalled, we need to update its command
- // line so that it doesn't include uninstalling Chrome Frame now.
- update_chrome_uninstall_command =
- (installer::FindProduct(product.package().products(),
- BrowserDistribution::CHROME_BROWSER) == NULL);
- }
- } else {
- // It doesn't matter here if we're installing or uninstalling Chrome Frame.
- // If ready mode isn't specified on the command line for installs, we need
- // to delete the ready mode flag from the registry if it exists - this
- // constitutes an opt-in for the user. If we're uninstalling CF and ready
- // mode isn't specified on the command line, that means that CF wasn't
- // installed with ready mode enabled (the --ready-mode switch should be set
- // in the registry) so deleting the value should have no effect.
- // In both cases (install/uninstall), we need to make sure that Chrome's
- // uninstallation command line does not include the --chrome-frame switch
- // so that uninstalling Chrome will no longer uninstall Chrome Frame.
-
- if (RegKey(root, product.package().properties()->GetStateKey().c_str(),
- KEY_QUERY_VALUE).Valid()) {
- list->AddDeleteRegValueWorkItem(root,
- product.package().properties()->GetStateKey(),
- installer::kChromeFrameReadyModeField, REG_QWORD);
- }
-
- const Product* chrome = installer::FindProduct(product.package().products(),
- BrowserDistribution::CHROME_BROWSER);
- if (chrome) {
- // Chrome is already a part of this installation run, so we can assume
- // that the uninstallation arguments will be updated correctly.
- } else {
- // Chrome is not a part of this installation run, so we have to explicitly
- // check if Chrome is installed, and if so, update its uninstallation
- // command lines.
- BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
- BrowserDistribution::CHROME_BROWSER,
- MasterPreferences::ForCurrentProcess());
- update_chrome_uninstall_command =
- IsInstalledAsMulti(product.system_level(), dist);
- }
- }
-
- if (!ready_mode || !install) {
- list->AddDeleteRegValueWorkItem(root, version_key,
- google_update::kRegCFTempOptOutCmdField,
- REG_SZ);
- list->AddDeleteRegValueWorkItem(root, version_key,
- google_update::kRegCFEndTempOptOutCmdField,
- REG_SZ);
- list->AddDeleteRegValueWorkItem(root, version_key,
- google_update::kRegCFOptOutCmdField,
- REG_SZ);
- list->AddDeleteRegValueWorkItem(root, version_key,
- google_update::kRegCFOptInCmdField, REG_SZ);
- }
-
- if (update_chrome_uninstall_command) {
- // Chrome is not a part of this installation run, so we have to explicitly
- // check if Chrome is installed, and if so, update its uninstallation
- // command lines.
- BrowserDistribution* chrome_dist =
- BrowserDistribution::GetSpecificDistribution(
- BrowserDistribution::CHROME_BROWSER, prefs);
- const Package& pack = product.package();
- scoped_refptr<Package> package(new Package(pack.multi_install(),
- pack.system_level(), pack.path(), pack.properties()));
- scoped_refptr<Product> chrome_product(new Product(chrome_dist, package));
- AddUninstallShortcutWorkItems(setup_path, new_version, list,
- *chrome_product.get());
- }
-}
-
} // namespace installer
diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h
index 7e8d465..c3ff033 100644
--- a/chrome/installer/setup/install.h
+++ b/chrome/installer/setup/install.h
@@ -51,40 +51,7 @@ InstallStatus InstallOrUpdateProduct(
const installer::MasterPreferences& prefs, const Version& new_version,
const Package& package);
-// Appends registration or unregistration work items to |work_item_list| for the
-// COM DLLs whose file names are given in |dll_files| and which reside in the
-// path |dll_folder|.
-// |system_level| specifies whether to call the system or user level DLL
-// registration entry points.
-// |do_register| says whether to register or unregister.
-// |may_fail| states whether this is best effort or not. If |may_fail| is true
-// then |work_item_list| will still succeed if the registration fails and
-// no registration rollback will be performed.
-void AddRegisterComDllWorkItems(const FilePath& dll_folder,
- const std::vector<FilePath>& dll_files,
- bool system_level,
- bool do_register,
- bool ignore_failures,
- WorkItemList* work_item_list);
-void AddSetMsiMarkerWorkItem(const Product& product,
- bool set,
- WorkItemList* work_item_list);
-
-// This method adds work items to create (or update) Chrome uninstall entry in
-// either the Control Panel->Add/Remove Programs list or in the Omaha client
-// state key if running under an MSI installer.
-void AddUninstallShortcutWorkItems(const FilePath& setup_path,
- const Version& new_version,
- WorkItemList* install_list,
- const Product& product);
-
-// Called for either installation or uninstallation. This method updates the
-// registry according to Chrome Frame specific options for the current
-// installation. This includes handling of the ready-mode option.
-void AddChromeFrameWorkItems(bool install, const FilePath& setup_path,
- const Version& new_version, const Product& product,
- WorkItemList* list);
} // namespace installer
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
new file mode 100644
index 0000000..e945bdb
--- /dev/null
+++ b/chrome/installer/setup/install_worker.cc
@@ -0,0 +1,872 @@
+// 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.
+//
+// This file contains the definitions of the installer functions that build
+// the WorkItemList used to install the application.
+
+#include "chrome/installer/setup/install_worker.h"
+
+#include <shlobj.h>
+#include <time.h>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/version.h"
+#include "base/win/registry.h"
+#include "chrome/installer/setup/install.h"
+#include "chrome/installer/setup/setup_constants.h"
+#include "chrome/installer/util/chrome_frame_distribution.h"
+#include "chrome/installer/util/conditional_work_item_list.h"
+#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/google_update_constants.h"
+#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/installation_state.h"
+#include "chrome/installer/util/installer_state.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/master_preferences.h"
+#include "chrome/installer/util/master_preferences_constants.h"
+#include "chrome/installer/util/package.h"
+#include "chrome/installer/util/package_properties.h"
+#include "chrome/installer/util/product.h"
+#include "chrome/installer/util/set_reg_value_work_item.h"
+#include "chrome/installer/util/shell_util.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/work_item_list.h"
+
+using base::win::RegKey;
+
+namespace {
+
+// This method tells if we are running on 64 bit platform so that we can copy
+// one extra exe. If the API call to determine 64 bit fails, we play it safe
+// and return true anyway so that the executable can be copied.
+bool Is64bit() {
+ typedef BOOL (WINAPI* WOW_FUNC)(HANDLE, BOOL*);
+ BOOL is_64 = FALSE;
+
+ HMODULE module = GetModuleHandle(L"kernel32.dll");
+ WOW_FUNC is_wow64 = reinterpret_cast<WOW_FUNC>(
+ GetProcAddress(module, "IsWow64Process"));
+ return (is_wow64 != NULL) &&
+ (!(is_wow64)(GetCurrentProcess(), &is_64) || (is_64 != FALSE));
+}
+
+} // namespace
+
+namespace installer {
+
+// Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
+// products managed by a given package.
+void AddRegisterComDllWorkItemsForPackage(const Package& package,
+ const Version* old_version,
+ const Version& new_version,
+ WorkItemList* work_item_list) {
+ // First collect the list of DLLs to be registered from each product.
+ const Products& products = package.products();
+ Products::const_iterator product_iter(products.begin());
+ std::vector<FilePath> com_dll_list;
+ for (; product_iter != products.end(); ++product_iter) {
+ BrowserDistribution* dist = product_iter->get()->distribution();
+ std::vector<FilePath> dist_dll_list(dist->GetComDllList());
+ com_dll_list.insert(com_dll_list.end(), dist_dll_list.begin(),
+ dist_dll_list.end());
+ }
+
+ // Then, if we got some, attempt to unregister the DLLs from the old
+ // version directory and then re-register them in the new one.
+ // Note that if we are migrating the install directory then we will not
+ // successfully unregister the old DLLs.
+ // TODO(robertshield): See whether we need to fix the migration case.
+ // TODO(robertshield): If we ever remove a DLL from a product, this will
+ // not unregister it on update. We should build the unregistration list from
+ // saved state instead of assuming it is the same as the registration list.
+ if (!com_dll_list.empty()) {
+ if (old_version) {
+ FilePath old_dll_path(
+ package.path().Append(UTF8ToWide(old_version->GetString())));
+
+ installer::AddRegisterComDllWorkItems(old_dll_path,
+ com_dll_list,
+ package.system_level(),
+ false, // Unregister
+ true, // May fail
+ work_item_list);
+ }
+
+ FilePath dll_path(
+ package.path().Append(UTF8ToWide(new_version.GetString())));
+ installer::AddRegisterComDllWorkItems(dll_path,
+ com_dll_list,
+ package.system_level(),
+ true, // Register
+ false, // Must succeed.
+ work_item_list);
+ }
+}
+
+void AddInstallerCopyTasks(const FilePath& setup_path,
+ const FilePath& archive_path,
+ const FilePath& temp_path,
+ const Version& new_version,
+ WorkItemList* install_list,
+ const Package& package) {
+ DCHECK(install_list);
+ FilePath installer_dir(package.GetInstallerDirectory(new_version));
+ install_list->AddCreateDirWorkItem(installer_dir);
+
+ FilePath exe_dst(installer_dir.Append(setup_path.BaseName()));
+ FilePath archive_dst(installer_dir.Append(archive_path.BaseName()));
+
+ install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(),
+ temp_path.value(), WorkItem::ALWAYS);
+ if (package.system_level()) {
+ install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(),
+ temp_path.value(), WorkItem::ALWAYS);
+ } else {
+ install_list->AddMoveTreeWorkItem(archive_path.value(), archive_dst.value(),
+ temp_path.value());
+ }
+}
+
+// This method adds work items to create (or update) Chrome uninstall entry in
+// either the Control Panel->Add/Remove Programs list or in the Omaha client
+// state key if running under an MSI installer.
+void AddUninstallShortcutWorkItems(const FilePath& setup_path,
+ const Version& new_version,
+ WorkItemList* install_list,
+ const Product& product) {
+ HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ BrowserDistribution* browser_dist = product.distribution();
+ DCHECK(browser_dist);
+
+ // When we are installed via an MSI, we need to store our uninstall strings
+ // 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).
+ // Do not quote the command line for the MSI invocation.
+ FilePath install_path(product.package().path());
+ FilePath installer_path(
+ product.package().GetInstallerDirectory(new_version));
+ installer_path = installer_path.Append(setup_path.BaseName());
+
+ CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
+ AppendUninstallCommandLineFlags(&uninstall_arguments, product);
+
+ if (product.is_chrome()) {
+ // The Chrome uninstallation command serves as the master uninstall
+ // command for Chrome + all other products (i.e. Chrome Frame) that do
+ // not have an uninstall entry in the Add/Remove Programs dialog.
+ const Products& products = product.package().products();
+ for (size_t i = 0; i < products.size(); ++i) {
+ const Product& p = *products[i];
+ if (!p.is_chrome() && !p.ShouldCreateUninstallEntry()) {
+ p.distribution()->AppendUninstallCommandLineFlags(&uninstall_arguments);
+ }
+ }
+ }
+
+ std::wstring update_state_key(browser_dist->GetStateKey());
+ install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
+ install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
+ installer::kUninstallStringField, installer_path.value(), true);
+ install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
+ installer::kUninstallArgumentsField,
+ uninstall_arguments.command_line_string(), true);
+
+ if (product.ShouldCreateUninstallEntry()) {
+ // We need to quote the command line for the Add/Remove Programs dialog.
+ CommandLine quoted_uninstall_cmd(installer_path);
+ DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"');
+ quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
+
+ std::wstring uninstall_reg = browser_dist->GetUninstallRegPath();
+ install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ installer::kUninstallDisplayNameField,
+ browser_dist->GetAppShortCutName(), true);
+ install_list->AddSetRegValueWorkItem(reg_root,
+ uninstall_reg, installer::kUninstallStringField,
+ quoted_uninstall_cmd.command_line_string(), true);
+ install_list->AddSetRegValueWorkItem(reg_root,
+ uninstall_reg,
+ L"InstallLocation",
+ install_path.value(),
+ true);
+
+ // DisplayIcon, NoModify and NoRepair
+ FilePath chrome_icon(install_path.Append(installer::kChromeExe));
+ ShellUtil::GetChromeIcon(product.distribution(), chrome_icon.value());
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"DisplayIcon", chrome_icon.value(),
+ true);
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"NoModify", static_cast<DWORD>(1),
+ true);
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"NoRepair", static_cast<DWORD>(1),
+ true);
+
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"Publisher",
+ browser_dist->GetPublisherName(),
+ true);
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"Version",
+ UTF8ToWide(new_version.GetString()),
+ true);
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"DisplayVersion",
+ UTF8ToWide(new_version.GetString()),
+ true);
+ time_t rawtime = time(NULL);
+ struct tm timeinfo = {0};
+ localtime_s(&timeinfo, &rawtime);
+ wchar_t buffer[9];
+ if (wcsftime(buffer, 9, L"%Y%m%d", &timeinfo) == 8) {
+ install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
+ L"InstallDate",
+ buffer, false);
+ }
+ }
+}
+
+// Create Version key for a product (if not already present) and sets the new
+// product version as the last step.
+void AddVersionKeyWorkItems(HKEY root,
+ const Product& product,
+ const Version& new_version,
+ WorkItemList* list) {
+ // Create Version key for each distribution (if not already present) and set
+ // the new product version as the last step.
+ std::wstring version_key(product.distribution()->GetVersionKey());
+ list->AddCreateRegKeyWorkItem(root, version_key);
+
+ std::wstring product_name(product.distribution()->GetAppShortCutName());
+ list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
+ product_name, true); // overwrite name also
+ list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegOopcrashesField,
+ static_cast<DWORD>(1),
+ false); // set during first install
+ list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegVersionField,
+ UTF8ToWide(new_version.GetString()),
+ true); // overwrite version
+}
+
+void AddProductSpecificWorkItems(bool install,
+ const FilePath& setup_path,
+ const Version& new_version,
+ const Package& package,
+ WorkItemList* list) {
+ const Products& products = package.products();
+ for (size_t i = 0; i < products.size(); ++i) {
+ const Product& p = *products[i];
+ if (p.is_chrome_frame()) {
+ AddChromeFrameWorkItems(install, setup_path, new_version, p, list);
+ }
+ }
+}
+
+// Adds work items that make registry adjustments for Google Update. When a
+// product is installed (including overinstall), Google Update will write the
+// channel ("ap") value into either Chrome or Chrome Frame's ClientState key.
+// In the multi-install case, this value is used as the basis upon which the
+// package's channel value is built (by adding the ordered list of installed
+// products and their options).
+void AddGoogleUpdateWorkItems(const InstallationState& original_state,
+ const InstallerState& installer_state,
+ const Package& package,
+ WorkItemList* install_list) {
+ // Is a multi-install product being installed or over-installed?
+ if (installer_state.operation() != InstallerState::MULTI_INSTALL)
+ return;
+
+ const HKEY reg_root = package.system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ const std::wstring key_path = installer_state.state_key();
+ ChannelInfo channel_info;
+
+ // Update the "ap" value for the product being installed/updated.
+ // It is completely acceptable for there to be no "ap" value or even no
+ // ClientState key. Note that we check the registry rather than
+ // original_state since on a fresh install the "ap" value will be present
+ // sans "pv" value.
+ channel_info.Initialize(RegKey(reg_root, key_path.c_str(), KEY_QUERY_VALUE));
+
+ // This is a multi-install product.
+ bool modified = channel_info.SetMultiInstall(true);
+
+ // Add the appropriate modifiers for all products and their options.
+ Products::const_iterator scan = package.products().begin();
+ const Products::const_iterator end = package.products().end();
+ for (; scan != end; ++scan) {
+ modified |= scan->get()->distribution()->SetChannelFlags(true,
+ &channel_info);
+ }
+
+ // Write the results if needed.
+ if (modified) {
+ install_list->AddSetRegValueWorkItem(reg_root, key_path,
+ google_update::kRegApField,
+ channel_info.value(), true);
+ }
+
+ // Synchronize the other products and the package with this one.
+ std::wstring other_key;
+ std::vector<std::wstring> keys;
+
+ keys.reserve(package.products().size());
+ other_key = package.properties()->GetStateKey();
+ if (other_key != key_path)
+ keys.push_back(other_key);
+ scan = package.products().begin();
+ for (; scan != end; ++scan) {
+ other_key = scan->get()->distribution()->GetStateKey();
+ if (other_key != key_path)
+ keys.push_back(other_key);
+ }
+
+ RegKey key;
+ ChannelInfo other_info;
+ std::vector<std::wstring>::const_iterator kscan = keys.begin();
+ std::vector<std::wstring>::const_iterator kend = keys.end();
+ for (; kscan != kend; ++kscan) {
+ // Handle the case where the ClientState key doesn't exist by creating it.
+ // This takes care of the multi-installer's package key, which is not
+ // created by Google Update for us.
+ if (!key.Open(reg_root, kscan->c_str(), KEY_QUERY_VALUE) ||
+ !other_info.Initialize(key)) {
+ other_info.set_value(std::wstring());
+ }
+ if (!other_info.Equals(channel_info)) {
+ if (!key.Valid())
+ install_list->AddCreateRegKeyWorkItem(reg_root, *kscan);
+ install_list->AddSetRegValueWorkItem(reg_root, *kscan,
+ google_update::kRegApField,
+ channel_info.value(), true);
+ }
+ }
+ // TODO(grt): check for other keys/values we should put in the package's
+ // ClientState and/or Clients key.
+}
+
+// This is called when an MSI installation is run. It may be that a user is
+// attempting to install the MSI on top of a non-MSI managed installation.
+// If so, try and remove any existing uninstallation shortcuts, as we want the
+// uninstall to be managed entirely by the MSI machinery (accessible via the
+// Add/Remove programs dialog).
+void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product,
+ WorkItemList* work_item_list) {
+ DCHECK(product.IsMsi()) << "This must only be called for MSI installations!";
+
+ // First attempt to delete the old installation's ARP dialog entry.
+ HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS);
+ std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath());
+
+ WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
+ reg_root, uninstall_reg);
+ delete_reg_key->set_ignore_failure(true);
+
+ // Then attempt to delete the old installation's start menu shortcut.
+ FilePath uninstall_link;
+ if (product.system_level()) {
+ PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
+ } else {
+ PathService::Get(base::DIR_START_MENU, &uninstall_link);
+ }
+
+ if (uninstall_link.empty()) {
+ LOG(ERROR) << "Failed to get location for shortcut.";
+ } else {
+ uninstall_link = uninstall_link.Append(
+ product.distribution()->GetAppShortCutName());
+ uninstall_link = uninstall_link.Append(
+ product.distribution()->GetUninstallLinkName() + L".lnk");
+ VLOG(1) << "Deleting old uninstall shortcut (if present): "
+ << uninstall_link.value();
+ WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
+ uninstall_link);
+ delete_link->set_ignore_failure(true);
+ delete_link->set_log_message(
+ "Failed to delete old uninstall shortcut.");
+ }
+}
+
+// After a successful copying of all the files, this function is called to
+// do a few post install tasks:
+// - Handle the case of in-use-update by updating "opv" (old version) key or
+// deleting it if not required.
+// - Register any new dlls and unregister old dlls.
+// - If this is an MSI install, ensures that the MSI marker is set, and sets
+// it if not.
+// If these operations are successful, the function returns true, otherwise
+// false.
+bool AppendPostInstallTasks(bool multi_install,
+ const FilePath& setup_path,
+ const FilePath& new_chrome_exe,
+ const Version* current_version,
+ const Version& new_version,
+ const Package& package,
+ WorkItemList* post_install_task_list) {
+ DCHECK(post_install_task_list);
+ HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ const Products& products = package.products();
+
+
+ // Append work items that will only be executed if this was an update.
+ // We update the 'opv' key with the current version that is active and 'cmd'
+ // key with the rename command to run.
+ {
+ scoped_ptr<WorkItemList> in_use_update_work_items(
+ WorkItem::CreateConditionalWorkItemList(
+ new ConditionRunIfFileExists(new_chrome_exe)));
+ in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
+
+ FilePath installer_path(package.GetInstallerDirectory(new_version)
+ .Append(setup_path.BaseName()));
+
+ CommandLine rename(installer_path);
+ rename.AppendSwitch(installer::switches::kRenameChromeExe);
+ if (package.system_level())
+ rename.AppendSwitch(installer::switches::kSystemLevel);
+
+ if (InstallUtil::IsChromeSxSProcess())
+ rename.AppendSwitch(installer::switches::kChromeSxS);
+
+ if (multi_install)
+ rename.AppendSwitch(installer::switches::kMultiInstall);
+
+ std::wstring version_key;
+ for (size_t i = 0; i < products.size(); ++i) {
+ BrowserDistribution* dist = products[i]->distribution();
+ version_key = dist->GetVersionKey();
+
+ if (current_version != NULL) {
+ in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegOldVersionField,
+ UTF8ToWide(current_version->GetString()), true);
+ }
+
+ // Adding this registry entry for all products is overkill.
+ // However, as it stands, we don't have a way to know which distribution
+ // will check the key and run the command, so we add it for all.
+ // After the first run, the subsequent runs should just be noops.
+ // (see Upgrade::SwapNewChromeExeIfPresent).
+ in_use_update_work_items->AddSetRegValueWorkItem(
+ root,
+ version_key,
+ google_update::kRegRenameCmdField,
+ rename.command_line_string(),
+ true);
+ }
+
+ if (multi_install) {
+ PackageProperties* props = package.properties();
+ if (props->ReceivesUpdates() && current_version != NULL) {
+ in_use_update_work_items->AddSetRegValueWorkItem(
+ root,
+ props->GetVersionKey(),
+ google_update::kRegOldVersionField,
+ UTF8ToWide(current_version->GetString()),
+ true);
+ // TODO(tommi): We should move the rename command here. We also need to
+ // update Upgrade::SwapNewChromeExeIfPresent.
+ }
+ }
+
+ post_install_task_list->AddWorkItem(in_use_update_work_items.release());
+ }
+
+ // Append work items that will be executed if this was NOT an in-use update.
+ {
+ scoped_ptr<WorkItemList> regular_update_work_items(
+ WorkItem::CreateConditionalWorkItemList(
+ new Not(new ConditionRunIfFileExists(new_chrome_exe))));
+ regular_update_work_items->set_log_message(
+ "RegularUpdateWorkItemList");
+
+ // Since this was not an in-use-update, delete 'opv' and 'cmd' keys.
+ for (size_t i = 0; i < products.size(); ++i) {
+ BrowserDistribution* dist = products[i]->distribution();
+ std::wstring version_key(dist->GetVersionKey());
+ regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
+ google_update::kRegOldVersionField,
+ true);
+ regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
+ google_update::kRegRenameCmdField,
+ true);
+ }
+
+ post_install_task_list->AddWorkItem(regular_update_work_items.release());
+ }
+
+ AddRegisterComDllWorkItemsForPackage(package, current_version, new_version,
+ post_install_task_list);
+
+ for (size_t i = 0; i < products.size(); ++i) {
+ const Product* product = products[i];
+ // If we're told that we're an MSI install, make sure to set the marker
+ // in the client state key so that future updates do the right thing.
+ if (product->IsMsi()) {
+ AddSetMsiMarkerWorkItem(*product, true, post_install_task_list);
+
+ // We want MSI installs to take over the Add/Remove Programs shortcut.
+ // Make a best-effort attempt to delete any shortcuts left over from
+ // previous non-MSI installations for the same type of install (system or
+ // per user).
+ AddDeleteUninstallShortcutsForMSIWorkItems(*product,
+ post_install_task_list);
+ }
+ }
+
+ return true;
+}
+
+void AddInstallWorkItems(const InstallationState& original_state,
+ const InstallerState& installer_state,
+ bool multi_install,
+ const FilePath& setup_path,
+ const FilePath& archive_path,
+ const FilePath& src_path,
+ const FilePath& temp_dir,
+ const Version& new_version,
+ scoped_ptr<Version>* current_version,
+ const Package& package,
+ WorkItemList* install_list) {
+ DCHECK(install_list);
+
+ // A temp directory that work items need and the actual install directory.
+ install_list->AddCreateDirWorkItem(temp_dir);
+ install_list->AddCreateDirWorkItem(package.path());
+
+ // Delete any new_chrome.exe if present (we will end up creating a new one
+ // if required) and then copy chrome.exe
+ FilePath new_chrome_exe(
+ package.path().Append(installer::kChromeNewExe));
+
+ install_list->AddDeleteTreeWorkItem(new_chrome_exe);
+ install_list->AddCopyTreeWorkItem(
+ src_path.Append(installer::kChromeExe).value(),
+ package.path().Append(installer::kChromeExe).value(),
+ temp_dir.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value());
+
+ // Extra executable for 64 bit systems.
+ if (Is64bit()) {
+ install_list->AddCopyTreeWorkItem(
+ src_path.Append(installer::kWowHelperExe).value(),
+ package.path().Append(installer::kWowHelperExe).value(),
+ temp_dir.value(), WorkItem::ALWAYS);
+ }
+
+ // If it is system level install copy the version folder (since we want to
+ // take the permissions of %ProgramFiles% folder) otherwise just move it.
+ if (package.system_level()) {
+ install_list->AddCopyTreeWorkItem(
+ src_path.Append(UTF8ToWide(new_version.GetString())).value(),
+ package.path().Append(UTF8ToWide(new_version.GetString())).value(),
+ temp_dir.value(), WorkItem::ALWAYS);
+ } else {
+ install_list->AddMoveTreeWorkItem(
+ src_path.Append(UTF8ToWide(new_version.GetString())).value(),
+ package.path().Append(UTF8ToWide(new_version.GetString())).value(),
+ temp_dir.value());
+ }
+
+ // Copy the default Dictionaries only if the folder doesn't exist already.
+ install_list->AddCopyTreeWorkItem(
+ src_path.Append(installer::kDictionaries).value(),
+ package.path().Append(installer::kDictionaries).value(),
+ temp_dir.value(), WorkItem::IF_NOT_PRESENT);
+
+ // Delete any old_chrome.exe if present.
+ install_list->AddDeleteTreeWorkItem(
+ package.path().Append(installer::kChromeOldExe));
+
+ // Copy installer in install directory and
+ // add shortcut in Control Panel->Add/Remove Programs.
+ AddInstallerCopyTasks(setup_path, archive_path, temp_dir, new_version,
+ install_list, package);
+
+ HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+
+ const Products& products = package.products();
+ for (size_t i = 0; i < products.size(); ++i) {
+ const Product* product = products[i];
+
+ AddUninstallShortcutWorkItems(setup_path, new_version, install_list,
+ *product);
+
+ AddVersionKeyWorkItems(root, *product, new_version, install_list);
+ }
+
+ if (multi_install) {
+ PackageProperties* props = package.properties();
+ if (props->ReceivesUpdates()) {
+ std::wstring version_key(props->GetVersionKey());
+ install_list->AddCreateRegKeyWorkItem(root, version_key);
+ install_list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegVersionField,
+ UTF8ToWide(new_version.GetString()),
+ true); // overwrite version
+ install_list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegNameField,
+ ASCIIToWide(installer::PackageProperties::kPackageProductName),
+ true); // overwrite name also
+ }
+ }
+
+ // Add any remaining work items that involve special settings for
+ // each product.
+ AddProductSpecificWorkItems(true, setup_path, new_version, package,
+ install_list);
+
+ AddGoogleUpdateWorkItems(original_state, installer_state, package,
+ install_list);
+
+ // Append the tasks that run after the installation.
+ AppendPostInstallTasks(multi_install,
+ setup_path,
+ new_chrome_exe,
+ current_version->get(),
+ new_version,
+ package,
+ install_list);
+}
+
+void AddRegisterComDllWorkItems(const FilePath& dll_folder,
+ const std::vector<FilePath>& dll_list,
+ bool system_level,
+ bool do_register,
+ bool ignore_failures,
+ WorkItemList* work_item_list) {
+ DCHECK(work_item_list);
+ if (dll_list.empty()) {
+ VLOG(1) << "No COM DLLs to register";
+ } else {
+ std::vector<FilePath>::const_iterator dll_iter(dll_list.begin());
+ for (; dll_iter != dll_list.end(); ++dll_iter) {
+ FilePath dll_path = dll_folder.Append(*dll_iter);
+ WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
+ dll_path.value(), do_register, !system_level);
+ DCHECK(work_item);
+ work_item->set_ignore_failure(ignore_failures);
+ }
+ }
+}
+
+void AddSetMsiMarkerWorkItem(const Product& product,
+ bool set,
+ WorkItemList* work_item_list) {
+ DCHECK(work_item_list);
+ BrowserDistribution* dist = product.distribution();
+ HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ DWORD msi_value = set ? 1 : 0;
+ WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
+ reg_root, dist->GetStateKey(), google_update::kRegMSIField,
+ msi_value, true);
+ DCHECK(set_msi_work_item);
+ set_msi_work_item->set_ignore_failure(true);
+ set_msi_work_item->set_log_message("Could not write MSI marker!");
+}
+
+void AddChromeFrameWorkItems(bool install,
+ const FilePath& setup_path,
+ const Version& new_version,
+ const Product& product,
+ WorkItemList* list) {
+ DCHECK(product.is_chrome_frame());
+ if (!product.package().multi_install()) {
+ VLOG(1) << "Not adding GCF specific work items for single install.";
+ return;
+ }
+
+ const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
+
+ BrowserDistribution* cf = BrowserDistribution::GetSpecificDistribution(
+ BrowserDistribution::CHROME_FRAME, prefs);
+ std::wstring version_key(cf->GetVersionKey());
+
+ // TODO(tommi): This assumes we know exactly how ShouldCreateUninstallEntry
+ // is implemented. Since there is logic in ChromeFrameDistribution for how
+ // to determine when this is enabled, this is how we have to figure out if
+ // this feature is enabled right now, but it's a hack and we need a cleaner
+ // way to figure this out.
+ // Note that we cannot just check the master preferences for
+ // kChromeFrameReadyMode, since there are other things that need to be correct
+ // in the environment in order to enable this feature.
+ bool ready_mode = !product.distribution()->ShouldCreateUninstallEntry();
+
+ HKEY root = product.package().system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ bool update_chrome_uninstall_command = false;
+ if (ready_mode) {
+ // If GCF is being installed in ready mode, we write an entry to the
+ // multi-install state key. If the value already exists, we will not
+ // overwrite it since the user might have opted out.
+ list->AddCreateRegKeyWorkItem(root,
+ product.package().properties()->GetStateKey());
+ list->AddSetRegValueWorkItem(root,
+ product.package().properties()->GetStateKey(),
+ installer::kChromeFrameReadyModeField,
+ static_cast<int64>(install ? 1 : 0), // The value we want to set.
+ install ? false : true); // Overwrite existing value.
+ if (install) {
+ FilePath installer_path(product.package()
+ .GetInstallerDirectory(new_version).Append(setup_path.BaseName()));
+
+ CommandLine basic_cl(installer_path);
+ basic_cl.AppendSwitch(installer::switches::kChromeFrame);
+ basic_cl.AppendSwitch(installer::switches::kMultiInstall);
+
+ if (product.package().system_level())
+ basic_cl.AppendSwitch(installer::switches::kSystemLevel);
+
+ if (InstallUtil::IsChromeSxSProcess())
+ basic_cl.AppendSwitch(installer::switches::kChromeSxS);
+
+ CommandLine temp_opt_out(basic_cl);
+ temp_opt_out.AppendSwitch(
+ installer::switches::kChromeFrameReadyModeTempOptOut);
+
+ CommandLine end_temp_opt_out(basic_cl);
+ end_temp_opt_out.AppendSwitch(
+ installer::switches::kChromeFrameReadyModeEndTempOptOut);
+
+ CommandLine opt_out(installer_path);
+ AppendUninstallCommandLineFlags(&opt_out, product);
+ // Force Uninstall silences the prompt to reboot to complete uninstall.
+ opt_out.AppendSwitch(installer::switches::kForceUninstall);
+
+ CommandLine opt_in(basic_cl);
+ opt_in.AppendSwitch(
+ installer::switches::kChromeFrameReadyModeOptIn);
+
+ list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegCFTempOptOutCmdField,
+ temp_opt_out.command_line_string(), true);
+ list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegCFEndTempOptOutCmdField,
+ end_temp_opt_out.command_line_string(),
+ true);
+ list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegCFOptOutCmdField,
+ opt_out.command_line_string(), true);
+ list->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegCFOptInCmdField,
+ opt_in.command_line_string(), true);
+ } else {
+ // If Chrome is not also being uninstalled, we need to update its command
+ // line so that it doesn't include uninstalling Chrome Frame now.
+ update_chrome_uninstall_command =
+ (installer::FindProduct(product.package().products(),
+ BrowserDistribution::CHROME_BROWSER) == NULL);
+ }
+ } else {
+ // It doesn't matter here if we're installing or uninstalling Chrome Frame.
+ // If ready mode isn't specified on the command line for installs, we need
+ // to delete the ready mode flag from the registry if it exists - this
+ // constitutes an opt-in for the user. If we're uninstalling CF and ready
+ // mode isn't specified on the command line, that means that CF wasn't
+ // installed with ready mode enabled (the --ready-mode switch should be set
+ // in the registry) so deleting the value should have no effect.
+ // In both cases (install/uninstall), we need to make sure that Chrome's
+ // uninstallation command line does not include the --chrome-frame switch
+ // so that uninstalling Chrome will no longer uninstall Chrome Frame.
+
+ if (RegKey(root, product.package().properties()->GetStateKey().c_str(),
+ KEY_QUERY_VALUE).Valid()) {
+ list->AddDeleteRegValueWorkItem(root,
+ product.package().properties()->GetStateKey(),
+ installer::kChromeFrameReadyModeField, REG_QWORD);
+ }
+
+ const Product* chrome = installer::FindProduct(product.package().products(),
+ BrowserDistribution::CHROME_BROWSER);
+ if (chrome) {
+ // Chrome is already a part of this installation run, so we can assume
+ // that the uninstallation arguments will be updated correctly.
+ } else {
+ // Chrome is not a part of this installation run, so we have to explicitly
+ // check if Chrome is installed, and if so, update its uninstallation
+ // command lines.
+ BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
+ BrowserDistribution::CHROME_BROWSER,
+ MasterPreferences::ForCurrentProcess());
+ update_chrome_uninstall_command =
+ IsInstalledAsMulti(product.system_level(), dist);
+ }
+ }
+
+ if (!ready_mode || !install) {
+ list->AddDeleteRegValueWorkItem(root, version_key,
+ google_update::kRegCFTempOptOutCmdField,
+ REG_SZ);
+ list->AddDeleteRegValueWorkItem(root, version_key,
+ google_update::kRegCFEndTempOptOutCmdField,
+ REG_SZ);
+ list->AddDeleteRegValueWorkItem(root, version_key,
+ google_update::kRegCFOptOutCmdField,
+ REG_SZ);
+ list->AddDeleteRegValueWorkItem(root, version_key,
+ google_update::kRegCFOptInCmdField, REG_SZ);
+ }
+
+ if (update_chrome_uninstall_command) {
+ // Chrome is not a part of this installation run, so we have to explicitly
+ // check if Chrome is installed, and if so, update its uninstallation
+ // command lines.
+ BrowserDistribution* chrome_dist =
+ BrowserDistribution::GetSpecificDistribution(
+ BrowserDistribution::CHROME_BROWSER, prefs);
+ const Package& pack = product.package();
+ scoped_refptr<Package> package(new Package(pack.multi_install(),
+ pack.system_level(), pack.path(), pack.properties()));
+ scoped_refptr<Product> chrome_product(new Product(chrome_dist, package));
+ AddUninstallShortcutWorkItems(setup_path, new_version, list,
+ *chrome_product.get());
+ }
+}
+
+void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd,
+ const Product& product) {
+ DCHECK(uninstall_cmd);
+
+ uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
+
+ // Append the product-specific uninstall flags.
+ product.distribution()->AppendUninstallCommandLineFlags(uninstall_cmd);
+ if (product.IsMsi()) {
+ uninstall_cmd->AppendSwitch(installer::switches::kMsi);
+ // See comment in uninstall.cc where we check for the kDeleteProfile switch.
+ if (product.is_chrome_frame()) {
+ uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile);
+ }
+ }
+ if (product.system_level())
+ uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
+
+ // Propagate switches obtained from preferences as well.
+ const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
+ if (prefs.is_multi_install()) {
+ uninstall_cmd->AppendSwitch(installer::switches::kMultiInstall);
+ }
+ bool value = false;
+ if (prefs.GetBool(installer::master_preferences::kVerboseLogging,
+ &value) && value)
+ uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
+}
+
+} // namespace installer
diff --git a/chrome/installer/setup/install_worker.h b/chrome/installer/setup/install_worker.h
new file mode 100644
index 0000000..167ed9b
--- /dev/null
+++ b/chrome/installer/setup/install_worker.h
@@ -0,0 +1,98 @@
+// 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.
+//
+// This file contains the declarations of the installer functions that build
+// the WorkItemList used to install the application.
+
+#ifndef CHROME_INSTALLER_SETUP_INSTALL_WORKER_H_
+#define CHROME_INSTALLER_SETUP_INSTALL_WORKER_H_
+#pragma once
+
+#include <vector>
+
+#include "base/scoped_ptr.h"
+
+class CommandLine;
+class FilePath;
+class Version;
+class WorkItemList;
+
+namespace installer {
+
+class InstallationState;
+class InstallerState;
+class Package;
+class Product;
+
+// Builds the complete WorkItemList used to build the set of installation steps
+// needed to lay down one or more installed products.
+//
+// setup_path: Path to the executable (setup.exe) as it will be copied
+// to Chrome install folder after install is complete
+// archive_path: Path to the archive (chrome.7z) as it will be copied
+// to Chrome install folder after install is complete
+// src_path: the path that contains a complete and unpacked Chrome package
+// to be installed.
+// temp_dir: the path of working directory used during installation. This path
+// does not need to exist.
+void AddInstallWorkItems(const InstallationState& original_state,
+ const InstallerState& installer_state,
+ bool multi_install,
+ const FilePath& setup_path,
+ const FilePath& archive_path,
+ const FilePath& src_path,
+ const FilePath& temp_dir,
+ const Version& new_version,
+ scoped_ptr<Version>* current_version,
+ const Package& package,
+ WorkItemList* install_list);
+
+// Appends registration or unregistration work items to |work_item_list| for the
+// COM DLLs whose file names are given in |dll_files| and which reside in the
+// path |dll_folder|.
+// |system_level| specifies whether to call the system or user level DLL
+// registration entry points.
+// |do_register| says whether to register or unregister.
+// |may_fail| states whether this is best effort or not. If |may_fail| is true
+// then |work_item_list| will still succeed if the registration fails and
+// no registration rollback will be performed.
+void AddRegisterComDllWorkItems(const FilePath& dll_folder,
+ const std::vector<FilePath>& dll_files,
+ bool system_level,
+ bool do_register,
+ bool ignore_failures,
+ WorkItemList* work_item_list);
+
+void AddSetMsiMarkerWorkItem(const Product& product,
+ bool set,
+ WorkItemList* work_item_list);
+
+// Called for either installation or uninstallation. This method updates the
+// registry according to Chrome Frame specific options for the current
+// installation. This includes handling of the ready-mode option.
+void AddChromeFrameWorkItems(bool install, const FilePath& setup_path,
+ const Version& new_version, const Product& product,
+ WorkItemList* list);
+
+
+// This method adds work items to create (or update) Chrome uninstall entry in
+// either the Control Panel->Add/Remove Programs list or in the Omaha client
+// state key if running under an MSI installer.
+void AddUninstallShortcutWorkItems(const FilePath& setup_path,
+ const Version& new_version,
+ WorkItemList* install_list,
+ const Product& product);
+
+void AddUninstallShortcutWorkItems(const FilePath& setup_path,
+ const Version& new_version,
+ WorkItemList* install_list,
+ const Product& product);
+
+// Utility method currently shared between install.cc and install_worker.cc
+void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd,
+ const Product& product);
+
+} // namespace installer
+
+#endif // CHROME_INSTALLER_SETUP_INSTALL_WORKER_H_
diff --git a/chrome/installer/setup/install_worker_unittest.cc b/chrome/installer/setup/install_worker_unittest.cc
new file mode 100644
index 0000000..51f066e
--- /dev/null
+++ b/chrome/installer/setup/install_worker_unittest.cc
@@ -0,0 +1,205 @@
+// 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/setup/install_worker.h"
+
+#include "base/win/registry.h"
+#include "base/version.h"
+#include "chrome/installer/util/installation_state.h"
+#include "chrome/installer/util/installer_state.h"
+#include "chrome/installer/util/package.h"
+#include "chrome/installer/util/package_properties.h"
+#include "chrome/installer/util/work_item_list.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using installer::ChromiumPackageProperties;
+using installer::InstallationState;
+using installer::InstallerState;
+using installer::Package;
+using installer::PackageProperties;
+using installer::PackagePropertiesImpl;
+using installer::ProductState;
+
+using ::testing::_;
+using ::testing::AtLeast;
+
+// Mock classes to help with testing
+//------------------------------------------------------------------------------
+
+class MockWorkItemList : public WorkItemList {
+ public:
+ MockWorkItemList() {}
+
+ MOCK_METHOD5(AddCopyTreeWorkItem, WorkItem*(const std::wstring&,
+ const std::wstring&,
+ const std::wstring&,
+ CopyOverWriteOption,
+ const std::wstring&));
+ MOCK_METHOD1(AddCreateDirWorkItem, WorkItem* (const FilePath&));
+ MOCK_METHOD2(AddCreateRegKeyWorkItem, WorkItem* (HKEY, const std::wstring&));
+ MOCK_METHOD2(AddDeleteRegKeyWorkItem, WorkItem* (HKEY, const std::wstring&));
+ MOCK_METHOD4(AddDeleteRegValueWorkItem, WorkItem* (HKEY,
+ const std::wstring&,
+ const std::wstring&,
+ bool));
+ MOCK_METHOD2(AddDeleteTreeWorkItem, WorkItem* (const FilePath&,
+ const std::vector<FilePath>&));
+ MOCK_METHOD1(AddDeleteTreeWorkItem, WorkItem* (const FilePath&));
+ MOCK_METHOD3(AddMoveTreeWorkItem, WorkItem* (const std::wstring&,
+ const std::wstring&,
+ const std::wstring&));
+ MOCK_METHOD5(AddSetRegValueWorkItem, WorkItem*(HKEY,
+ const std::wstring&,
+ const std::wstring&,
+ const std::wstring&,
+ bool));
+ MOCK_METHOD5(AddSetRegValueWorkItem, WorkItem* (HKEY,
+ const std::wstring&,
+ const std::wstring&,
+ DWORD,
+ bool));
+ MOCK_METHOD3(AddSelfRegWorkItem, WorkItem* (const std::wstring&,
+ bool,
+ bool));
+};
+
+// Okay, so this isn't really a mock as such, but it does add setter methods
+// to make it easier to build custom InstallationStates.
+class MockInstallationState : public InstallationState {
+ public:
+ // Included for testing.
+ void SetMultiPackageState(bool system_install,
+ const ProductState& package_state) {
+ ProductState& target =
+ (system_install ? system_products_ : user_products_)
+ [MULTI_PACKAGE_INDEX];
+ target.CopyFrom(package_state);
+ }
+
+ // Included for testing.
+ void SetProductState(bool system_install,
+ BrowserDistribution::Type type,
+ const ProductState& product_state) {
+ ProductState& target = (system_install ? system_products_ :
+ user_products_)[IndexFromDistType(type)];
+ target.CopyFrom(product_state);
+ }
+};
+
+class MockInstallerState : public InstallerState {
+ public:
+ void set_system_install(bool system_install) {
+ system_install_ = system_install;
+ }
+
+ void set_operation(Operation operation) { operation_ = operation; }
+
+ void set_state_key(const std::wstring& state_key) {
+ state_key_ = state_key;
+ }
+};
+
+// The test fixture
+//------------------------------------------------------------------------------
+
+class InstallWorkerTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ current_version_.reset(Version::GetVersionFromString("1.0.0.0"));
+ new_version_.reset(Version::GetVersionFromString("42.0.0.0"));
+
+ // Don't bother ensuring that these paths exist. Since we're just
+ // building the work item lists and not running them, they shouldn't
+ // actually be touched.
+ archive_path_ = FilePath(L"C:\\UnlikelyPath\\Temp\\chrome_123\\chrome.7z");
+ // TODO(robertshield): Take this from the BrowserDistribution once that
+ // no longer depends on MasterPreferences.
+ installation_path_ = FilePath(L"C:\\Program Files\\Google\\Chrome\\");
+ src_path_ =
+ FilePath(L"C:\\UnlikelyPath\\Temp\\chrome_123\\source\\Chrome-bin");
+ setup_path_ = FilePath(L"C:\\UnlikelyPath\\Temp\\CR_123.tmp\\setup.exe");
+ temp_dir_ = FilePath(L"C:\\UnlikelyPath\\Temp\\chrome_123");
+ }
+
+ virtual void TearDown() {
+
+ }
+
+ MockInstallationState* BuildChromeSingleSystemInstallationState() {
+ scoped_ptr<MockInstallationState> installation_state(
+ new MockInstallationState());
+
+ ProductState product_state;
+ product_state.set_version(current_version_->Clone());
+ // Do not call SetMultiPackageState since this is a single install.
+ installation_state->SetProductState(true,
+ BrowserDistribution::CHROME_BROWSER,
+ product_state);
+
+ return installation_state.release();
+ }
+
+ MockInstallerState* BuildChromeSingleSystemInstallerState() {
+ scoped_ptr<MockInstallerState> installer_state(new MockInstallerState());
+
+ installer_state->set_system_install(true);
+ installer_state->set_operation(InstallerState::SINGLE_INSTALL_OR_UPDATE);
+ // Hope this next one isn't checked for now.
+ installer_state->set_state_key(L"PROBABLY_INVALID_REG_PATH");
+
+ return installer_state.release();
+ }
+
+ protected:
+ scoped_ptr<Version> current_version_;
+ scoped_ptr<Version> new_version_;
+ FilePath archive_path_;
+ FilePath installation_path_;
+ FilePath setup_path_;
+ FilePath src_path_;
+ FilePath temp_dir_;
+};
+
+// Tests
+//------------------------------------------------------------------------------
+
+TEST_F(InstallWorkerTest, TestInstallChromeSingleSystem) {
+ MockWorkItemList work_item_list;
+
+ scoped_ptr<InstallationState> installation_state(
+ BuildChromeSingleSystemInstallationState());
+
+ scoped_ptr<InstallerState> installer_state(
+ BuildChromeSingleSystemInstallerState());
+
+ // This MUST outlive the package, since the package doesn't assume ownership
+ // of it. Note: This feels like an implementation bug:
+ // The PackageProperties <-> Package relationship is 1:1 and nothing else
+ // uses the PackageProperties. I have the feeling that PackageProperties, and
+ // perhaps Package itself should not exist at all.
+ scoped_ptr<PackageProperties> package_properties(
+ new ChromiumPackageProperties());
+
+ scoped_refptr<Package> package(
+ new Package(false, true, installation_path_, package_properties.get()));
+
+ // Set up some expectations.
+ // TODO(robertshield): Set up some real expectations.
+ EXPECT_CALL(work_item_list, AddCopyTreeWorkItem(_,_,_,_,_))
+ .Times(AtLeast(1));
+
+ AddInstallWorkItems(*installation_state.get(),
+ *installer_state.get(),
+ false,
+ setup_path_,
+ archive_path_,
+ src_path_,
+ temp_dir_,
+ *new_version_.get(),
+ &current_version_,
+ *package.get(),
+ &work_item_list);
+}
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index f07e703..a886e3b 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -18,6 +18,7 @@
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/installer/setup/install.h"
+#include "chrome/installer/setup/install_worker.h"
#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
diff --git a/chrome/installer/util/installation_state.cc b/chrome/installer/util/installation_state.cc
index 5db624e..85023bf 100644
--- a/chrome/installer/util/installation_state.cc
+++ b/chrome/installer/util/installation_state.cc
@@ -108,14 +108,6 @@ const ProductState* InstallationState::GetMultiPackageState(
return product_state.version_.get() == NULL ? NULL : &product_state;
}
-// Included for testing.
-void InstallationState::SetMultiPackageState(bool system_install,
- const ProductState& package_state) {
- ProductState& target =
- (system_install ? system_products_ : user_products_)[MULTI_PACKAGE_INDEX];
- target.CopyFrom(package_state);
-}
-
const ProductState* InstallationState::GetProductState(
bool system_install,
BrowserDistribution::Type type) const {
@@ -124,12 +116,4 @@ const ProductState* InstallationState::GetProductState(
return product_state.version_.get() == NULL ? NULL : &product_state;
}
-// Included for testing.
-void InstallationState::SetProductState(bool system_install,
- BrowserDistribution::Type type, const ProductState& product_state) {
- ProductState& target = (system_install ? system_products_ :
- user_products_)[IndexFromDistType(type)];
- target.CopyFrom(product_state);
-}
-
} // namespace installer
diff --git a/chrome/installer/util/installation_state.h b/chrome/installer/util/installation_state.h
index 29a4006..3657c62 100644
--- a/chrome/installer/util/installation_state.h
+++ b/chrome/installer/util/installation_state.h
@@ -55,22 +55,15 @@ class InstallationState {
// Returns the state of the multi-installer package or NULL if no
// multi-install products are installed.
+ // Caller does NOT assume ownership of returned pointer.
const ProductState* GetMultiPackageState(bool system_install) const;
- // Sets the state of the multi-installer package; intended for tests.
- void SetMultiPackageState(bool system_install,
- const ProductState& package_state);
-
// Returns the state of a product or NULL if not installed.
+ // Caller does NOT assume ownership of returned pointer.
const ProductState* GetProductState(bool system_install,
BrowserDistribution::Type type) const;
- // Sets the state of a product; indended for tests.
- void SetProductState(bool system_install,
- BrowserDistribution::Type type,
- const ProductState& product_state);
-
- private:
+ protected:
enum {
CHROME_BROWSER_INDEX,
CHROME_FRAME_INDEX,
@@ -89,6 +82,7 @@ class InstallationState {
ProductState user_products_[NUM_PRODUCTS];
ProductState system_products_[NUM_PRODUCTS];
+ private:
DISALLOW_COPY_AND_ASSIGN(InstallationState);
}; // class InstallationState
diff --git a/chrome/installer/util/installer_state.cc b/chrome/installer/util/installer_state.cc
index fd396da..69d2841 100644
--- a/chrome/installer/util/installer_state.cc
+++ b/chrome/installer/util/installer_state.cc
@@ -85,7 +85,6 @@ void InstallerState::Initialize(const MasterPreferences& prefs,
}
if (operand != NULL) {
- install_operand_ = operand->GetType();
state_key_ = operand->GetStateKey();
}
}
diff --git a/chrome/installer/util/installer_state.h b/chrome/installer/util/installer_state.h
index 214ae45..0bc2a5f 100644
--- a/chrome/installer/util/installer_state.h
+++ b/chrome/installer/util/installer_state.h
@@ -38,22 +38,18 @@ class InstallerState {
Operation operation() const { return operation_; }
- BrowserDistribution::Type GetInstallOperand() const {
- return install_operand_;
- }
-
// The ClientState key by which we interact with Google Update.
const std::wstring& state_key() const { return state_key_; }
- private:
+ protected:
bool IsMultiInstallUpdate(const MasterPreferences& prefs,
const InstallationState& machine_state);
Operation operation_;
- BrowserDistribution::Type install_operand_;
std::wstring state_key_;
bool system_install_;
+ private:
DISALLOW_COPY_AND_ASSIGN(InstallerState);
}; // class InstallerState
diff --git a/chrome/installer/util/work_item_list.h b/chrome/installer/util/work_item_list.h
index 3474c34..5792d0f 100644
--- a/chrome/installer/util/work_item_list.h
+++ b/chrome/installer/util/work_item_list.h
@@ -35,31 +35,32 @@ class WorkItemList : public WorkItem {
// Add a WorkItem to the list.
// A WorkItem can only be added to the list before the list's DO() is called.
// Once a WorkItem is added to the list. The list owns the WorkItem.
- void AddWorkItem(WorkItem* work_item);
+ virtual void AddWorkItem(WorkItem* work_item);
// Add a CopyTreeWorkItem to the list of work items.
- WorkItem* AddCopyTreeWorkItem(const std::wstring& source_path,
- const std::wstring& dest_path,
- const std::wstring& temp_dir,
- CopyOverWriteOption overwrite_option,
- const std::wstring& alternative_path = L"");
+ virtual WorkItem* AddCopyTreeWorkItem(
+ const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir,
+ CopyOverWriteOption overwrite_option,
+ const std::wstring& alternative_path = L"");
// Add a CreateDirWorkItem that creates a directory at the given path.
- WorkItem* AddCreateDirWorkItem(const FilePath& path);
+ virtual WorkItem* AddCreateDirWorkItem(const FilePath& path);
// Add a CreateRegKeyWorkItem that creates a registry key at the given
// path.
- WorkItem* AddCreateRegKeyWorkItem(HKEY predefined_root,
- const std::wstring& path);
+ virtual WorkItem* AddCreateRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& path);
// Add a DeleteRegKeyWorkItem that deletes a registry key from the given
// path.
- WorkItem* AddDeleteRegKeyWorkItem(HKEY predefined_root,
- const std::wstring& path);
+ virtual WorkItem* AddDeleteRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& path);
// Add a DeleteRegValueWorkItem that deletes registry value of type REG_SZ
// or REG_DWORD.
- WorkItem* AddDeleteRegValueWorkItem(HKEY predefined_root,
+ virtual WorkItem* AddDeleteRegValueWorkItem(HKEY predefined_root,
const std::wstring& key_path,
const std::wstring& value_name,
DWORD type);
@@ -67,32 +68,33 @@ class WorkItemList : public WorkItem {
// Add a DeleteTreeWorkItem that recursively deletes a file system
// hierarchy at the given root path. A key file can be optionally specified
// by key_path.
- WorkItem* AddDeleteTreeWorkItem(const FilePath& root_path,
- const std::vector<FilePath>& key_paths);
+ virtual WorkItem* AddDeleteTreeWorkItem(
+ const FilePath& root_path,
+ const std::vector<FilePath>& key_paths);
// Same as above but without support for key files.
- WorkItem* AddDeleteTreeWorkItem(const FilePath& root_path);
+ virtual WorkItem* AddDeleteTreeWorkItem(const FilePath& root_path);
// Add a MoveTreeWorkItem to the list of work items.
- WorkItem* AddMoveTreeWorkItem(const std::wstring& source_path,
- const std::wstring& dest_path,
- const std::wstring& temp_dir);
+ virtual WorkItem* AddMoveTreeWorkItem(const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir);
// Add a SetRegValueWorkItem that sets a registry value with REG_SZ type
// at the key with specified path.
- WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- const std::wstring& value_data,
- bool overwrite);
+ virtual WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ const std::wstring& value_data,
+ bool overwrite);
// Add a SetRegValueWorkItem that sets a registry value with REG_DWORD type
// at the key with specified path.
- WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- DWORD value_data,
- bool overwrite);
+ virtual WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ DWORD value_data,
+ bool overwrite);
// Add a SetRegValueWorkItem that sets a registry value with REG_QWORD type
// at the key with specified path.
@@ -105,9 +107,9 @@ class WorkItemList : public WorkItem {
// Add a SelfRegWorkItem that registers or unregisters a DLL at the
// specified path. If user_level_registration is true, then alternate
// registration and unregistration entry point names will be used.
- WorkItem* AddSelfRegWorkItem(const std::wstring& dll_path,
- bool do_register,
- bool user_level_registration);
+ virtual WorkItem* AddSelfRegWorkItem(const std::wstring& dll_path,
+ bool do_register,
+ bool user_level_registration);
protected:
friend class WorkItem;