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