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 ®istered_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