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