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