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