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