Chromium Code Reviews| Index: chrome/installer/setup/install_worker.cc |
| =================================================================== |
| --- chrome/installer/setup/install_worker.cc (revision 0) |
| +++ chrome/installer/setup/install_worker.cc (revision 0) |
| @@ -0,0 +1,816 @@ |
| +// 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, PBOOL); |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
BOOL* instead of PBOOL.
robertshield
2011/01/13 17:06:32
Done.
|
| + BOOL is64 = FALSE; |
|
amit
2011/01/12 17:41:22
nit: is_64bit :)
robertshield
2011/01/13 17:06:32
Done.
|
| + |
| + HANDLE handle = GetCurrentProcess(); |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
'handle' is less descriptive than just calling Get
robertshield
2011/01/13 17:06:32
Done. (In case it wasn't clear, this code is copy
|
| + HMODULE module = GetModuleHandle(L"kernel32.dll"); |
| + WOW_FUNC p = reinterpret_cast<WOW_FUNC>(GetProcAddress(module, |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
nit: is_wow_64 instead of p
robertshield
2011/01/13 17:06:32
Done.
|
| + "IsWow64Process")); |
| + if ((p != NULL) && (!(p)(handle, &is64) || (is64 != FALSE))) { |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
You don't really need the if and two return statem
robertshield
2011/01/13 17:06:32
Done.
|
| + return true; |
| + } |
| + |
| + return false; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace installer { |
| + |
|
erikwright (departed)
2011/01/12 16:31:49
I assume these are identical to the originals?
tommi (sloooow) - chröme
2011/01/12 19:02:49
+1 (to save on review time)
robertshield
2011/01/13 17:06:32
Yes, copy/pasted.
robertshield
2011/01/13 17:06:32
Done.
|
| +// 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, |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
observation - we should factor out all these facto
robertshield
2011/01/13 17:06:32
I'm mocking the WorkItemList at the moment which a
|
| + L"DisplayIcon", chrome_icon.value(), |
| + true); |
| + install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| + L"NoModify", 1, true); |
| + install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, |
| + L"NoRepair", 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); |
| + } |
| + } |
| +} |
| + |
| + |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
one empty line
robertshield
2011/01/13 17:06:32
Done.
|
| + |
| +// 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, 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); |
| + } |
| + } |
| +} |
| + |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
one empty line
robertshield
2011/01/13 17:06:32
Done.
|
| + |
| +// 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."); |
| + } |
| +} |
| + |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
one empty line (and throughout)
robertshield
2011/01/13 17:06:32
Done.
|
| + |
| +// 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(); |
| + |
|
tommi (sloooow) - chröme
2011/01/12 19:02:49
here too :)
robertshield
2011/01/13 17:06:32
Done.
|
| + |
| + // 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; |
| + } |
| + |
| + // 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, |
| + install ? 1 : 0, // The value we want to set. |
| + install ? false : true); // Overwrite existing value. |
| + if (!install) { |
| + // 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, false); |
| + } |
| + |
| + 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 (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. |
| + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); |
| + 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 |
| Property changes on: chrome\installer\setup\install_worker.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |