Home | History | Annotate | Download | only in setup
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // This file contains the definitions of the installer functions that build
      6 // the WorkItemList used to install the application.
      7 
      8 #include "chrome/installer/setup/install_worker.h"
      9 
     10 #include <oaidl.h>
     11 #include <shlobj.h>
     12 #include <time.h>
     13 
     14 #include <vector>
     15 
     16 #include "base/bind.h"
     17 #include "base/command_line.h"
     18 #include "base/file_util.h"
     19 #include "base/files/file_path.h"
     20 #include "base/logging.h"
     21 #include "base/memory/scoped_ptr.h"
     22 #include "base/path_service.h"
     23 #include "base/strings/string16.h"
     24 #include "base/strings/string_util.h"
     25 #include "base/strings/utf_string_conversions.h"
     26 #include "base/version.h"
     27 #include "base/win/registry.h"
     28 #include "base/win/scoped_comptr.h"
     29 #include "base/win/windows_version.h"
     30 #include "chrome/common/chrome_constants.h"
     31 #include "chrome/common/chrome_switches.h"
     32 #include "chrome/installer/setup/install.h"
     33 #include "chrome/installer/setup/setup_constants.h"
     34 #include "chrome/installer/setup/setup_util.h"
     35 #include "chrome/installer/util/browser_distribution.h"
     36 #include "chrome/installer/util/callback_work_item.h"
     37 #include "chrome/installer/util/conditional_work_item_list.h"
     38 #include "chrome/installer/util/create_reg_key_work_item.h"
     39 #include "chrome/installer/util/google_update_constants.h"
     40 #include "chrome/installer/util/helper.h"
     41 #include "chrome/installer/util/install_util.h"
     42 #include "chrome/installer/util/installation_state.h"
     43 #include "chrome/installer/util/installer_state.h"
     44 #include "chrome/installer/util/l10n_string_util.h"
     45 #include "chrome/installer/util/product.h"
     46 #include "chrome/installer/util/set_reg_value_work_item.h"
     47 #include "chrome/installer/util/shell_util.h"
     48 #include "chrome/installer/util/util_constants.h"
     49 #include "chrome/installer/util/work_item_list.h"
     50 
     51 #if !defined(OMIT_CHROME_FRAME)
     52 #include "chrome_frame/chrome_tab.h"
     53 #endif
     54 
     55 using base::win::RegKey;
     56 
     57 namespace installer {
     58 
     59 namespace {
     60 
     61 enum ElevationPolicyId {
     62   CURRENT_ELEVATION_POLICY,
     63   OLD_ELEVATION_POLICY,
     64 };
     65 
     66 // The version identifying the work done by setup.exe --configure-user-settings
     67 // on user login by way of Active Setup.  Increase this value if the work done
     68 // in setup_main.cc's handling of kConfigureUserSettings changes and should be
     69 // executed again for all users.
     70 const wchar_t kActiveSetupVersion[] = L"24,0,0,0";
     71 
     72 // Although the UUID of the ChromeFrame class is used for the "current" value,
     73 // this is done only as a convenience; there is no need for the GUID of the Low
     74 // Rights policies to match the ChromeFrame class's GUID.  Hence, it is safe to
     75 // use this completely unrelated GUID for the "old" policies.
     76 const wchar_t kIELowRightsPolicyOldGuid[] =
     77     L"{6C288DD7-76FB-4721-B628-56FAC252E199}";
     78 
     79 #if defined(OMIT_CHROME_FRAME)
     80 // For historical reasons, this GUID is the same as CLSID_ChromeFrame. Included
     81 // here to break the dependency on Chrome Frame when Chrome Frame is not being
     82 // built.
     83 // TODO(robertshield): Remove this when Chrome Frame works with Aura.
     84 const wchar_t kIELowRightsPolicyCurrentGuid[] =
     85     L"{E0A900DF-9611-4446-86BD-4B1D47E7DB2A}";
     86 #endif
     87 
     88 const wchar_t kElevationPolicyKeyPath[] =
     89     L"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\";
     90 
     91 void GetIELowRightsElevationPolicyKeyPath(ElevationPolicyId policy,
     92                                           string16* key_path) {
     93   DCHECK(policy == CURRENT_ELEVATION_POLICY || policy == OLD_ELEVATION_POLICY);
     94   key_path->assign(kElevationPolicyKeyPath,
     95                    arraysize(kElevationPolicyKeyPath) - 1);
     96   if (policy == CURRENT_ELEVATION_POLICY) {
     97 #if defined(OMIT_CHROME_FRAME)
     98     key_path->append(kIELowRightsPolicyCurrentGuid,
     99                      arraysize(kIELowRightsPolicyCurrentGuid) - 1);
    100 #else
    101     wchar_t cf_clsid[64];
    102     int len = StringFromGUID2(__uuidof(ChromeFrame), &cf_clsid[0],
    103                               arraysize(cf_clsid));
    104     key_path->append(&cf_clsid[0], len - 1);
    105 #endif
    106   } else {
    107     key_path->append(kIELowRightsPolicyOldGuid,
    108                      arraysize(kIELowRightsPolicyOldGuid)- 1);
    109   }
    110 }
    111 
    112 // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
    113 // products managed by a given package.
    114 // |old_version| can be NULL to indicate no Chrome is currently installed.
    115 void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state,
    116                                           const Version* old_version,
    117                                           const Version& new_version,
    118                                           WorkItemList* work_item_list) {
    119   // First collect the list of DLLs to be registered from each product.
    120   std::vector<base::FilePath> com_dll_list;
    121   installer_state.AddComDllList(&com_dll_list);
    122 
    123   // Then, if we got some, attempt to unregister the DLLs from the old
    124   // version directory and then re-register them in the new one.
    125   // Note that if we are migrating the install directory then we will not
    126   // successfully unregister the old DLLs.
    127   // TODO(robertshield): See whether we need to fix the migration case.
    128   // TODO(robertshield): If we ever remove a DLL from a product, this will
    129   // not unregister it on update. We should build the unregistration list from
    130   // saved state instead of assuming it is the same as the registration list.
    131   if (!com_dll_list.empty()) {
    132     if (old_version) {
    133       base::FilePath old_dll_path(installer_state.target_path().AppendASCII(
    134           old_version->GetString()));
    135 
    136       installer::AddRegisterComDllWorkItems(old_dll_path,
    137                                             com_dll_list,
    138                                             installer_state.system_install(),
    139                                             false,  // Unregister
    140                                             true,   // May fail
    141                                             work_item_list);
    142     }
    143 
    144     base::FilePath dll_path(installer_state.target_path().AppendASCII(
    145         new_version.GetString()));
    146     installer::AddRegisterComDllWorkItems(dll_path,
    147                                           com_dll_list,
    148                                           installer_state.system_install(),
    149                                           true,   // Register
    150                                           false,  // Must succeed.
    151                                           work_item_list);
    152   }
    153 }
    154 
    155 void AddInstallerCopyTasks(const InstallerState& installer_state,
    156                            const base::FilePath& setup_path,
    157                            const base::FilePath& archive_path,
    158                            const base::FilePath& temp_path,
    159                            const Version& new_version,
    160                            WorkItemList* install_list) {
    161   DCHECK(install_list);
    162   base::FilePath installer_dir(
    163       installer_state.GetInstallerDirectory(new_version));
    164   install_list->AddCreateDirWorkItem(installer_dir);
    165 
    166   base::FilePath exe_dst(installer_dir.Append(setup_path.BaseName()));
    167 
    168   if (exe_dst != setup_path) {
    169     install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(),
    170                                       temp_path.value(), WorkItem::ALWAYS);
    171   }
    172 
    173   if (installer_state.RequiresActiveSetup()) {
    174     // Make a copy of setup.exe with a different name so that Active Setup
    175     // doesn't require an admin on XP thanks to Application Compatibility.
    176     base::FilePath active_setup_exe(installer_dir.Append(kActiveSetupExe));
    177     install_list->AddCopyTreeWorkItem(
    178         setup_path.value(), active_setup_exe.value(), temp_path.value(),
    179         WorkItem::ALWAYS);
    180   }
    181 
    182   // If only the App Host (not even the Chrome Binaries) is being installed,
    183   // this must be a user-level App Host piggybacking on system-level Chrome
    184   // Binaries. Only setup.exe is required, and only for uninstall.
    185   if (installer_state.products().size() != 1 ||
    186       !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
    187     base::FilePath archive_dst(installer_dir.Append(archive_path.BaseName()));
    188     if (archive_path != archive_dst) {
    189       // In the past, we copied rather than moved for system level installs so
    190       // that the permissions of %ProgramFiles% would be picked up.  Now that
    191       // |temp_path| is in %ProgramFiles% for system level installs (and in
    192       // %LOCALAPPDATA% otherwise), there is no need to do this for the archive.
    193       // Setup.exe, on the other hand, is created elsewhere so it must always be
    194       // copied.
    195       if (temp_path.IsParent(archive_path)) {
    196         install_list->AddMoveTreeWorkItem(archive_path.value(),
    197                                           archive_dst.value(),
    198                                           temp_path.value(),
    199                                           WorkItem::ALWAYS_MOVE);
    200       } else {
    201         // This may occur when setup is run out of an existing installation
    202         // directory. For example, when quick-enabling user-level App Launcher
    203         // from system-level Binaries. We can't (and don't want to) remove the
    204         // system-level archive.
    205         install_list->AddCopyTreeWorkItem(archive_path.value(),
    206                                           archive_dst.value(),
    207                                           temp_path.value(),
    208                                           WorkItem::ALWAYS);
    209       }
    210     }
    211   }
    212 }
    213 
    214 string16 GetRegCommandKey(BrowserDistribution* dist,
    215                           const wchar_t* name) {
    216   string16 cmd_key(dist->GetVersionKey());
    217   cmd_key.append(1, base::FilePath::kSeparators[0])
    218       .append(google_update::kRegCommandsKey)
    219       .append(1, base::FilePath::kSeparators[0])
    220       .append(name);
    221   return cmd_key;
    222 }
    223 
    224 // Adds work items to create (or delete if uninstalling) app commands to launch
    225 // the app with a switch. The following criteria should be true:
    226 //  1. The switch takes one parameter.
    227 //  2. The command send pings.
    228 //  3. The command is web accessible.
    229 //  4. The command is run as the user.
    230 void AddCommandWithParameterWorkItems(const InstallerState& installer_state,
    231                                       const InstallationState& machine_state,
    232                                       const Version& new_version,
    233                                       const Product& product,
    234                                       const wchar_t* command_key,
    235                                       const wchar_t* app,
    236                                       const char* command_with_parameter,
    237                                       WorkItemList* work_item_list) {
    238   DCHECK(command_key);
    239   DCHECK(app);
    240   DCHECK(command_with_parameter);
    241   DCHECK(work_item_list);
    242 
    243   string16 full_cmd_key(GetRegCommandKey(product.distribution(), command_key));
    244 
    245   if (installer_state.operation() == InstallerState::UNINSTALL) {
    246     work_item_list->AddDeleteRegKeyWorkItem(
    247         installer_state.root_key(), full_cmd_key)->set_log_message(
    248             "removing " + WideToASCII(command_key) + " command");
    249   } else {
    250     CommandLine cmd_line(installer_state.target_path().Append(app));
    251     cmd_line.AppendSwitchASCII(command_with_parameter, "%1");
    252 
    253     AppCommand cmd(cmd_line.GetCommandLineString());
    254     cmd.set_sends_pings(true);
    255     cmd.set_is_web_accessible(true);
    256     cmd.set_is_run_as_user(true);
    257     cmd.AddWorkItems(installer_state.root_key(), full_cmd_key, work_item_list);
    258   }
    259 }
    260 
    261 void AddInstallAppCommandWorkItems(const InstallerState& installer_state,
    262                                    const InstallationState& machine_state,
    263                                    const Version& new_version,
    264                                    const Product& product,
    265                                    WorkItemList* work_item_list) {
    266   DCHECK(product.is_chrome_app_host());
    267   AddCommandWithParameterWorkItems(installer_state, machine_state, new_version,
    268                                    product, kCmdInstallApp,
    269                                    installer::kChromeAppHostExe,
    270                                    ::switches::kInstallFromWebstore,
    271                                    work_item_list);
    272 }
    273 
    274 void AddInstallExtensionCommandWorkItem(const InstallerState& installer_state,
    275                                         const InstallationState& machine_state,
    276                                         const base::FilePath& setup_path,
    277                                         const Version& new_version,
    278                                         const Product& product,
    279                                         WorkItemList* work_item_list) {
    280   DCHECK(product.is_chrome());
    281   AddCommandWithParameterWorkItems(installer_state, machine_state, new_version,
    282                                    product, kCmdInstallExtension,
    283                                    installer::kChromeExe,
    284                                    ::switches::kLimitedInstallFromWebstore,
    285                                    work_item_list);
    286 }
    287 
    288 // Returns the basic CommandLine to setup.exe for a quick-enable operation on
    289 // the binaries. This will unconditionally include --multi-install as well as
    290 // --verbose-logging if the current installation was launched with
    291 // --verbose-logging.  |setup_path| and |new_version| are optional only when
    292 // the operation is an uninstall.
    293 CommandLine GetGenericQuickEnableCommand(
    294     const InstallerState& installer_state,
    295     const InstallationState& machine_state,
    296     const base::FilePath& setup_path,
    297     const Version& new_version) {
    298   // Only valid for multi-install operations.
    299   DCHECK(installer_state.is_multi_install());
    300   // Only valid when Chrome Binaries aren't being uninstalled.
    301   DCHECK(installer_state.operation() != InstallerState::UNINSTALL ||
    302          !installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES));
    303   // setup_path and new_version are required when not uninstalling.
    304   DCHECK(installer_state.operation() == InstallerState::UNINSTALL ||
    305          (!setup_path.empty() && new_version.IsValid()));
    306 
    307   // The path to setup.exe contains the version of the Chrome Binaries, so it
    308   // takes a little work to get it right.
    309   base::FilePath binaries_setup_path;
    310   if (installer_state.operation() == InstallerState::UNINSTALL) {
    311     // One or more products are being uninstalled, but not Chrome Binaries.
    312     // Use the path to the currently installed Chrome Binaries' setup.exe.
    313     const ProductState* product_state = machine_state.GetProductState(
    314         installer_state.system_install(),
    315         BrowserDistribution::CHROME_BINARIES);
    316     DCHECK(product_state);
    317     binaries_setup_path = product_state->uninstall_command().GetProgram();
    318   } else {
    319     // Chrome Binaries are being installed, updated, or otherwise operated on.
    320     // Use the path to the given |setup_path| in the normal location of
    321     // multi-install Chrome Binaries of the given |version|.
    322     binaries_setup_path = installer_state.GetInstallerDirectory(new_version)
    323                               .Append(setup_path.BaseName());
    324   }
    325   DCHECK(!binaries_setup_path.empty());
    326 
    327   CommandLine cmd_line(binaries_setup_path);
    328   cmd_line.AppendSwitch(switches::kMultiInstall);
    329   if (installer_state.verbose_logging())
    330     cmd_line.AppendSwitch(switches::kVerboseLogging);
    331   return cmd_line;
    332 }
    333 
    334 // Adds work items to add the "quick-enable-application-host" command to the
    335 // multi-installer binaries' version key on the basis of the current operation
    336 // (represented in |installer_state|) and the pre-existing machine configuration
    337 // (represented in |machine_state|).
    338 void AddQuickEnableApplicationLauncherWorkItems(
    339     const InstallerState& installer_state,
    340     const InstallationState& machine_state,
    341     const base::FilePath& setup_path,
    342     const Version& new_version,
    343     WorkItemList* work_item_list) {
    344   DCHECK(work_item_list);
    345 
    346   bool will_have_chrome_binaries =
    347       WillProductBePresentAfterSetup(installer_state, machine_state,
    348                                      BrowserDistribution::CHROME_BINARIES);
    349 
    350   // For system-level binaries there is no way to keep the command state in sync
    351   // with the installation/uninstallation of the Application Launcher (which is
    352   // always at user-level).  So we do not try to remove the command, i.e., it
    353   // will always be installed if the Chrome Binaries are installed.
    354   if (will_have_chrome_binaries) {
    355     string16 cmd_key(GetRegCommandKey(
    356                          BrowserDistribution::GetSpecificDistribution(
    357                              BrowserDistribution::CHROME_BINARIES),
    358                          kCmdQuickEnableApplicationHost));
    359     CommandLine cmd_line(GetGenericQuickEnableCommand(installer_state,
    360                                                       machine_state,
    361                                                       setup_path,
    362                                                       new_version));
    363     // kMultiInstall and kVerboseLogging were processed above.
    364     cmd_line.AppendSwitch(switches::kChromeAppLauncher);
    365     cmd_line.AppendSwitch(switches::kEnsureGoogleUpdatePresent);
    366     AppCommand cmd(cmd_line.GetCommandLineString());
    367     cmd.set_sends_pings(true);
    368     cmd.set_is_web_accessible(true);
    369     cmd.set_is_run_as_user(true);
    370     cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
    371   }
    372 }
    373 
    374 void AddProductSpecificWorkItems(const InstallationState& original_state,
    375                                  const InstallerState& installer_state,
    376                                  const base::FilePath& setup_path,
    377                                  const Version& new_version,
    378                                  WorkItemList* list) {
    379   const Products& products = installer_state.products();
    380   for (Products::const_iterator it = products.begin(); it < products.end();
    381        ++it) {
    382     const Product& p = **it;
    383     if (p.is_chrome_frame()) {
    384       AddChromeFrameWorkItems(original_state, installer_state, setup_path,
    385                               new_version, p, list);
    386     }
    387     if (p.is_chrome_app_host()) {
    388       AddInstallAppCommandWorkItems(installer_state, original_state,
    389                                     new_version, p, list);
    390     }
    391     if (p.is_chrome()) {
    392       AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p,
    393                             list);
    394       AddInstallExtensionCommandWorkItem(installer_state, original_state,
    395                                          setup_path, new_version, p, list);
    396     }
    397     if (p.is_chrome_binaries()) {
    398       AddQueryEULAAcceptanceWorkItems(
    399           installer_state, setup_path, new_version, p, list);
    400       AddQuickEnableChromeFrameWorkItems(
    401           installer_state, original_state, setup_path, new_version, list);
    402       AddQuickEnableApplicationLauncherWorkItems(
    403           installer_state, original_state, setup_path, new_version, list);
    404     }
    405   }
    406 }
    407 
    408 // This is called when an MSI installation is run. It may be that a user is
    409 // attempting to install the MSI on top of a non-MSI managed installation.
    410 // If so, try and remove any existing uninstallation shortcuts, as we want the
    411 // uninstall to be managed entirely by the MSI machinery (accessible via the
    412 // Add/Remove programs dialog).
    413 void AddDeleteUninstallShortcutsForMSIWorkItems(
    414     const InstallerState& installer_state,
    415     const Product& product,
    416     const base::FilePath& temp_path,
    417     WorkItemList* work_item_list) {
    418   DCHECK(installer_state.is_msi())
    419       << "This must only be called for MSI installations!";
    420 
    421   // First attempt to delete the old installation's ARP dialog entry.
    422   HKEY reg_root = installer_state.root_key();
    423   string16 uninstall_reg(product.distribution()->GetUninstallRegPath());
    424 
    425   WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
    426       reg_root, uninstall_reg);
    427   delete_reg_key->set_ignore_failure(true);
    428 
    429   // Then attempt to delete the old installation's start menu shortcut.
    430   base::FilePath uninstall_link;
    431   if (installer_state.system_install()) {
    432     PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
    433   } else {
    434     PathService::Get(base::DIR_START_MENU, &uninstall_link);
    435   }
    436 
    437   if (uninstall_link.empty()) {
    438     LOG(ERROR) << "Failed to get location for shortcut.";
    439   } else {
    440     uninstall_link = uninstall_link.Append(
    441         product.distribution()->GetAppShortCutName());
    442     uninstall_link = uninstall_link.Append(
    443         product.distribution()->GetUninstallLinkName() + installer::kLnkExt);
    444     VLOG(1) << "Deleting old uninstall shortcut (if present): "
    445             << uninstall_link.value();
    446     WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
    447         uninstall_link, temp_path);
    448     delete_link->set_ignore_failure(true);
    449     delete_link->set_log_message(
    450         "Failed to delete old uninstall shortcut.");
    451   }
    452 }
    453 
    454 // Adds Chrome specific install work items to |install_list|.
    455 // |current_version| can be NULL to indicate no Chrome is currently installed.
    456 void AddChromeWorkItems(const InstallationState& original_state,
    457                         const InstallerState& installer_state,
    458                         const base::FilePath& setup_path,
    459                         const base::FilePath& archive_path,
    460                         const base::FilePath& src_path,
    461                         const base::FilePath& temp_path,
    462                         const Version* current_version,
    463                         const Version& new_version,
    464                         WorkItemList* install_list) {
    465   const base::FilePath& target_path = installer_state.target_path();
    466 
    467   if (current_version) {
    468     // Delete the archive from an existing install to save some disk space.  We
    469     // make this an unconditional work item since there's no need to roll this
    470     // back; if installation fails we'll be moved to the "-full" channel anyway.
    471     base::FilePath old_installer_dir(
    472         installer_state.GetInstallerDirectory(*current_version));
    473     base::FilePath old_archive(
    474         old_installer_dir.Append(installer::kChromeArchive));
    475     // Don't delete the archive that we are actually installing from.
    476     if (archive_path != old_archive) {
    477       install_list->AddDeleteTreeWorkItem(old_archive, temp_path)->
    478           set_ignore_failure(true);
    479     }
    480   }
    481 
    482   // Delete any new_chrome.exe if present (we will end up creating a new one
    483   // if required) and then copy chrome.exe
    484   base::FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe));
    485 
    486   install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path);
    487 
    488   if (installer_state.IsChromeFrameRunning(original_state)) {
    489     VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe";
    490     install_list->AddCopyTreeWorkItem(
    491         src_path.Append(installer::kChromeExe).value(),
    492         new_chrome_exe.value(),
    493         temp_path.value(),
    494         WorkItem::ALWAYS);
    495   } else {
    496     install_list->AddCopyTreeWorkItem(
    497         src_path.Append(installer::kChromeExe).value(),
    498         target_path.Append(installer::kChromeExe).value(),
    499         temp_path.value(),
    500         WorkItem::NEW_NAME_IF_IN_USE,
    501         new_chrome_exe.value());
    502   }
    503 
    504   // Extra executable for 64 bit systems.
    505   // NOTE: We check for "not disabled" so that if the API call fails, we play it
    506   // safe and copy the executable anyway.
    507   // NOTE: the file wow_helper.exe is only needed for Vista and below.
    508   if (base::win::OSInfo::GetInstance()->wow64_status() !=
    509       base::win::OSInfo::WOW64_DISABLED &&
    510       base::win::GetVersion() <= base::win::VERSION_VISTA) {
    511     install_list->AddMoveTreeWorkItem(
    512         src_path.Append(installer::kWowHelperExe).value(),
    513         target_path.Append(installer::kWowHelperExe).value(),
    514         temp_path.value(),
    515         WorkItem::ALWAYS_MOVE);
    516   }
    517 
    518   // Install kVisualElementsManifest if it is present in |src_path|. No need to
    519   // make this a conditional work item as if the file is not there now, it will
    520   // never be.
    521   if (base::PathExists(
    522           src_path.Append(installer::kVisualElementsManifest))) {
    523     install_list->AddMoveTreeWorkItem(
    524         src_path.Append(installer::kVisualElementsManifest).value(),
    525         target_path.Append(installer::kVisualElementsManifest).value(),
    526         temp_path.value(),
    527         WorkItem::ALWAYS_MOVE);
    528   } else {
    529     // We do not want to have an old VisualElementsManifest pointing to an old
    530     // version directory. Delete it as there wasn't a new one to replace it.
    531     install_list->AddDeleteTreeWorkItem(
    532         target_path.Append(installer::kVisualElementsManifest),
    533         temp_path);
    534   }
    535 
    536   // For the component build to work with the installer, we need to drop a
    537   // config file and a manifest by chrome.exe. These files are only found in
    538   // the archive if this is a component build.
    539 #if defined(COMPONENT_BUILD)
    540   static const base::FilePath::CharType kChromeExeConfig[] =
    541       FILE_PATH_LITERAL("chrome.exe.config");
    542   static const base::FilePath::CharType kChromeExeManifest[] =
    543       FILE_PATH_LITERAL("chrome.exe.manifest");
    544   install_list->AddMoveTreeWorkItem(
    545       src_path.Append(kChromeExeConfig).value(),
    546       target_path.Append(kChromeExeConfig).value(),
    547       temp_path.value(),
    548       WorkItem::ALWAYS_MOVE);
    549   install_list->AddMoveTreeWorkItem(
    550       src_path.Append(kChromeExeManifest).value(),
    551       target_path.Append(kChromeExeManifest).value(),
    552       temp_path.value(),
    553       WorkItem::ALWAYS_MOVE);
    554 #endif  // defined(COMPONENT_BUILD)
    555 
    556   // In the past, we copied rather than moved for system level installs so that
    557   // the permissions of %ProgramFiles% would be picked up.  Now that |temp_path|
    558   // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
    559   // otherwise), there is no need to do this.
    560   // Note that we pass true for check_duplicates to avoid failing on in-use
    561   // repair runs if the current_version is the same as the new_version.
    562   bool check_for_duplicates = (current_version &&
    563                                current_version->Equals(new_version));
    564   install_list->AddMoveTreeWorkItem(
    565       src_path.AppendASCII(new_version.GetString()).value(),
    566       target_path.AppendASCII(new_version.GetString()).value(),
    567       temp_path.value(),
    568       check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
    569                              WorkItem::ALWAYS_MOVE);
    570 
    571   // Delete any old_chrome.exe if present (ignore failure if it's in use).
    572   install_list->AddDeleteTreeWorkItem(
    573       target_path.Append(installer::kChromeOldExe), temp_path)->
    574           set_ignore_failure(true);
    575 }
    576 
    577 // Probes COM machinery to get an instance of delegate_execute.exe's
    578 // CommandExecuteImpl class.  This is required so that COM purges its cache of
    579 // the path to the binary, which changes on updates.  This callback
    580 // unconditionally returns true since an install should not be aborted if the
    581 // probe fails.
    582 bool ProbeCommandExecuteCallback(const string16& command_execute_id,
    583                                  const CallbackWorkItem& work_item) {
    584   // Noop on rollback.
    585   if (work_item.IsRollback())
    586     return true;
    587 
    588   CLSID class_id = {};
    589 
    590   HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
    591   if (FAILED(hr)) {
    592     LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
    593                    "CLSID; hr=0x" << std::hex << hr;
    594   } else {
    595     base::win::ScopedComPtr<IUnknown> command_execute_impl;
    596     hr = command_execute_impl.CreateInstance(class_id, NULL,
    597                                              CLSCTX_LOCAL_SERVER);
    598     if (hr != REGDB_E_CLASSNOTREG) {
    599       LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x"
    600                  << std::hex << hr;
    601     }
    602   }
    603 
    604   return true;
    605 }
    606 
    607 void AddUninstallDelegateExecuteWorkItems(HKEY root,
    608                                           const string16& delegate_execute_path,
    609                                           WorkItemList* list) {
    610   VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
    611           << root;
    612   list->AddDeleteRegKeyWorkItem(root, delegate_execute_path);
    613 
    614   // In the past, the ICommandExecuteImpl interface and a TypeLib were both
    615   // registered.  Remove these since this operation may be updating a machine
    616   // that had the old registrations.
    617   list->AddDeleteRegKeyWorkItem(root,
    618                                 L"Software\\Classes\\Interface\\"
    619                                 L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}");
    620   list->AddDeleteRegKeyWorkItem(root,
    621                                 L"Software\\Classes\\TypeLib\\"
    622 #if defined(GOOGLE_CHROME_BUILD)
    623                                 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}"
    624 #else
    625                                 L"{7779FB70-B399-454A-AA1A-BAA850032B10}"
    626 #endif
    627                                 );
    628 }
    629 
    630 // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
    631 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by
    632 // mistake (with the same GUID as Chrome). The fix stopped registering the bad
    633 // value, but didn't delete it. This is a problem for users who had installed
    634 // Canary before 20.0.1106.0 and now have a system-level Chrome, as the
    635 // left-behind Canary registrations in HKCU mask the HKLM registrations for the
    636 // same GUID. Cleanup those registrations if they still exist and belong to this
    637 // Canary (i.e., the registered delegate_execute's path is under |target_path|).
    638 void CleanupBadCanaryDelegateExecuteRegistration(
    639     const base::FilePath& target_path,
    640     WorkItemList* list) {
    641   string16 google_chrome_delegate_execute_path(
    642       L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
    643   string16 google_chrome_local_server_32(
    644       google_chrome_delegate_execute_path + L"\\LocalServer32");
    645 
    646   RegKey local_server_32_key;
    647   string16 registered_server;
    648   if (local_server_32_key.Open(HKEY_CURRENT_USER,
    649                                google_chrome_local_server_32.c_str(),
    650                                KEY_QUERY_VALUE) == ERROR_SUCCESS &&
    651       local_server_32_key.ReadValue(L"ServerExecutable",
    652                                     &registered_server) == ERROR_SUCCESS &&
    653       target_path.IsParent(base::FilePath(registered_server))) {
    654     scoped_ptr<WorkItemList> no_rollback_list(
    655         WorkItem::CreateNoRollbackWorkItemList());
    656     AddUninstallDelegateExecuteWorkItems(
    657         HKEY_CURRENT_USER, google_chrome_delegate_execute_path,
    658         no_rollback_list.get());
    659     list->AddWorkItem(no_rollback_list.release());
    660     VLOG(1) << "Added deletion items for bad Canary registrations.";
    661   }
    662 }
    663 
    664 }  // namespace
    665 
    666 // This method adds work items to create (or update) Chrome uninstall entry in
    667 // either the Control Panel->Add/Remove Programs list or in the Omaha client
    668 // state key if running under an MSI installer.
    669 void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
    670                                    const base::FilePath& setup_path,
    671                                    const Version& new_version,
    672                                    const Product& product,
    673                                    WorkItemList* install_list) {
    674   HKEY reg_root = installer_state.root_key();
    675   BrowserDistribution* browser_dist = product.distribution();
    676   DCHECK(browser_dist);
    677 
    678   // When we are installed via an MSI, we need to store our uninstall strings
    679   // in the Google Update client state key. We do this even for non-MSI
    680   // managed installs to avoid breaking the edge case whereby an MSI-managed
    681   // install is updated by a non-msi installer (which would confuse the MSI
    682   // machinery if these strings were not also updated). The UninstallString
    683   // value placed in the client state key is also used by the mini_installer to
    684   // locate the setup.exe instance used for binary patching.
    685   // Do not quote the command line for the MSI invocation.
    686   base::FilePath install_path(installer_state.target_path());
    687   base::FilePath installer_path(
    688       installer_state.GetInstallerDirectory(new_version));
    689   installer_path = installer_path.Append(setup_path.BaseName());
    690 
    691   CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
    692   AppendUninstallCommandLineFlags(installer_state, product,
    693                                   &uninstall_arguments);
    694 
    695   // If Chrome Frame is installed in Ready Mode, add --chrome-frame to Chrome's
    696   // uninstall entry. We skip this processing in case of uninstall since this
    697   // means that Chrome Frame is being uninstalled, so there's no need to do any
    698   // looping.
    699   if (product.is_chrome() &&
    700       installer_state.operation() != InstallerState::UNINSTALL) {
    701     const Product* chrome_frame =
    702         installer_state.FindProduct(BrowserDistribution::CHROME_FRAME);
    703     if (chrome_frame && chrome_frame->HasOption(kOptionReadyMode))
    704       chrome_frame->AppendProductFlags(&uninstall_arguments);
    705   }
    706 
    707   string16 update_state_key(browser_dist->GetStateKey());
    708   install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
    709   install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
    710       installer::kUninstallStringField, installer_path.value(), true);
    711   install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
    712       installer::kUninstallArgumentsField,
    713       uninstall_arguments.GetCommandLineString(), true);
    714 
    715   // MSI installations will manage their own uninstall shortcuts.
    716   if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
    717     // We need to quote the command line for the Add/Remove Programs dialog.
    718     CommandLine quoted_uninstall_cmd(installer_path);
    719     DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"');
    720     quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
    721 
    722     string16 uninstall_reg = browser_dist->GetUninstallRegPath();
    723     install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
    724     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    725         installer::kUninstallDisplayNameField,
    726         browser_dist->GetAppShortCutName(), true);
    727     install_list->AddSetRegValueWorkItem(reg_root,
    728         uninstall_reg, installer::kUninstallStringField,
    729         quoted_uninstall_cmd.GetCommandLineString(), true);
    730     install_list->AddSetRegValueWorkItem(reg_root,
    731                                          uninstall_reg,
    732                                          L"InstallLocation",
    733                                          install_path.value(),
    734                                          true);
    735 
    736     BrowserDistribution* dist = product.distribution();
    737     string16 chrome_icon = ShellUtil::FormatIconLocation(
    738         install_path.Append(dist->GetIconFilename()).value(),
    739         dist->GetIconIndex());
    740     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    741                                          L"DisplayIcon", chrome_icon, true);
    742     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    743                                          L"NoModify", static_cast<DWORD>(1),
    744                                          true);
    745     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    746                                          L"NoRepair", static_cast<DWORD>(1),
    747                                          true);
    748 
    749     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    750                                          L"Publisher",
    751                                          browser_dist->GetPublisherName(),
    752                                          true);
    753     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    754                                          L"Version",
    755                                          ASCIIToWide(new_version.GetString()),
    756                                          true);
    757     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    758                                          L"DisplayVersion",
    759                                          ASCIIToWide(new_version.GetString()),
    760                                          true);
    761     install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    762                                          L"InstallDate",
    763                                          InstallUtil::GetCurrentDate(),
    764                                          false);
    765 
    766     const std::vector<uint16>& version_components = new_version.components();
    767     if (version_components.size() == 4) {
    768       // Our version should be in major.minor.build.rev.
    769       install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    770           L"VersionMajor", static_cast<DWORD>(version_components[2]), true);
    771       install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
    772           L"VersionMinor", static_cast<DWORD>(version_components[3]), true);
    773     }
    774   }
    775 }
    776 
    777 // Create Version key for a product (if not already present) and sets the new
    778 // product version as the last step.
    779 void AddVersionKeyWorkItems(HKEY root,
    780                             BrowserDistribution* dist,
    781                             const Version& new_version,
    782                             bool add_language_identifier,
    783                             WorkItemList* list) {
    784   // Create Version key for each distribution (if not already present) and set
    785   // the new product version as the last step.
    786   string16 version_key(dist->GetVersionKey());
    787   list->AddCreateRegKeyWorkItem(root, version_key);
    788 
    789   string16 product_name(dist->GetAppShortCutName());
    790   list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
    791                                product_name, true);  // overwrite name also
    792   list->AddSetRegValueWorkItem(root, version_key,
    793                                google_update::kRegOopcrashesField,
    794                                static_cast<DWORD>(1),
    795                                false);  // set during first install
    796   if (add_language_identifier) {
    797     // Write the language identifier of the current translation.  Omaha's set of
    798     // languages is a superset of Chrome's set of translations with this one
    799     // exception: what Chrome calls "en-us", Omaha calls "en".  sigh.
    800     string16 language(GetCurrentTranslation());
    801     if (LowerCaseEqualsASCII(language, "en-us"))
    802       language.resize(2);
    803     list->AddSetRegValueWorkItem(root, version_key,
    804                                  google_update::kRegLangField, language,
    805                                  false);  // do not overwrite language
    806   }
    807   list->AddSetRegValueWorkItem(root, version_key,
    808                                google_update::kRegVersionField,
    809                                ASCIIToWide(new_version.GetString()),
    810                                true);  // overwrite version
    811 }
    812 
    813 // Mirror oeminstall the first time anything is installed multi.  There is no
    814 // need to update the value on future install/update runs since this value never
    815 // changes.  Note that the value is removed by Google Update after EULA
    816 // acceptance is processed.
    817 void AddOemInstallWorkItems(const InstallationState& original_state,
    818                             const InstallerState& installer_state,
    819                             WorkItemList* install_list) {
    820   DCHECK(installer_state.is_multi_install());
    821   const bool system_install = installer_state.system_install();
    822   if (!original_state.GetProductState(system_install,
    823                                       BrowserDistribution::CHROME_BINARIES)) {
    824     const HKEY root_key = installer_state.root_key();
    825     string16 multi_key(
    826         installer_state.multi_package_binaries_distribution()->GetStateKey());
    827 
    828     // Copy the value from Chrome unless Chrome isn't installed or being
    829     // installed.
    830     BrowserDistribution::Type source_type;
    831     if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) {
    832       source_type = BrowserDistribution::CHROME_BROWSER;
    833     } else if (!installer_state.products().empty()) {
    834       // Pick a product, any product.
    835       source_type = installer_state.products()[0]->distribution()->GetType();
    836     } else {
    837       // Nothing is being installed?  Entirely unexpected, so do no harm.
    838       LOG(ERROR) << "No products found in AddOemInstallWorkItems";
    839       return;
    840     }
    841     const ProductState* source_product =
    842         original_state.GetNonVersionedProductState(system_install, source_type);
    843 
    844     string16 oem_install;
    845     if (source_product->GetOemInstall(&oem_install)) {
    846       VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from "
    847               << BrowserDistribution::GetSpecificDistribution(source_type)->
    848                     GetAppShortCutName();
    849       install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
    850       // Always overwrite an old value.
    851       install_list->AddSetRegValueWorkItem(root_key, multi_key,
    852                                            google_update::kRegOemInstallField,
    853                                            oem_install, true);
    854     } else {
    855       // Clear any old value.
    856       install_list->AddDeleteRegValueWorkItem(
    857           root_key, multi_key, google_update::kRegOemInstallField);
    858     }
    859   }
    860 }
    861 
    862 // Mirror eulaaccepted the first time anything is installed multi.  There is no
    863 // need to update the value on future install/update runs since
    864 // GoogleUpdateSettings::SetEULAConsent will modify the value for both the
    865 // relevant product and for the binaries.
    866 void AddEulaAcceptedWorkItems(const InstallationState& original_state,
    867                               const InstallerState& installer_state,
    868                               WorkItemList* install_list) {
    869   DCHECK(installer_state.is_multi_install());
    870   const bool system_install = installer_state.system_install();
    871   if (!original_state.GetProductState(system_install,
    872                                       BrowserDistribution::CHROME_BINARIES)) {
    873     const HKEY root_key = installer_state.root_key();
    874     string16 multi_key(
    875         installer_state.multi_package_binaries_distribution()->GetStateKey());
    876 
    877     // Copy the value from the product with the greatest value.
    878     bool have_eula_accepted = false;
    879     BrowserDistribution::Type product_type;
    880     DWORD eula_accepted;
    881     const Products& products = installer_state.products();
    882     for (Products::const_iterator it = products.begin(); it < products.end();
    883          ++it) {
    884       const Product& product = **it;
    885       if (product.is_chrome_binaries())
    886         continue;
    887       DWORD dword_value = 0;
    888       BrowserDistribution::Type this_type = product.distribution()->GetType();
    889       const ProductState* product_state =
    890           original_state.GetNonVersionedProductState(
    891               system_install, this_type);
    892       if (product_state->GetEulaAccepted(&dword_value) &&
    893           (!have_eula_accepted || dword_value > eula_accepted)) {
    894         have_eula_accepted = true;
    895         eula_accepted = dword_value;
    896         product_type = this_type;
    897       }
    898     }
    899 
    900     if (have_eula_accepted) {
    901       VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from "
    902               << BrowserDistribution::GetSpecificDistribution(product_type)->
    903                      GetAppShortCutName();
    904       install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
    905       install_list->AddSetRegValueWorkItem(
    906           root_key, multi_key, google_update::kRegEULAAceptedField,
    907           eula_accepted, true);
    908     } else {
    909       // Clear any old value.
    910       install_list->AddDeleteRegValueWorkItem(
    911           root_key, multi_key, google_update::kRegEULAAceptedField);
    912     }
    913   }
    914 }
    915 
    916 // Adds work items that make registry adjustments for Google Update.
    917 void AddGoogleUpdateWorkItems(const InstallationState& original_state,
    918                               const InstallerState& installer_state,
    919                               WorkItemList* install_list) {
    920   // Is a multi-install product being installed or over-installed?
    921   if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
    922       installer_state.operation() != InstallerState::MULTI_UPDATE) {
    923     VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
    924     return;
    925   }
    926 
    927   const bool system_install = installer_state.system_install();
    928   const HKEY root_key = installer_state.root_key();
    929   string16 multi_key(
    930       installer_state.multi_package_binaries_distribution()->GetStateKey());
    931 
    932   // For system-level installs, make sure the ClientStateMedium key for the
    933   // binaries exists.
    934   if (system_install) {
    935     install_list->AddCreateRegKeyWorkItem(
    936         root_key,
    937         installer_state.multi_package_binaries_distribution()->
    938             GetStateMediumKey().c_str());
    939   }
    940 
    941   // Creating the ClientState key for binaries, if we're migrating to multi then
    942   // copy over Chrome's brand code if it has one. Chrome Frame currently never
    943   // has a brand code.
    944   if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
    945     const ProductState* chrome_product_state =
    946         original_state.GetNonVersionedProductState(
    947             system_install, BrowserDistribution::CHROME_BROWSER);
    948 
    949     const string16& brand(chrome_product_state->brand());
    950     if (!brand.empty()) {
    951       install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
    952       // Write Chrome's brand code to the multi key. Never overwrite the value
    953       // if one is already present (although this shouldn't happen).
    954       install_list->AddSetRegValueWorkItem(root_key,
    955                                            multi_key,
    956                                            google_update::kRegBrandField,
    957                                            brand,
    958                                            false);
    959     }
    960   }
    961 
    962   AddOemInstallWorkItems(original_state, installer_state, install_list);
    963   AddEulaAcceptedWorkItems(original_state, installer_state, install_list);
    964   AddUsageStatsWorkItems(original_state, installer_state, install_list);
    965 
    966   // TODO(grt): check for other keys/values we should put in the package's
    967   // ClientState and/or Clients key.
    968 }
    969 
    970 void AddUsageStatsWorkItems(const InstallationState& original_state,
    971                             const InstallerState& installer_state,
    972                             WorkItemList* install_list) {
    973   DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
    974          installer_state.operation() == InstallerState::MULTI_UPDATE);
    975 
    976   HKEY root_key = installer_state.root_key();
    977   bool value_found = false;
    978   DWORD usagestats = 0;
    979   const Products& products = installer_state.products();
    980 
    981   // Search for an existing usagestats value for any product.
    982   for (Products::const_iterator scan = products.begin(), end = products.end();
    983        !value_found && scan != end; ++scan) {
    984     if ((*scan)->is_chrome_binaries())
    985       continue;
    986     BrowserDistribution* dist = (*scan)->distribution();
    987     const ProductState* product_state =
    988         original_state.GetNonVersionedProductState(
    989             installer_state.system_install(), dist->GetType());
    990     value_found = product_state->GetUsageStats(&usagestats);
    991   }
    992 
    993   // If a value was found, write it in the appropriate location for the
    994   // binaries and remove all values from the products.
    995   if (value_found) {
    996     string16 state_key(
    997         installer_state.multi_package_binaries_distribution()->GetStateKey());
    998     install_list->AddCreateRegKeyWorkItem(root_key, state_key);
    999     // Overwrite any existing value so that overinstalls (where Omaha writes a
   1000     // new value into a product's state key) pick up the correct value.
   1001     install_list->AddSetRegValueWorkItem(root_key, state_key,
   1002                                          google_update::kRegUsageStatsField,
   1003                                          usagestats, true);
   1004 
   1005     for (Products::const_iterator scan = products.begin(), end = products.end();
   1006          scan != end; ++scan) {
   1007       if ((*scan)->is_chrome_binaries())
   1008         continue;
   1009       BrowserDistribution* dist = (*scan)->distribution();
   1010       if (installer_state.system_install()) {
   1011         install_list->AddDeleteRegValueWorkItem(
   1012             root_key, dist->GetStateMediumKey(),
   1013             google_update::kRegUsageStatsField);
   1014         // Previous versions of Chrome also wrote a value in HKCU even for
   1015         // system-level installs, so clean that up.
   1016         install_list->AddDeleteRegValueWorkItem(
   1017             HKEY_CURRENT_USER, dist->GetStateKey(),
   1018             google_update::kRegUsageStatsField);
   1019       }
   1020       install_list->AddDeleteRegValueWorkItem(
   1021           root_key, dist->GetStateKey(), google_update::kRegUsageStatsField);
   1022     }
   1023   }
   1024 }
   1025 
   1026 bool AppendPostInstallTasks(const InstallerState& installer_state,
   1027                             const base::FilePath& setup_path,
   1028                             const Version* current_version,
   1029                             const Version& new_version,
   1030                             const base::FilePath& temp_path,
   1031                             WorkItemList* post_install_task_list) {
   1032   DCHECK(post_install_task_list);
   1033 
   1034   HKEY root = installer_state.root_key();
   1035   const Products& products = installer_state.products();
   1036   base::FilePath new_chrome_exe(
   1037       installer_state.target_path().Append(installer::kChromeNewExe));
   1038 
   1039   // Append work items that will only be executed if this was an update.
   1040   // We update the 'opv' value with the current version that is active,
   1041   // the 'cpv' value with the critical update version (if present), and the
   1042   // 'cmd' value with the rename command to run.
   1043   {
   1044     scoped_ptr<WorkItemList> in_use_update_work_items(
   1045         WorkItem::CreateConditionalWorkItemList(
   1046             new ConditionRunIfFileExists(new_chrome_exe)));
   1047     in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
   1048 
   1049     // |critical_version| will be valid only if this in-use update includes a
   1050     // version considered critical relative to the version being updated.
   1051     Version critical_version(installer_state.DetermineCriticalVersion(
   1052         current_version, new_version));
   1053     base::FilePath installer_path(
   1054         installer_state.GetInstallerDirectory(new_version).Append(
   1055             setup_path.BaseName()));
   1056 
   1057     CommandLine rename(installer_path);
   1058     rename.AppendSwitch(switches::kRenameChromeExe);
   1059     if (installer_state.system_install())
   1060       rename.AppendSwitch(switches::kSystemLevel);
   1061 
   1062     if (installer_state.verbose_logging())
   1063       rename.AppendSwitch(switches::kVerboseLogging);
   1064 
   1065     string16 version_key;
   1066     for (size_t i = 0; i < products.size(); ++i) {
   1067       BrowserDistribution* dist = products[i]->distribution();
   1068       version_key = dist->GetVersionKey();
   1069 
   1070       if (current_version) {
   1071         in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
   1072             google_update::kRegOldVersionField,
   1073             ASCIIToWide(current_version->GetString()), true);
   1074       }
   1075       if (critical_version.IsValid()) {
   1076         in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
   1077             google_update::kRegCriticalVersionField,
   1078             ASCIIToWide(critical_version.GetString()), true);
   1079       } else {
   1080         in_use_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
   1081             google_update::kRegCriticalVersionField);
   1082       }
   1083 
   1084       // Adding this registry entry for all products (but the binaries) is
   1085       // overkill. However, as it stands, we don't have a way to know which
   1086       // product will check the key and run the command, so we add it for all.
   1087       // The first to run it will perform the operation and clean up the other
   1088       // values.
   1089       if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
   1090         CommandLine product_rename_cmd(rename);
   1091         products[i]->AppendRenameFlags(&product_rename_cmd);
   1092         in_use_update_work_items->AddSetRegValueWorkItem(
   1093             root, version_key, google_update::kRegRenameCmdField,
   1094             product_rename_cmd.GetCommandLineString(), true);
   1095       }
   1096     }
   1097 
   1098     if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
   1099       AddCopyIELowRightsPolicyWorkItems(installer_state,
   1100                                         in_use_update_work_items.get());
   1101     }
   1102 
   1103     post_install_task_list->AddWorkItem(in_use_update_work_items.release());
   1104   }
   1105 
   1106   // Append work items that will be executed if this was NOT an in-use update.
   1107   {
   1108     scoped_ptr<WorkItemList> regular_update_work_items(
   1109         WorkItem::CreateConditionalWorkItemList(
   1110             new Not(new ConditionRunIfFileExists(new_chrome_exe))));
   1111     regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
   1112 
   1113     // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
   1114     for (size_t i = 0; i < products.size(); ++i) {
   1115       BrowserDistribution* dist = products[i]->distribution();
   1116       string16 version_key(dist->GetVersionKey());
   1117       regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
   1118           google_update::kRegOldVersionField);
   1119       regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
   1120           google_update::kRegCriticalVersionField);
   1121       regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
   1122           google_update::kRegRenameCmdField);
   1123     }
   1124 
   1125     if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
   1126       AddDeleteOldIELowRightsPolicyWorkItems(installer_state,
   1127                                              regular_update_work_items.get());
   1128     }
   1129 
   1130     post_install_task_list->AddWorkItem(regular_update_work_items.release());
   1131   }
   1132 
   1133   AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
   1134                                        new_version, post_install_task_list);
   1135 
   1136   // If we're told that we're an MSI install, make sure to set the marker
   1137   // in the client state key so that future updates do the right thing.
   1138   if (installer_state.is_msi()) {
   1139     for (size_t i = 0; i < products.size(); ++i) {
   1140       const Product* product = products[i];
   1141       AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
   1142                               post_install_task_list);
   1143 
   1144       // We want MSI installs to take over the Add/Remove Programs shortcut.
   1145       // Make a best-effort attempt to delete any shortcuts left over from
   1146       // previous non-MSI installations for the same type of install (system or
   1147       // per user).
   1148       if (product->ShouldCreateUninstallEntry()) {
   1149         AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
   1150                                                    temp_path,
   1151                                                    post_install_task_list);
   1152       }
   1153     }
   1154   }
   1155 
   1156   return true;
   1157 }
   1158 
   1159 void AddInstallWorkItems(const InstallationState& original_state,
   1160                          const InstallerState& installer_state,
   1161                          const base::FilePath& setup_path,
   1162                          const base::FilePath& archive_path,
   1163                          const base::FilePath& src_path,
   1164                          const base::FilePath& temp_path,
   1165                          const Version* current_version,
   1166                          const Version& new_version,
   1167                          WorkItemList* install_list) {
   1168   DCHECK(install_list);
   1169 
   1170   const base::FilePath& target_path = installer_state.target_path();
   1171 
   1172   // A temp directory that work items need and the actual install directory.
   1173   install_list->AddCreateDirWorkItem(temp_path);
   1174   install_list->AddCreateDirWorkItem(target_path);
   1175 
   1176   if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
   1177       installer_state.FindProduct(BrowserDistribution::CHROME_FRAME) ||
   1178       installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
   1179     AddChromeWorkItems(original_state,
   1180                        installer_state,
   1181                        setup_path,
   1182                        archive_path,
   1183                        src_path,
   1184                        temp_path,
   1185                        current_version,
   1186                        new_version,
   1187                        install_list);
   1188   }
   1189 
   1190   if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
   1191     install_list->AddCopyTreeWorkItem(
   1192         src_path.Append(installer::kChromeAppHostExe).value(),
   1193         target_path.Append(installer::kChromeAppHostExe).value(),
   1194         temp_path.value(),
   1195         WorkItem::ALWAYS,
   1196         L"");
   1197   }
   1198 
   1199   // Copy installer in install directory
   1200   AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
   1201                         new_version, install_list);
   1202 
   1203   const HKEY root = installer_state.root_key();
   1204   // Only set "lang" for user-level installs since for system-level, the install
   1205   // language may not be related to a given user's runtime language.
   1206   const bool add_language_identifier = !installer_state.system_install();
   1207 
   1208   const Products& products = installer_state.products();
   1209   for (Products::const_iterator it = products.begin(); it < products.end();
   1210        ++it) {
   1211     const Product& product = **it;
   1212 
   1213     AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
   1214                                   product, install_list);
   1215 
   1216     AddVersionKeyWorkItems(root, product.distribution(), new_version,
   1217                            add_language_identifier, install_list);
   1218 
   1219     AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
   1220                                 product, install_list);
   1221 
   1222     AddActiveSetupWorkItems(installer_state, setup_path, new_version, product,
   1223                             install_list);
   1224   }
   1225 
   1226   // TODO(huangs): Implement actual migration code and remove the hack below.
   1227   // If installing Chrome without the legacy stand-alone App Launcher (to be
   1228   // handled later), add "shadow" App Launcher registry keys so Google Update
   1229   // would recognize the "dr" value in the App Launcher ClientState key.
   1230   // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome.
   1231   if (installer_state.is_multi_install() &&
   1232       installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) &&
   1233       !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
   1234     BrowserDistribution* shadow_app_launcher_dist =
   1235         BrowserDistribution::GetSpecificDistribution(
   1236             BrowserDistribution::CHROME_APP_HOST);
   1237     AddVersionKeyWorkItems(root, shadow_app_launcher_dist, new_version,
   1238                            add_language_identifier, install_list);
   1239   }
   1240 
   1241   // Add any remaining work items that involve special settings for
   1242   // each product.
   1243   AddProductSpecificWorkItems(original_state, installer_state, setup_path,
   1244                               new_version, install_list);
   1245 
   1246   // Copy over brand, usagestats, and other values.
   1247   AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
   1248 
   1249   // Append the tasks that run after the installation.
   1250   AppendPostInstallTasks(installer_state,
   1251                          setup_path,
   1252                          current_version,
   1253                          new_version,
   1254                          temp_path,
   1255                          install_list);
   1256 }
   1257 
   1258 void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
   1259                                 const std::vector<base::FilePath>& dll_list,
   1260                                 bool system_level,
   1261                                 bool do_register,
   1262                                 bool ignore_failures,
   1263                                 WorkItemList* work_item_list) {
   1264   DCHECK(work_item_list);
   1265   if (dll_list.empty()) {
   1266     VLOG(1) << "No COM DLLs to register";
   1267   } else {
   1268     std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
   1269     for (; dll_iter != dll_list.end(); ++dll_iter) {
   1270       base::FilePath dll_path = dll_folder.Append(*dll_iter);
   1271       WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
   1272           dll_path.value(), do_register, !system_level);
   1273       DCHECK(work_item);
   1274       work_item->set_ignore_failure(ignore_failures);
   1275     }
   1276   }
   1277 }
   1278 
   1279 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
   1280                              BrowserDistribution* dist,
   1281                              bool set,
   1282                              WorkItemList* work_item_list) {
   1283   DCHECK(work_item_list);
   1284   DWORD msi_value = set ? 1 : 0;
   1285   WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
   1286       installer_state.root_key(), dist->GetStateKey(),
   1287       google_update::kRegMSIField, msi_value, true);
   1288   DCHECK(set_msi_work_item);
   1289   set_msi_work_item->set_ignore_failure(true);
   1290   set_msi_work_item->set_log_message("Could not write MSI marker!");
   1291 }
   1292 
   1293 void AddChromeFrameWorkItems(const InstallationState& original_state,
   1294                              const InstallerState& installer_state,
   1295                              const base::FilePath& setup_path,
   1296                              const Version& new_version,
   1297                              const Product& product,
   1298                              WorkItemList* list) {
   1299   DCHECK(product.is_chrome_frame());
   1300   if (!installer_state.is_multi_install()) {
   1301     VLOG(1) << "Not adding GCF specific work items for single install.";
   1302     return;
   1303   }
   1304 
   1305   string16 version_key(product.distribution()->GetVersionKey());
   1306   bool ready_mode = product.HasOption(kOptionReadyMode);
   1307   HKEY root = installer_state.root_key();
   1308   const bool is_install =
   1309       (installer_state.operation() != InstallerState::UNINSTALL);
   1310   bool update_chrome_uninstall_command = false;
   1311   BrowserDistribution* dist =
   1312       installer_state.multi_package_binaries_distribution();
   1313   if (ready_mode) {
   1314     // If GCF is being installed in ready mode, we write an entry to the
   1315     // multi-install state key.  If the value already exists, we will not
   1316     // overwrite it since the user might have opted out.
   1317     list->AddCreateRegKeyWorkItem(root, dist->GetStateKey());
   1318     list->AddSetRegValueWorkItem(root, dist->GetStateKey(),
   1319         kChromeFrameReadyModeField,
   1320         static_cast<int64>(is_install ? 1 : 0),  // The value we want to set.
   1321         !is_install);  // Overwrite existing value.
   1322     if (is_install) {
   1323       base::FilePath installer_path(installer_state
   1324           .GetInstallerDirectory(new_version).Append(setup_path.BaseName()));
   1325 
   1326       CommandLine basic_cl(installer_path);
   1327       basic_cl.AppendSwitch(switches::kChromeFrame);
   1328       basic_cl.AppendSwitch(switches::kMultiInstall);
   1329 
   1330       if (installer_state.system_install())
   1331         basic_cl.AppendSwitch(switches::kSystemLevel);
   1332 
   1333       CommandLine temp_opt_out(basic_cl);
   1334       temp_opt_out.AppendSwitch(switches::kChromeFrameReadyModeTempOptOut);
   1335 
   1336       CommandLine end_temp_opt_out(basic_cl);
   1337       end_temp_opt_out.AppendSwitch(
   1338           switches::kChromeFrameReadyModeEndTempOptOut);
   1339 
   1340       CommandLine opt_out(installer_path);
   1341       AppendUninstallCommandLineFlags(installer_state, product, &opt_out);
   1342       // Force Uninstall silences the prompt to reboot to complete uninstall.
   1343       opt_out.AppendSwitch(switches::kForceUninstall);
   1344 
   1345       CommandLine opt_in(basic_cl);
   1346       opt_in.AppendSwitch(switches::kChromeFrameReadyModeOptIn);
   1347 
   1348       list->AddSetRegValueWorkItem(root, version_key,
   1349                                    google_update::kRegCFTempOptOutCmdField,
   1350                                    temp_opt_out.GetCommandLineString(), true);
   1351       list->AddSetRegValueWorkItem(root, version_key,
   1352                                    google_update::kRegCFEndTempOptOutCmdField,
   1353                                    end_temp_opt_out.GetCommandLineString(),
   1354                                    true);
   1355       list->AddSetRegValueWorkItem(root, version_key,
   1356                                    google_update::kRegCFOptOutCmdField,
   1357                                    opt_out.GetCommandLineString(), true);
   1358       list->AddSetRegValueWorkItem(root, version_key,
   1359                                    google_update::kRegCFOptInCmdField,
   1360                                    opt_in.GetCommandLineString(), true);
   1361     } else {
   1362       // If Chrome is not also being uninstalled, we need to update its command
   1363       // line so that it doesn't include uninstalling Chrome Frame now.
   1364       update_chrome_uninstall_command =
   1365           (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ==
   1366            NULL);
   1367     }
   1368   } else {
   1369     // It doesn't matter here if we're installing or uninstalling Chrome Frame.
   1370     // If ready mode isn't specified on the command line for installs, we need
   1371     // to delete the ready mode flag from the registry if it exists - this
   1372     // constitutes an opt-in for the user.  If we're uninstalling CF and ready
   1373     // mode isn't specified on the command line, that means that CF wasn't
   1374     // installed with ready mode enabled (the --ready-mode switch should be set
   1375     // in the registry) so deleting the value should have no effect.
   1376     // In both cases (install/uninstall), we need to make sure that Chrome's
   1377     // uninstallation command line does not include the --chrome-frame switch
   1378     // so that uninstalling Chrome will no longer uninstall Chrome Frame.
   1379 
   1380     list->AddDeleteRegValueWorkItem(root, dist->GetStateKey(),
   1381         kChromeFrameReadyModeField);
   1382 
   1383     const Product* chrome =
   1384         installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
   1385     if (chrome) {
   1386       // Chrome is already a part of this installation run, so we can assume
   1387       // that the uninstallation arguments will be updated correctly.
   1388     } else {
   1389       // Chrome is not a part of this installation run, so we have to explicitly
   1390       // check if Chrome is installed, and if so, update its uninstallation
   1391       // command lines.
   1392       const ProductState* chrome_state = original_state.GetProductState(
   1393           installer_state.system_install(),
   1394           BrowserDistribution::CHROME_BROWSER);
   1395       update_chrome_uninstall_command =
   1396           (chrome_state != NULL) && chrome_state->is_multi_install();
   1397     }
   1398   }
   1399 
   1400   if (!ready_mode || !is_install) {
   1401     list->AddDeleteRegValueWorkItem(root, version_key,
   1402                                     google_update::kRegCFTempOptOutCmdField);
   1403     list->AddDeleteRegValueWorkItem(root, version_key,
   1404                                     google_update::kRegCFEndTempOptOutCmdField);
   1405     list->AddDeleteRegValueWorkItem(root, version_key,
   1406                                     google_update::kRegCFOptOutCmdField);
   1407     list->AddDeleteRegValueWorkItem(root, version_key,
   1408                                     google_update::kRegCFOptInCmdField);
   1409   }
   1410 
   1411   if (update_chrome_uninstall_command) {
   1412     // Chrome is not a part of this installation run, so we have to explicitly
   1413     // check if Chrome is installed, and if so, update its uninstallation
   1414     // command lines.
   1415     const ProductState* chrome_state = original_state.GetProductState(
   1416         installer_state.system_install(), BrowserDistribution::CHROME_BROWSER);
   1417     if (chrome_state != NULL) {
   1418       DCHECK(chrome_state->is_multi_install());
   1419       Product chrome(BrowserDistribution::GetSpecificDistribution(
   1420                          BrowserDistribution::CHROME_BROWSER));
   1421       chrome.InitializeFromUninstallCommand(chrome_state->uninstall_command());
   1422       AddUninstallShortcutWorkItems(installer_state, setup_path,
   1423                                     chrome_state->version(), chrome, list);
   1424     } else {
   1425       NOTREACHED() << "What happened to Chrome?";
   1426     }
   1427   }
   1428 }
   1429 
   1430 void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
   1431                                  const base::FilePath& target_path,
   1432                                  const Version& new_version,
   1433                                  const Product& product,
   1434                                  WorkItemList* list) {
   1435   string16 handler_class_uuid;
   1436   BrowserDistribution* distribution = product.distribution();
   1437   if (!distribution->GetCommandExecuteImplClsid(&handler_class_uuid)) {
   1438     if (InstallUtil::IsChromeSxSProcess()) {
   1439       CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
   1440     } else {
   1441       VLOG(1) << "No DelegateExecute verb handler processing to do for "
   1442               << distribution->GetAppShortCutName();
   1443     }
   1444     return;
   1445   }
   1446 
   1447   HKEY root = installer_state.root_key();
   1448   string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
   1449   delegate_execute_path.append(handler_class_uuid);
   1450 
   1451   // Unconditionally remove registration regardless of whether or not it is
   1452   // needed since builds after r132190 included it when it wasn't strictly
   1453   // necessary.  Do this removal before adding in the new key to ensure that
   1454   // the COM probe/flush below does its job.
   1455   AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
   1456 
   1457   // Add work items to register the handler iff it is present.
   1458   // See also shell_util.cc's GetProgIdEntries.
   1459   if (installer_state.operation() != InstallerState::UNINSTALL) {
   1460     VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
   1461 
   1462     // Force COM to flush its cache containing the path to the old handler.
   1463     list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
   1464                                          handler_class_uuid));
   1465 
   1466     // The path to the exe (in the version directory).
   1467     base::FilePath delegate_execute(target_path);
   1468     if (new_version.IsValid())
   1469       delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
   1470     delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
   1471 
   1472     // Command-line featuring the quoted path to the exe.
   1473     string16 command(1, L'"');
   1474     command.append(delegate_execute.value()).append(1, L'"');
   1475 
   1476     // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
   1477     list->AddCreateRegKeyWorkItem(root, delegate_execute_path);
   1478     list->AddSetRegValueWorkItem(root, delegate_execute_path, L"",
   1479                                  L"CommandExecuteImpl Class", true);
   1480     string16 subkey(delegate_execute_path);
   1481     subkey.append(L"\\LocalServer32");
   1482     list->AddCreateRegKeyWorkItem(root, subkey);
   1483     list->AddSetRegValueWorkItem(root, subkey, L"", command, true);
   1484     list->AddSetRegValueWorkItem(root, subkey, L"ServerExecutable",
   1485                                  delegate_execute.value(), true);
   1486 
   1487     subkey.assign(delegate_execute_path).append(L"\\Programmable");
   1488     list->AddCreateRegKeyWorkItem(root, subkey);
   1489   }
   1490 }
   1491 
   1492 void AddActiveSetupWorkItems(const InstallerState& installer_state,
   1493                              const base::FilePath& setup_path,
   1494                              const Version& new_version,
   1495                              const Product& product,
   1496                              WorkItemList* list) {
   1497   DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
   1498   BrowserDistribution* distribution = product.distribution();
   1499 
   1500   if (!product.is_chrome() || !installer_state.system_install()) {
   1501     const char* install_level =
   1502         installer_state.system_install() ? "system" : "user";
   1503     VLOG(1) << "No Active Setup processing to do for " << install_level
   1504             << "-level " << distribution->GetAppShortCutName();
   1505     return;
   1506   }
   1507   DCHECK(installer_state.RequiresActiveSetup());
   1508 
   1509   const HKEY root = HKEY_LOCAL_MACHINE;
   1510   const string16 active_setup_path(
   1511       InstallUtil::GetActiveSetupPath(distribution));
   1512 
   1513   VLOG(1) << "Adding registration items for Active Setup.";
   1514   list->AddCreateRegKeyWorkItem(root, active_setup_path);
   1515   list->AddSetRegValueWorkItem(root, active_setup_path, L"",
   1516                                distribution->GetAppShortCutName(), true);
   1517 
   1518   base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
   1519       new_version).Append(kActiveSetupExe));
   1520   CommandLine cmd(active_setup_exe);
   1521   cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
   1522   cmd.AppendSwitch(installer::switches::kVerboseLogging);
   1523   cmd.AppendSwitch(installer::switches::kSystemLevel);
   1524   product.AppendProductFlags(&cmd);
   1525   list->AddSetRegValueWorkItem(root, active_setup_path, L"StubPath",
   1526                                cmd.GetCommandLineString(), true);
   1527 
   1528   // TODO(grt): http://crbug.com/75152 Write a reference to a localized
   1529   // resource.
   1530   list->AddSetRegValueWorkItem(root, active_setup_path, L"Localized Name",
   1531                                distribution->GetAppShortCutName(), true);
   1532 
   1533   list->AddSetRegValueWorkItem(root, active_setup_path, L"IsInstalled",
   1534                                static_cast<DWORD>(1U), true);
   1535 
   1536   list->AddSetRegValueWorkItem(root, active_setup_path, L"Version",
   1537                                kActiveSetupVersion, true);
   1538 }
   1539 
   1540 void AddDeleteOldIELowRightsPolicyWorkItems(
   1541     const InstallerState& installer_state,
   1542     WorkItemList* install_list) {
   1543   DCHECK(install_list);
   1544 
   1545   string16 key_path;
   1546   GetIELowRightsElevationPolicyKeyPath(OLD_ELEVATION_POLICY, &key_path);
   1547   install_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), key_path);
   1548 }
   1549 
   1550 // Adds work items to copy the chrome_launcher IE low rights elevation policy
   1551 // from the primary policy GUID to the "old" policy GUID.  Take care not to
   1552 // perform the copy if there is already an old policy present, as the ones under
   1553 // the main kElevationPolicyGuid would then correspond to an intermediate
   1554 // version (current_version < pv < new_version).
   1555 void AddCopyIELowRightsPolicyWorkItems(const InstallerState& installer_state,
   1556                                        WorkItemList* install_list) {
   1557   DCHECK(install_list);
   1558 
   1559   string16 current_key_path;
   1560   string16 old_key_path;
   1561 
   1562   GetIELowRightsElevationPolicyKeyPath(CURRENT_ELEVATION_POLICY,
   1563                                        &current_key_path);
   1564   GetIELowRightsElevationPolicyKeyPath(OLD_ELEVATION_POLICY, &old_key_path);
   1565   // Do not clobber existing old policies.
   1566   install_list->AddCopyRegKeyWorkItem(installer_state.root_key(),
   1567                                       current_key_path, old_key_path,
   1568                                       WorkItem::IF_NOT_PRESENT);
   1569 }
   1570 
   1571 void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
   1572                                      const Product& product,
   1573                                      CommandLine* uninstall_cmd) {
   1574   DCHECK(uninstall_cmd);
   1575 
   1576   uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
   1577 
   1578   // Append the product-specific uninstall flags.
   1579   product.AppendProductFlags(uninstall_cmd);
   1580   if (installer_state.is_msi()) {
   1581     uninstall_cmd->AppendSwitch(installer::switches::kMsi);
   1582     // See comment in uninstall.cc where we check for the kDeleteProfile switch.
   1583     if (product.is_chrome_frame()) {
   1584       uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile);
   1585     }
   1586   }
   1587   if (installer_state.system_install())
   1588     uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
   1589   if (installer_state.verbose_logging())
   1590     uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
   1591 }
   1592 
   1593 void RefreshElevationPolicy() {
   1594   const wchar_t kIEFrameDll[] = L"ieframe.dll";
   1595   const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
   1596 
   1597   HMODULE ieframe = LoadLibrary(kIEFrameDll);
   1598   if (ieframe) {
   1599     typedef HRESULT (__stdcall *IERefreshPolicy)();
   1600     IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
   1601         GetProcAddress(ieframe, kIERefreshPolicy));
   1602 
   1603     if (ie_refresh_policy) {
   1604       ie_refresh_policy();
   1605     } else {
   1606       VLOG(1) << kIERefreshPolicy << " not supported.";
   1607     }
   1608 
   1609     FreeLibrary(ieframe);
   1610   } else {
   1611     VLOG(1) << "Cannot load " << kIEFrameDll;
   1612   }
   1613 }
   1614 
   1615 void AddOsUpgradeWorkItems(const InstallerState& installer_state,
   1616                            const base::FilePath& setup_path,
   1617                            const Version& new_version,
   1618                            const Product& product,
   1619                            WorkItemList* install_list) {
   1620   const HKEY root_key = installer_state.root_key();
   1621   string16 cmd_key(GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
   1622 
   1623   if (installer_state.operation() == InstallerState::UNINSTALL) {
   1624     install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)->
   1625         set_log_message("Removing OS upgrade command");
   1626   } else {
   1627     // Register with Google Update to have setup.exe --on-os-upgrade called on
   1628     // OS upgrade.
   1629     CommandLine cmd_line(installer_state
   1630         .GetInstallerDirectory(new_version)
   1631         .Append(setup_path.BaseName()));
   1632     // Add the main option to indicate OS upgrade flow.
   1633     cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
   1634     // Add product-specific options.
   1635     product.AppendProductFlags(&cmd_line);
   1636     if (installer_state.system_install())
   1637       cmd_line.AppendSwitch(installer::switches::kSystemLevel);
   1638     // Log everything for now.
   1639     cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
   1640 
   1641     AppCommand cmd(cmd_line.GetCommandLineString());
   1642     cmd.set_is_auto_run_on_os_upgrade(true);
   1643     cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
   1644   }
   1645 }
   1646 
   1647 void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state,
   1648                                      const base::FilePath& setup_path,
   1649                                      const Version& new_version,
   1650                                      const Product& product,
   1651                                      WorkItemList* work_item_list) {
   1652   const HKEY root_key = installer_state.root_key();
   1653   string16 cmd_key(GetRegCommandKey(product.distribution(),
   1654                                     kCmdQueryEULAAcceptance));
   1655   if (installer_state.operation() == InstallerState::UNINSTALL) {
   1656     work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)->
   1657         set_log_message("Removing query EULA acceptance command");
   1658   } else {
   1659     CommandLine cmd_line(installer_state
   1660         .GetInstallerDirectory(new_version)
   1661         .Append(setup_path.BaseName()));
   1662     cmd_line.AppendSwitch(switches::kQueryEULAAcceptance);
   1663     if (installer_state.system_install())
   1664       cmd_line.AppendSwitch(installer::switches::kSystemLevel);
   1665     if (installer_state.verbose_logging())
   1666       cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
   1667     AppCommand cmd(cmd_line.GetCommandLineString());
   1668     cmd.set_is_web_accessible(true);
   1669     cmd.set_is_run_as_user(true);
   1670     cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
   1671   }
   1672 }
   1673 
   1674 void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
   1675                                         const InstallationState& machine_state,
   1676                                         const base::FilePath& setup_path,
   1677                                         const Version& new_version,
   1678                                         WorkItemList* work_item_list) {
   1679   DCHECK(work_item_list);
   1680 
   1681   const bool system_install = installer_state.system_install();
   1682   bool will_have_chrome_frame =
   1683       WillProductBePresentAfterSetup(installer_state, machine_state,
   1684                                      BrowserDistribution::CHROME_FRAME);
   1685   bool will_have_chrome_binaries =
   1686       WillProductBePresentAfterSetup(installer_state, machine_state,
   1687                                      BrowserDistribution::CHROME_BINARIES);
   1688 
   1689   string16 cmd_key(GetRegCommandKey(
   1690                        BrowserDistribution::GetSpecificDistribution(
   1691                            BrowserDistribution::CHROME_BINARIES),
   1692                        kCmdQuickEnableCf));
   1693 
   1694   if (will_have_chrome_frame) {
   1695     // Chrome Frame is (to be) installed. Unconditionally remove the Quick
   1696     // Enable command from the binaries. We do this even if multi-install Chrome
   1697     // isn't installed since we don't want them left behind in any case.
   1698     work_item_list->AddDeleteRegKeyWorkItem(
   1699         installer_state.root_key(), cmd_key)->set_log_message(
   1700             "removing " + WideToASCII(kCmdQuickEnableCf) + " command");
   1701 
   1702   } else if (will_have_chrome_binaries) {
   1703     // Chrome Frame isn't (to be) installed while some other multi-install
   1704     // product is (to be) installed. Add the Quick Enable command to
   1705     // the binaries.
   1706     CommandLine cmd_line(GetGenericQuickEnableCommand(installer_state,
   1707                                                       machine_state,
   1708                                                       setup_path,
   1709                                                       new_version));
   1710     // kMultiInstall and kVerboseLogging were processed above.
   1711     cmd_line.AppendSwitch(switches::kChromeFrameQuickEnable);
   1712     if (installer_state.system_install())
   1713       cmd_line.AppendSwitch(switches::kSystemLevel);
   1714     AppCommand cmd(cmd_line.GetCommandLineString());
   1715     cmd.set_sends_pings(true);
   1716     cmd.set_is_web_accessible(true);
   1717     cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
   1718   }
   1719 }
   1720 
   1721 }  // namespace installer
   1722