install_worker.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 " + base::UTF16ToASCII(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  // In the past, we copied rather than moved for system level installs so that
504  // the permissions of %ProgramFiles% would be picked up.  Now that |temp_path|
505  // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
506  // otherwise), there is no need to do this.
507  // Note that we pass true for check_duplicates to avoid failing on in-use
508  // repair runs if the current_version is the same as the new_version.
509  bool check_for_duplicates = (current_version &&
510                               current_version->Equals(new_version));
511  install_list->AddMoveTreeWorkItem(
512      src_path.AppendASCII(new_version.GetString()).value(),
513      target_path.AppendASCII(new_version.GetString()).value(),
514      temp_path.value(),
515      check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
516                             WorkItem::ALWAYS_MOVE);
517
518  // Delete any old_chrome.exe if present (ignore failure if it's in use).
519  install_list->AddDeleteTreeWorkItem(
520      target_path.Append(installer::kChromeOldExe), temp_path)->
521          set_ignore_failure(true);
522}
523
524// Probes COM machinery to get an instance of delegate_execute.exe's
525// CommandExecuteImpl class.  This is required so that COM purges its cache of
526// the path to the binary, which changes on updates.  This callback
527// unconditionally returns true since an install should not be aborted if the
528// probe fails.
529bool ProbeCommandExecuteCallback(const base::string16& command_execute_id,
530                                 const CallbackWorkItem& work_item) {
531  // Noop on rollback.
532  if (work_item.IsRollback())
533    return true;
534
535  CLSID class_id = {};
536
537  HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
538  if (FAILED(hr)) {
539    LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
540                   "CLSID; hr=0x" << std::hex << hr;
541  } else {
542    base::win::ScopedComPtr<IUnknown> command_execute_impl;
543    hr = command_execute_impl.CreateInstance(class_id, NULL,
544                                             CLSCTX_LOCAL_SERVER);
545    if (hr != REGDB_E_CLASSNOTREG) {
546      LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x"
547                 << std::hex << hr;
548    }
549  }
550
551  return true;
552}
553
554void AddUninstallDelegateExecuteWorkItems(
555    HKEY root,
556    const base::string16& delegate_execute_path,
557    WorkItemList* list) {
558  VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
559          << root;
560  list->AddDeleteRegKeyWorkItem(root, delegate_execute_path);
561
562  // In the past, the ICommandExecuteImpl interface and a TypeLib were both
563  // registered.  Remove these since this operation may be updating a machine
564  // that had the old registrations.
565  list->AddDeleteRegKeyWorkItem(root,
566                                L"Software\\Classes\\Interface\\"
567                                L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}");
568  list->AddDeleteRegKeyWorkItem(root,
569                                L"Software\\Classes\\TypeLib\\"
570#if defined(GOOGLE_CHROME_BUILD)
571                                L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}"
572#else
573                                L"{7779FB70-B399-454A-AA1A-BAA850032B10}"
574#endif
575                                );
576}
577
578// Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
579// (exclusively -- crrev.com/132596), registered a DelegateExecute class by
580// mistake (with the same GUID as Chrome). The fix stopped registering the bad
581// value, but didn't delete it. This is a problem for users who had installed
582// Canary before 20.0.1106.0 and now have a system-level Chrome, as the
583// left-behind Canary registrations in HKCU mask the HKLM registrations for the
584// same GUID. Cleanup those registrations if they still exist and belong to this
585// Canary (i.e., the registered delegate_execute's path is under |target_path|).
586void CleanupBadCanaryDelegateExecuteRegistration(
587    const base::FilePath& target_path,
588    WorkItemList* list) {
589  base::string16 google_chrome_delegate_execute_path(
590      L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
591  base::string16 google_chrome_local_server_32(
592      google_chrome_delegate_execute_path + L"\\LocalServer32");
593
594  RegKey local_server_32_key;
595  base::string16 registered_server;
596  if (local_server_32_key.Open(HKEY_CURRENT_USER,
597                               google_chrome_local_server_32.c_str(),
598                               KEY_QUERY_VALUE) == ERROR_SUCCESS &&
599      local_server_32_key.ReadValue(L"ServerExecutable",
600                                    &registered_server) == ERROR_SUCCESS &&
601      target_path.IsParent(base::FilePath(registered_server))) {
602    scoped_ptr<WorkItemList> no_rollback_list(
603        WorkItem::CreateNoRollbackWorkItemList());
604    AddUninstallDelegateExecuteWorkItems(
605        HKEY_CURRENT_USER, google_chrome_delegate_execute_path,
606        no_rollback_list.get());
607    list->AddWorkItem(no_rollback_list.release());
608    VLOG(1) << "Added deletion items for bad Canary registrations.";
609  }
610}
611
612}  // namespace
613
614// This method adds work items to create (or update) Chrome uninstall entry in
615// either the Control Panel->Add/Remove Programs list or in the Omaha client
616// state key if running under an MSI installer.
617void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
618                                   const base::FilePath& setup_path,
619                                   const Version& new_version,
620                                   const Product& product,
621                                   WorkItemList* install_list) {
622  HKEY reg_root = installer_state.root_key();
623  BrowserDistribution* browser_dist = product.distribution();
624  DCHECK(browser_dist);
625
626  // When we are installed via an MSI, we need to store our uninstall strings
627  // in the Google Update client state key. We do this even for non-MSI
628  // managed installs to avoid breaking the edge case whereby an MSI-managed
629  // install is updated by a non-msi installer (which would confuse the MSI
630  // machinery if these strings were not also updated). The UninstallString
631  // value placed in the client state key is also used by the mini_installer to
632  // locate the setup.exe instance used for binary patching.
633  // Do not quote the command line for the MSI invocation.
634  base::FilePath install_path(installer_state.target_path());
635  base::FilePath installer_path(
636      installer_state.GetInstallerDirectory(new_version));
637  installer_path = installer_path.Append(setup_path.BaseName());
638
639  CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
640  AppendUninstallCommandLineFlags(installer_state, product,
641                                  &uninstall_arguments);
642
643  base::string16 update_state_key(browser_dist->GetStateKey());
644  install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
645  install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
646      installer::kUninstallStringField, installer_path.value(), true);
647  install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
648      installer::kUninstallArgumentsField,
649      uninstall_arguments.GetCommandLineString(), true);
650
651  // MSI installations will manage their own uninstall shortcuts.
652  if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
653    // We need to quote the command line for the Add/Remove Programs dialog.
654    CommandLine quoted_uninstall_cmd(installer_path);
655    DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"');
656    quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
657
658    base::string16 uninstall_reg = browser_dist->GetUninstallRegPath();
659    install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
660    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
661        installer::kUninstallDisplayNameField, browser_dist->GetDisplayName(),
662        true);
663    install_list->AddSetRegValueWorkItem(reg_root,
664        uninstall_reg, installer::kUninstallStringField,
665        quoted_uninstall_cmd.GetCommandLineString(), true);
666    install_list->AddSetRegValueWorkItem(reg_root,
667                                         uninstall_reg,
668                                         L"InstallLocation",
669                                         install_path.value(),
670                                         true);
671
672    BrowserDistribution* dist = product.distribution();
673    base::string16 chrome_icon = ShellUtil::FormatIconLocation(
674        install_path.Append(dist->GetIconFilename()).value(),
675        dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
676    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
677                                         L"DisplayIcon", chrome_icon, true);
678    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
679                                         L"NoModify", static_cast<DWORD>(1),
680                                         true);
681    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
682                                         L"NoRepair", static_cast<DWORD>(1),
683                                         true);
684
685    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
686                                         L"Publisher",
687                                         browser_dist->GetPublisherName(),
688                                         true);
689    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
690                                         L"Version",
691                                         ASCIIToWide(new_version.GetString()),
692                                         true);
693    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
694                                         L"DisplayVersion",
695                                         ASCIIToWide(new_version.GetString()),
696                                         true);
697    install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
698                                         L"InstallDate",
699                                         InstallUtil::GetCurrentDate(),
700                                         false);
701
702    const std::vector<uint16>& version_components = new_version.components();
703    if (version_components.size() == 4) {
704      // Our version should be in major.minor.build.rev.
705      install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
706          L"VersionMajor", static_cast<DWORD>(version_components[2]), true);
707      install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
708          L"VersionMinor", static_cast<DWORD>(version_components[3]), true);
709    }
710  }
711}
712
713// Create Version key for a product (if not already present) and sets the new
714// product version as the last step.
715void AddVersionKeyWorkItems(HKEY root,
716                            BrowserDistribution* dist,
717                            const Version& new_version,
718                            bool add_language_identifier,
719                            WorkItemList* list) {
720  // Create Version key for each distribution (if not already present) and set
721  // the new product version as the last step.
722  base::string16 version_key(dist->GetVersionKey());
723  list->AddCreateRegKeyWorkItem(root, version_key);
724
725  base::string16 product_name(dist->GetDisplayName());
726  list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
727                               product_name, true);  // overwrite name also
728  list->AddSetRegValueWorkItem(root, version_key,
729                               google_update::kRegOopcrashesField,
730                               static_cast<DWORD>(1),
731                               false);  // set during first install
732  if (add_language_identifier) {
733    // Write the language identifier of the current translation.  Omaha's set of
734    // languages is a superset of Chrome's set of translations with this one
735    // exception: what Chrome calls "en-us", Omaha calls "en".  sigh.
736    base::string16 language(GetCurrentTranslation());
737    if (LowerCaseEqualsASCII(language, "en-us"))
738      language.resize(2);
739    list->AddSetRegValueWorkItem(root, version_key,
740                                 google_update::kRegLangField, language,
741                                 false);  // do not overwrite language
742  }
743  list->AddSetRegValueWorkItem(root, version_key,
744                               google_update::kRegVersionField,
745                               ASCIIToWide(new_version.GetString()),
746                               true);  // overwrite version
747}
748
749// Mirror oeminstall the first time anything is installed multi.  There is no
750// need to update the value on future install/update runs since this value never
751// changes.  Note that the value is removed by Google Update after EULA
752// acceptance is processed.
753void AddOemInstallWorkItems(const InstallationState& original_state,
754                            const InstallerState& installer_state,
755                            WorkItemList* install_list) {
756  DCHECK(installer_state.is_multi_install());
757  const bool system_install = installer_state.system_install();
758  if (!original_state.GetProductState(system_install,
759                                      BrowserDistribution::CHROME_BINARIES)) {
760    const HKEY root_key = installer_state.root_key();
761    base::string16 multi_key(
762        installer_state.multi_package_binaries_distribution()->GetStateKey());
763
764    // Copy the value from Chrome unless Chrome isn't installed or being
765    // installed.
766    BrowserDistribution::Type source_type;
767    if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) {
768      source_type = BrowserDistribution::CHROME_BROWSER;
769    } else if (!installer_state.products().empty()) {
770      // Pick a product, any product.
771      source_type = installer_state.products()[0]->distribution()->GetType();
772    } else {
773      // Nothing is being installed?  Entirely unexpected, so do no harm.
774      LOG(ERROR) << "No products found in AddOemInstallWorkItems";
775      return;
776    }
777    const ProductState* source_product =
778        original_state.GetNonVersionedProductState(system_install, source_type);
779
780    base::string16 oem_install;
781    if (source_product->GetOemInstall(&oem_install)) {
782      VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from "
783              << BrowserDistribution::GetSpecificDistribution(source_type)->
784                     GetDisplayName();
785      install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
786      // Always overwrite an old value.
787      install_list->AddSetRegValueWorkItem(root_key, multi_key,
788                                           google_update::kRegOemInstallField,
789                                           oem_install, true);
790    } else {
791      // Clear any old value.
792      install_list->AddDeleteRegValueWorkItem(
793          root_key, multi_key, google_update::kRegOemInstallField);
794    }
795  }
796}
797
798// Mirror eulaaccepted the first time anything is installed multi.  There is no
799// need to update the value on future install/update runs since
800// GoogleUpdateSettings::SetEULAConsent will modify the value for both the
801// relevant product and for the binaries.
802void AddEulaAcceptedWorkItems(const InstallationState& original_state,
803                              const InstallerState& installer_state,
804                              WorkItemList* install_list) {
805  DCHECK(installer_state.is_multi_install());
806  const bool system_install = installer_state.system_install();
807  if (!original_state.GetProductState(system_install,
808                                      BrowserDistribution::CHROME_BINARIES)) {
809    const HKEY root_key = installer_state.root_key();
810    base::string16 multi_key(
811        installer_state.multi_package_binaries_distribution()->GetStateKey());
812
813    // Copy the value from the product with the greatest value.
814    bool have_eula_accepted = false;
815    BrowserDistribution::Type product_type;
816    DWORD eula_accepted;
817    const Products& products = installer_state.products();
818    for (Products::const_iterator it = products.begin(); it < products.end();
819         ++it) {
820      const Product& product = **it;
821      if (product.is_chrome_binaries())
822        continue;
823      DWORD dword_value = 0;
824      BrowserDistribution::Type this_type = product.distribution()->GetType();
825      const ProductState* product_state =
826          original_state.GetNonVersionedProductState(
827              system_install, this_type);
828      if (product_state->GetEulaAccepted(&dword_value) &&
829          (!have_eula_accepted || dword_value > eula_accepted)) {
830        have_eula_accepted = true;
831        eula_accepted = dword_value;
832        product_type = this_type;
833      }
834    }
835
836    if (have_eula_accepted) {
837      VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from "
838              << BrowserDistribution::GetSpecificDistribution(product_type)->
839                     GetDisplayName();
840      install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
841      install_list->AddSetRegValueWorkItem(
842          root_key, multi_key, google_update::kRegEULAAceptedField,
843          eula_accepted, true);
844    } else {
845      // Clear any old value.
846      install_list->AddDeleteRegValueWorkItem(
847          root_key, multi_key, google_update::kRegEULAAceptedField);
848    }
849  }
850}
851
852// Adds work items that make registry adjustments for Google Update.
853void AddGoogleUpdateWorkItems(const InstallationState& original_state,
854                              const InstallerState& installer_state,
855                              WorkItemList* install_list) {
856  // Is a multi-install product being installed or over-installed?
857  if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
858      installer_state.operation() != InstallerState::MULTI_UPDATE) {
859    VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
860    return;
861  }
862
863  const bool system_install = installer_state.system_install();
864  const HKEY root_key = installer_state.root_key();
865  base::string16 multi_key(
866      installer_state.multi_package_binaries_distribution()->GetStateKey());
867
868  // For system-level installs, make sure the ClientStateMedium key for the
869  // binaries exists.
870  if (system_install) {
871    install_list->AddCreateRegKeyWorkItem(
872        root_key,
873        installer_state.multi_package_binaries_distribution()->
874            GetStateMediumKey().c_str());
875  }
876
877  // Creating the ClientState key for binaries, if we're migrating to multi then
878  // copy over Chrome's brand code if it has one.
879  if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
880    const ProductState* chrome_product_state =
881        original_state.GetNonVersionedProductState(
882            system_install, BrowserDistribution::CHROME_BROWSER);
883
884    const base::string16& brand(chrome_product_state->brand());
885    if (!brand.empty()) {
886      install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
887      // Write Chrome's brand code to the multi key. Never overwrite the value
888      // if one is already present (although this shouldn't happen).
889      install_list->AddSetRegValueWorkItem(root_key,
890                                           multi_key,
891                                           google_update::kRegBrandField,
892                                           brand,
893                                           false);
894    }
895  }
896
897  AddOemInstallWorkItems(original_state, installer_state, install_list);
898  AddEulaAcceptedWorkItems(original_state, installer_state, install_list);
899  AddUsageStatsWorkItems(original_state, installer_state, install_list);
900
901  // TODO(grt): check for other keys/values we should put in the package's
902  // ClientState and/or Clients key.
903}
904
905void AddUsageStatsWorkItems(const InstallationState& original_state,
906                            const InstallerState& installer_state,
907                            WorkItemList* install_list) {
908  DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
909         installer_state.operation() == InstallerState::MULTI_UPDATE);
910
911  HKEY root_key = installer_state.root_key();
912  bool value_found = false;
913  DWORD usagestats = 0;
914  const Products& products = installer_state.products();
915
916  // Search for an existing usagestats value for any product.
917  for (Products::const_iterator scan = products.begin(), end = products.end();
918       !value_found && scan != end; ++scan) {
919    if ((*scan)->is_chrome_binaries())
920      continue;
921    BrowserDistribution* dist = (*scan)->distribution();
922    const ProductState* product_state =
923        original_state.GetNonVersionedProductState(
924            installer_state.system_install(), dist->GetType());
925    value_found = product_state->GetUsageStats(&usagestats);
926  }
927
928  // If a value was found, write it in the appropriate location for the
929  // binaries and remove all values from the products.
930  if (value_found) {
931    base::string16 state_key(
932        installer_state.multi_package_binaries_distribution()->GetStateKey());
933    install_list->AddCreateRegKeyWorkItem(root_key, state_key);
934    // Overwrite any existing value so that overinstalls (where Omaha writes a
935    // new value into a product's state key) pick up the correct value.
936    install_list->AddSetRegValueWorkItem(root_key, state_key,
937                                         google_update::kRegUsageStatsField,
938                                         usagestats, true);
939
940    for (Products::const_iterator scan = products.begin(), end = products.end();
941         scan != end; ++scan) {
942      if ((*scan)->is_chrome_binaries())
943        continue;
944      BrowserDistribution* dist = (*scan)->distribution();
945      if (installer_state.system_install()) {
946        install_list->AddDeleteRegValueWorkItem(
947            root_key, dist->GetStateMediumKey(),
948            google_update::kRegUsageStatsField);
949        // Previous versions of Chrome also wrote a value in HKCU even for
950        // system-level installs, so clean that up.
951        install_list->AddDeleteRegValueWorkItem(
952            HKEY_CURRENT_USER, dist->GetStateKey(),
953            google_update::kRegUsageStatsField);
954      }
955      install_list->AddDeleteRegValueWorkItem(
956          root_key, dist->GetStateKey(), google_update::kRegUsageStatsField);
957    }
958  }
959}
960
961bool AppendPostInstallTasks(const InstallerState& installer_state,
962                            const base::FilePath& setup_path,
963                            const Version* current_version,
964                            const Version& new_version,
965                            const base::FilePath& temp_path,
966                            WorkItemList* post_install_task_list) {
967  DCHECK(post_install_task_list);
968
969  HKEY root = installer_state.root_key();
970  const Products& products = installer_state.products();
971  base::FilePath new_chrome_exe(
972      installer_state.target_path().Append(installer::kChromeNewExe));
973
974  // Append work items that will only be executed if this was an update.
975  // We update the 'opv' value with the current version that is active,
976  // the 'cpv' value with the critical update version (if present), and the
977  // 'cmd' value with the rename command to run.
978  {
979    scoped_ptr<WorkItemList> in_use_update_work_items(
980        WorkItem::CreateConditionalWorkItemList(
981            new ConditionRunIfFileExists(new_chrome_exe)));
982    in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
983
984    // |critical_version| will be valid only if this in-use update includes a
985    // version considered critical relative to the version being updated.
986    Version critical_version(installer_state.DetermineCriticalVersion(
987        current_version, new_version));
988    base::FilePath installer_path(
989        installer_state.GetInstallerDirectory(new_version).Append(
990            setup_path.BaseName()));
991
992    CommandLine rename(installer_path);
993    rename.AppendSwitch(switches::kRenameChromeExe);
994    if (installer_state.system_install())
995      rename.AppendSwitch(switches::kSystemLevel);
996
997    if (installer_state.verbose_logging())
998      rename.AppendSwitch(switches::kVerboseLogging);
999
1000    base::string16 version_key;
1001    for (size_t i = 0; i < products.size(); ++i) {
1002      BrowserDistribution* dist = products[i]->distribution();
1003      version_key = dist->GetVersionKey();
1004
1005      if (current_version) {
1006        in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
1007            google_update::kRegOldVersionField,
1008            ASCIIToWide(current_version->GetString()), true);
1009      }
1010      if (critical_version.IsValid()) {
1011        in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
1012            google_update::kRegCriticalVersionField,
1013            ASCIIToWide(critical_version.GetString()), true);
1014      } else {
1015        in_use_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1016            google_update::kRegCriticalVersionField);
1017      }
1018
1019      // Adding this registry entry for all products (but the binaries) is
1020      // overkill. However, as it stands, we don't have a way to know which
1021      // product will check the key and run the command, so we add it for all.
1022      // The first to run it will perform the operation and clean up the other
1023      // values.
1024      if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
1025        CommandLine product_rename_cmd(rename);
1026        products[i]->AppendRenameFlags(&product_rename_cmd);
1027        in_use_update_work_items->AddSetRegValueWorkItem(
1028            root, version_key, google_update::kRegRenameCmdField,
1029            product_rename_cmd.GetCommandLineString(), true);
1030      }
1031    }
1032
1033    post_install_task_list->AddWorkItem(in_use_update_work_items.release());
1034  }
1035
1036  // Append work items that will be executed if this was NOT an in-use update.
1037  {
1038    scoped_ptr<WorkItemList> regular_update_work_items(
1039        WorkItem::CreateConditionalWorkItemList(
1040            new Not(new ConditionRunIfFileExists(new_chrome_exe))));
1041    regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
1042
1043    // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1044    for (size_t i = 0; i < products.size(); ++i) {
1045      BrowserDistribution* dist = products[i]->distribution();
1046      base::string16 version_key(dist->GetVersionKey());
1047      regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1048          google_update::kRegOldVersionField);
1049      regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1050          google_update::kRegCriticalVersionField);
1051      regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1052          google_update::kRegRenameCmdField);
1053    }
1054
1055    post_install_task_list->AddWorkItem(regular_update_work_items.release());
1056  }
1057
1058  AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
1059                                       new_version, post_install_task_list);
1060
1061  // If we're told that we're an MSI install, make sure to set the marker
1062  // in the client state key so that future updates do the right thing.
1063  if (installer_state.is_msi()) {
1064    for (size_t i = 0; i < products.size(); ++i) {
1065      const Product* product = products[i];
1066      AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
1067                              post_install_task_list);
1068
1069      // We want MSI installs to take over the Add/Remove Programs shortcut.
1070      // Make a best-effort attempt to delete any shortcuts left over from
1071      // previous non-MSI installations for the same type of install (system or
1072      // per user).
1073      if (product->ShouldCreateUninstallEntry()) {
1074        AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
1075                                                   temp_path,
1076                                                   post_install_task_list);
1077      }
1078    }
1079  }
1080
1081  return true;
1082}
1083
1084void AddInstallWorkItems(const InstallationState& original_state,
1085                         const InstallerState& installer_state,
1086                         const base::FilePath& setup_path,
1087                         const base::FilePath& archive_path,
1088                         const base::FilePath& src_path,
1089                         const base::FilePath& temp_path,
1090                         const Version* current_version,
1091                         const Version& new_version,
1092                         WorkItemList* install_list) {
1093  DCHECK(install_list);
1094
1095  const base::FilePath& target_path = installer_state.target_path();
1096
1097  // A temp directory that work items need and the actual install directory.
1098  install_list->AddCreateDirWorkItem(temp_path);
1099  install_list->AddCreateDirWorkItem(target_path);
1100
1101  if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
1102      installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
1103    AddChromeWorkItems(original_state,
1104                       installer_state,
1105                       setup_path,
1106                       archive_path,
1107                       src_path,
1108                       temp_path,
1109                       current_version,
1110                       new_version,
1111                       install_list);
1112  }
1113
1114  if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1115    install_list->AddCopyTreeWorkItem(
1116        src_path.Append(installer::kChromeAppHostExe).value(),
1117        target_path.Append(installer::kChromeAppHostExe).value(),
1118        temp_path.value(),
1119        WorkItem::ALWAYS,
1120        L"");
1121  }
1122
1123  // Copy installer in install directory
1124  AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
1125                        new_version, install_list);
1126
1127  const HKEY root = installer_state.root_key();
1128  // Only set "lang" for user-level installs since for system-level, the install
1129  // language may not be related to a given user's runtime language.
1130  const bool add_language_identifier = !installer_state.system_install();
1131
1132  const Products& products = installer_state.products();
1133  for (Products::const_iterator it = products.begin(); it < products.end();
1134       ++it) {
1135    const Product& product = **it;
1136
1137    AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
1138                                  product, install_list);
1139
1140    AddVersionKeyWorkItems(root, product.distribution(), new_version,
1141                           add_language_identifier, install_list);
1142
1143    AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
1144                                product, install_list);
1145
1146    AddActiveSetupWorkItems(installer_state, setup_path, new_version, product,
1147                            install_list);
1148  }
1149
1150  // TODO(huangs): Implement actual migration code and remove the hack below.
1151  // If installing Chrome without the legacy stand-alone App Launcher (to be
1152  // handled later), add "shadow" App Launcher registry keys so Google Update
1153  // would recognize the "dr" value in the App Launcher ClientState key.
1154  // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome.
1155  if (installer_state.is_multi_install() &&
1156      installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) &&
1157      !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1158    BrowserDistribution* shadow_app_launcher_dist =
1159        BrowserDistribution::GetSpecificDistribution(
1160            BrowserDistribution::CHROME_APP_HOST);
1161    AddVersionKeyWorkItems(root, shadow_app_launcher_dist, new_version,
1162                           add_language_identifier, install_list);
1163  }
1164
1165  // Add any remaining work items that involve special settings for
1166  // each product.
1167  AddProductSpecificWorkItems(original_state, installer_state, setup_path,
1168                              new_version, install_list);
1169
1170  // Copy over brand, usagestats, and other values.
1171  AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
1172
1173  // Append the tasks that run after the installation.
1174  AppendPostInstallTasks(installer_state,
1175                         setup_path,
1176                         current_version,
1177                         new_version,
1178                         temp_path,
1179                         install_list);
1180}
1181
1182void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
1183                                const std::vector<base::FilePath>& dll_list,
1184                                bool system_level,
1185                                bool do_register,
1186                                bool ignore_failures,
1187                                WorkItemList* work_item_list) {
1188  DCHECK(work_item_list);
1189  if (dll_list.empty()) {
1190    VLOG(1) << "No COM DLLs to register";
1191  } else {
1192    std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
1193    for (; dll_iter != dll_list.end(); ++dll_iter) {
1194      base::FilePath dll_path = dll_folder.Append(*dll_iter);
1195      WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
1196          dll_path.value(), do_register, !system_level);
1197      DCHECK(work_item);
1198      work_item->set_ignore_failure(ignore_failures);
1199    }
1200  }
1201}
1202
1203void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
1204                             BrowserDistribution* dist,
1205                             bool set,
1206                             WorkItemList* work_item_list) {
1207  DCHECK(work_item_list);
1208  DWORD msi_value = set ? 1 : 0;
1209  WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
1210      installer_state.root_key(), dist->GetStateKey(),
1211      google_update::kRegMSIField, msi_value, true);
1212  DCHECK(set_msi_work_item);
1213  set_msi_work_item->set_ignore_failure(true);
1214  set_msi_work_item->set_log_message("Could not write MSI marker!");
1215}
1216
1217void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
1218                                 const base::FilePath& target_path,
1219                                 const Version& new_version,
1220                                 const Product& product,
1221                                 WorkItemList* list) {
1222  base::string16 handler_class_uuid;
1223  BrowserDistribution* dist = product.distribution();
1224  if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) {
1225    if (InstallUtil::IsChromeSxSProcess()) {
1226      CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
1227    } else {
1228      VLOG(1) << "No DelegateExecute verb handler processing to do for "
1229              << dist->GetDisplayName();
1230    }
1231    return;
1232  }
1233
1234  HKEY root = installer_state.root_key();
1235  base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
1236  delegate_execute_path.append(handler_class_uuid);
1237
1238  // Unconditionally remove registration regardless of whether or not it is
1239  // needed since builds after r132190 included it when it wasn't strictly
1240  // necessary.  Do this removal before adding in the new key to ensure that
1241  // the COM probe/flush below does its job.
1242  AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
1243
1244  // Add work items to register the handler iff it is present.
1245  // See also shell_util.cc's GetProgIdEntries.
1246  if (installer_state.operation() != InstallerState::UNINSTALL) {
1247    VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1248
1249    // Force COM to flush its cache containing the path to the old handler.
1250    list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
1251                                         handler_class_uuid));
1252
1253    // The path to the exe (in the version directory).
1254    base::FilePath delegate_execute(target_path);
1255    if (new_version.IsValid())
1256      delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
1257    delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
1258
1259    // Command-line featuring the quoted path to the exe.
1260    base::string16 command(1, L'"');
1261    command.append(delegate_execute.value()).append(1, L'"');
1262
1263    // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1264    list->AddCreateRegKeyWorkItem(root, delegate_execute_path);
1265    list->AddSetRegValueWorkItem(root, delegate_execute_path, L"",
1266                                 L"CommandExecuteImpl Class", true);
1267    base::string16 subkey(delegate_execute_path);
1268    subkey.append(L"\\LocalServer32");
1269    list->AddCreateRegKeyWorkItem(root, subkey);
1270    list->AddSetRegValueWorkItem(root, subkey, L"", command, true);
1271    list->AddSetRegValueWorkItem(root, subkey, L"ServerExecutable",
1272                                 delegate_execute.value(), true);
1273
1274    subkey.assign(delegate_execute_path).append(L"\\Programmable");
1275    list->AddCreateRegKeyWorkItem(root, subkey);
1276  }
1277}
1278
1279void AddActiveSetupWorkItems(const InstallerState& installer_state,
1280                             const base::FilePath& setup_path,
1281                             const Version& new_version,
1282                             const Product& product,
1283                             WorkItemList* list) {
1284  DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
1285  BrowserDistribution* dist = product.distribution();
1286
1287  if (!product.is_chrome() || !installer_state.system_install()) {
1288    const char* install_level =
1289        installer_state.system_install() ? "system" : "user";
1290    VLOG(1) << "No Active Setup processing to do for " << install_level
1291            << "-level " << dist->GetDisplayName();
1292    return;
1293  }
1294  DCHECK(installer_state.RequiresActiveSetup());
1295
1296  const HKEY root = HKEY_LOCAL_MACHINE;
1297  const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist));
1298
1299  VLOG(1) << "Adding registration items for Active Setup.";
1300  list->AddCreateRegKeyWorkItem(root, active_setup_path);
1301  list->AddSetRegValueWorkItem(root, active_setup_path, L"",
1302                               dist->GetDisplayName(), true);
1303
1304  base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
1305      new_version).Append(kActiveSetupExe));
1306  CommandLine cmd(active_setup_exe);
1307  cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
1308  cmd.AppendSwitch(installer::switches::kVerboseLogging);
1309  cmd.AppendSwitch(installer::switches::kSystemLevel);
1310  product.AppendProductFlags(&cmd);
1311  list->AddSetRegValueWorkItem(root, active_setup_path, L"StubPath",
1312                               cmd.GetCommandLineString(), true);
1313
1314  // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1315  // resource.
1316  list->AddSetRegValueWorkItem(root, active_setup_path, L"Localized Name",
1317                               dist->GetDisplayName(), true);
1318
1319  list->AddSetRegValueWorkItem(root, active_setup_path, L"IsInstalled",
1320                               static_cast<DWORD>(1U), true);
1321
1322  list->AddSetRegValueWorkItem(root, active_setup_path, L"Version",
1323                               kActiveSetupVersion, true);
1324}
1325
1326void AddDeleteOldIELowRightsPolicyWorkItems(
1327    const InstallerState& installer_state,
1328    WorkItemList* install_list) {
1329  DCHECK(install_list);
1330
1331  base::string16 key_path;
1332  GetOldIELowRightsElevationPolicyKeyPath(&key_path);
1333  install_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), key_path);
1334}
1335
1336void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
1337                                     const Product& product,
1338                                     CommandLine* uninstall_cmd) {
1339  DCHECK(uninstall_cmd);
1340
1341  uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
1342
1343  // Append the product-specific uninstall flags.
1344  product.AppendProductFlags(uninstall_cmd);
1345  if (installer_state.is_msi())
1346    uninstall_cmd->AppendSwitch(installer::switches::kMsi);
1347  if (installer_state.system_install())
1348    uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
1349  if (installer_state.verbose_logging())
1350    uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
1351}
1352
1353void RefreshElevationPolicy() {
1354  const wchar_t kIEFrameDll[] = L"ieframe.dll";
1355  const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
1356
1357  HMODULE ieframe = LoadLibrary(kIEFrameDll);
1358  if (ieframe) {
1359    typedef HRESULT (__stdcall *IERefreshPolicy)();
1360    IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
1361        GetProcAddress(ieframe, kIERefreshPolicy));
1362
1363    if (ie_refresh_policy) {
1364      ie_refresh_policy();
1365    } else {
1366      VLOG(1) << kIERefreshPolicy << " not supported.";
1367    }
1368
1369    FreeLibrary(ieframe);
1370  } else {
1371    VLOG(1) << "Cannot load " << kIEFrameDll;
1372  }
1373}
1374
1375void AddOsUpgradeWorkItems(const InstallerState& installer_state,
1376                           const base::FilePath& setup_path,
1377                           const Version& new_version,
1378                           const Product& product,
1379                           WorkItemList* install_list) {
1380  const HKEY root_key = installer_state.root_key();
1381  base::string16 cmd_key(
1382      GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
1383
1384  if (installer_state.operation() == InstallerState::UNINSTALL) {
1385    install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)->
1386        set_log_message("Removing OS upgrade command");
1387  } else {
1388    // Register with Google Update to have setup.exe --on-os-upgrade called on
1389    // OS upgrade.
1390    CommandLine cmd_line(installer_state
1391        .GetInstallerDirectory(new_version)
1392        .Append(setup_path.BaseName()));
1393    // Add the main option to indicate OS upgrade flow.
1394    cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
1395    // Add product-specific options.
1396    product.AppendProductFlags(&cmd_line);
1397    if (installer_state.system_install())
1398      cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1399    // Log everything for now.
1400    cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1401
1402    AppCommand cmd(cmd_line.GetCommandLineString());
1403    cmd.set_is_auto_run_on_os_upgrade(true);
1404    cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
1405  }
1406}
1407
1408void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state,
1409                                     const base::FilePath& setup_path,
1410                                     const Version& new_version,
1411                                     const Product& product,
1412                                     WorkItemList* work_item_list) {
1413  const HKEY root_key = installer_state.root_key();
1414  base::string16 cmd_key(
1415      GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance));
1416  if (installer_state.operation() == InstallerState::UNINSTALL) {
1417    work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)->
1418        set_log_message("Removing query EULA acceptance command");
1419  } else {
1420    CommandLine cmd_line(installer_state
1421        .GetInstallerDirectory(new_version)
1422        .Append(setup_path.BaseName()));
1423    cmd_line.AppendSwitch(switches::kQueryEULAAcceptance);
1424    if (installer_state.system_install())
1425      cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1426    if (installer_state.verbose_logging())
1427      cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1428    AppCommand cmd(cmd_line.GetCommandLineString());
1429    cmd.set_is_web_accessible(true);
1430    cmd.set_is_run_as_user(true);
1431    cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
1432  }
1433}
1434
1435void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
1436                                        WorkItemList* work_item_list) {
1437  DCHECK(work_item_list);
1438
1439  base::string16 cmd_key(
1440      GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1441                           BrowserDistribution::CHROME_BINARIES),
1442                       kCmdQuickEnableCf));
1443
1444  // Unconditionally remove the legacy Quick Enable command from the binaries.
1445  // Do this even if multi-install Chrome isn't installed to ensure that it is
1446  // not left behind in any case.
1447  work_item_list->AddDeleteRegKeyWorkItem(
1448      installer_state.root_key(), cmd_key)->set_log_message(
1449          "removing " + base::UTF16ToASCII(kCmdQuickEnableCf) + " command");
1450
1451}
1452
1453}  // namespace installer
1454