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