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.
5 // This file contains the definitions of the installer functions that build
6 // the WorkItemList used to install the application.
8 #include "chrome/installer/setup/install_worker.h"
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/app_launcher_installer.h"
32 #include "chrome/installer/setup/install.h"
33 #include "chrome/installer/setup/setup_constants.h"
34 #include "chrome/installer/setup/setup_util.h"
35 #include "chrome/installer/util/app_registration_data.h"
36 #include "chrome/installer/util/browser_distribution.h"
37 #include "chrome/installer/util/callback_work_item.h"
38 #include "chrome/installer/util/conditional_work_item_list.h"
39 #include "chrome/installer/util/create_reg_key_work_item.h"
40 #include "chrome/installer/util/firewall_manager_win.h"
41 #include "chrome/installer/util/google_update_constants.h"
42 #include "chrome/installer/util/helper.h"
43 #include "chrome/installer/util/install_util.h"
44 #include "chrome/installer/util/installation_state.h"
45 #include "chrome/installer/util/installer_state.h"
46 #include "chrome/installer/util/l10n_string_util.h"
47 #include "chrome/installer/util/product.h"
48 #include "chrome/installer/util/set_reg_value_work_item.h"
49 #include "chrome/installer/util/shell_util.h"
50 #include "chrome/installer/util/util_constants.h"
51 #include "chrome/installer/util/work_item_list.h"
53 using base::ASCIIToUTF16
;
54 using base::win::RegKey
;
60 // The version identifying the work done by setup.exe --configure-user-settings
61 // on user login by way of Active Setup. Increase this value if the work done
62 // in setup_main.cc's handling of kConfigureUserSettings changes and should be
63 // executed again for all users.
64 const wchar_t kActiveSetupVersion
[] = L
"24,0,0,0";
66 // Although the UUID of the ChromeFrame class is used for the "current" value,
67 // this is done only as a convenience; there is no need for the GUID of the Low
68 // Rights policies to match the ChromeFrame class's GUID. Hence, it is safe to
69 // use this completely unrelated GUID for the "old" policies.
70 const wchar_t kIELowRightsPolicyOldGuid
[] =
71 L
"{6C288DD7-76FB-4721-B628-56FAC252E199}";
73 const wchar_t kElevationPolicyKeyPath
[] =
74 L
"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\";
76 void GetOldIELowRightsElevationPolicyKeyPath(base::string16
* key_path
) {
77 key_path
->assign(kElevationPolicyKeyPath
,
78 arraysize(kElevationPolicyKeyPath
) - 1);
79 key_path
->append(kIELowRightsPolicyOldGuid
,
80 arraysize(kIELowRightsPolicyOldGuid
)- 1);
83 // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
84 // products managed by a given package.
85 // |old_version| can be NULL to indicate no Chrome is currently installed.
86 void AddRegisterComDllWorkItemsForPackage(const InstallerState
& installer_state
,
87 const Version
* old_version
,
88 const Version
& new_version
,
89 WorkItemList
* work_item_list
) {
90 // First collect the list of DLLs to be registered from each product.
91 std::vector
<base::FilePath
> com_dll_list
;
92 installer_state
.AddComDllList(&com_dll_list
);
94 // Then, if we got some, attempt to unregister the DLLs from the old
95 // version directory and then re-register them in the new one.
96 // Note that if we are migrating the install directory then we will not
97 // successfully unregister the old DLLs.
98 // TODO(robertshield): See whether we need to fix the migration case.
99 // TODO(robertshield): If we ever remove a DLL from a product, this will
100 // not unregister it on update. We should build the unregistration list from
101 // saved state instead of assuming it is the same as the registration list.
102 if (!com_dll_list
.empty()) {
104 base::FilePath
old_dll_path(installer_state
.target_path().AppendASCII(
105 old_version
->GetString()));
107 installer::AddRegisterComDllWorkItems(old_dll_path
,
109 installer_state
.system_install(),
115 base::FilePath
dll_path(installer_state
.target_path().AppendASCII(
116 new_version
.GetString()));
117 installer::AddRegisterComDllWorkItems(dll_path
,
119 installer_state
.system_install(),
121 false, // Must succeed.
126 void AddInstallerCopyTasks(const InstallerState
& installer_state
,
127 const base::FilePath
& setup_path
,
128 const base::FilePath
& archive_path
,
129 const base::FilePath
& temp_path
,
130 const Version
& new_version
,
131 WorkItemList
* install_list
) {
132 DCHECK(install_list
);
133 base::FilePath
installer_dir(
134 installer_state
.GetInstallerDirectory(new_version
));
135 install_list
->AddCreateDirWorkItem(installer_dir
);
137 base::FilePath
exe_dst(installer_dir
.Append(setup_path
.BaseName()));
139 if (exe_dst
!= setup_path
) {
140 install_list
->AddCopyTreeWorkItem(setup_path
.value(), exe_dst
.value(),
141 temp_path
.value(), WorkItem::ALWAYS
);
144 if (installer_state
.RequiresActiveSetup()) {
145 // Make a copy of setup.exe with a different name so that Active Setup
146 // doesn't require an admin on XP thanks to Application Compatibility.
147 base::FilePath
active_setup_exe(installer_dir
.Append(kActiveSetupExe
));
148 install_list
->AddCopyTreeWorkItem(
149 setup_path
.value(), active_setup_exe
.value(), temp_path
.value(),
153 base::FilePath
archive_dst(installer_dir
.Append(archive_path
.BaseName()));
154 if (archive_path
!= archive_dst
) {
155 // In the past, we copied rather than moved for system level installs so
156 // that the permissions of %ProgramFiles% would be picked up. Now that
157 // |temp_path| is in %ProgramFiles% for system level installs (and in
158 // %LOCALAPPDATA% otherwise), there is no need to do this for the archive.
159 // Setup.exe, on the other hand, is created elsewhere so it must always be
161 if (temp_path
.IsParent(archive_path
)) {
162 install_list
->AddMoveTreeWorkItem(archive_path
.value(),
165 WorkItem::ALWAYS_MOVE
);
167 // This may occur when setup is run out of an existing installation
168 // directory. We cannot remove the system-level archive.
169 install_list
->AddCopyTreeWorkItem(archive_path
.value(),
177 base::string16
GetRegCommandKey(BrowserDistribution
* dist
,
178 const wchar_t* name
) {
179 return GetRegistrationDataCommandKey(dist
->GetAppRegistrationData(), name
);
182 // Adds work items to create (or delete if uninstalling) app commands to launch
183 // the app with a switch. The following criteria should be true:
184 // 1. The switch takes one parameter.
185 // 2. The command send pings.
186 // 3. The command is web accessible.
187 // 4. The command is run as the user.
188 void AddCommandWithParameterWorkItems(const InstallerState
& installer_state
,
189 const InstallationState
& machine_state
,
190 const Version
& new_version
,
191 const Product
& product
,
192 const wchar_t* command_key
,
194 const char* command_with_parameter
,
195 WorkItemList
* work_item_list
) {
198 DCHECK(command_with_parameter
);
199 DCHECK(work_item_list
);
201 base::string16
full_cmd_key(
202 GetRegCommandKey(product
.distribution(), command_key
));
204 if (installer_state
.operation() == InstallerState::UNINSTALL
) {
205 work_item_list
->AddDeleteRegKeyWorkItem(installer_state
.root_key(),
208 ->set_log_message("removing " + base::UTF16ToASCII(command_key
) +
211 base::CommandLine
cmd_line(installer_state
.target_path().Append(app
));
212 cmd_line
.AppendSwitchASCII(command_with_parameter
, "%1");
214 AppCommand
cmd(cmd_line
.GetCommandLineString());
215 cmd
.set_sends_pings(true);
216 cmd
.set_is_web_accessible(true);
217 cmd
.set_is_run_as_user(true);
218 cmd
.AddWorkItems(installer_state
.root_key(), full_cmd_key
, work_item_list
);
222 // A callback invoked by |work_item| that adds firewall rules for Chrome. Rules
223 // are left in-place on rollback unless |remove_on_rollback| is true. This is
224 // the case for new installs only. Updates and overinstalls leave the rule
225 // in-place on rollback since a previous install of Chrome will be used in that
227 bool AddFirewallRulesCallback(bool system_level
,
228 BrowserDistribution
* dist
,
229 const base::FilePath
& chrome_path
,
230 bool remove_on_rollback
,
231 const CallbackWorkItem
& work_item
) {
232 // There is no work to do on rollback if this is not a new install.
233 if (work_item
.IsRollback() && !remove_on_rollback
)
236 scoped_ptr
<FirewallManager
> manager
=
237 FirewallManager::Create(dist
, chrome_path
);
239 LOG(ERROR
) << "Failed creating a FirewallManager. Continuing with install.";
243 if (work_item
.IsRollback()) {
244 manager
->RemoveFirewallRules();
248 // Adding the firewall rule is expected to fail for user-level installs on
249 // Vista+. Try anyway in case the installer is running elevated.
250 if (!manager
->AddFirewallRules())
251 LOG(ERROR
) << "Failed creating a firewall rules. Continuing with install.";
253 // Don't abort installation if the firewall rule couldn't be added.
257 // Adds work items to |list| to create firewall rules.
258 void AddFirewallRulesWorkItems(const InstallerState
& installer_state
,
259 BrowserDistribution
* dist
,
261 WorkItemList
* list
) {
262 list
->AddCallbackWorkItem(
263 base::Bind(&AddFirewallRulesCallback
,
264 installer_state
.system_install(),
266 installer_state
.target_path().Append(kChromeExe
),
270 void AddProductSpecificWorkItems(const InstallationState
& original_state
,
271 const InstallerState
& installer_state
,
272 const base::FilePath
& setup_path
,
273 const Version
& new_version
,
275 bool add_language_identifier
,
276 WorkItemList
* list
) {
277 const Products
& products
= installer_state
.products();
278 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
280 const Product
& p
= **it
;
282 AddOsUpgradeWorkItems(installer_state
, setup_path
, new_version
, p
,
284 AddFirewallRulesWorkItems(
285 installer_state
, p
.distribution(), is_new_install
, list
);
287 #if defined(GOOGLE_CHROME_BUILD)
288 if (!InstallUtil::IsChromeSxSProcess()) {
289 // Add items to set up the App Launcher's version key if Google Chrome
290 // is being installed or updated.
291 AddAppLauncherVersionKeyWorkItems(installer_state
.root_key(),
292 new_version
, add_language_identifier
, list
);
294 #endif // GOOGLE_CHROME_BUILD
296 if (p
.is_chrome_binaries())
297 AddQuickEnableChromeFrameWorkItems(installer_state
, list
);
301 // This is called when an MSI installation is run. It may be that a user is
302 // attempting to install the MSI on top of a non-MSI managed installation.
303 // If so, try and remove any existing uninstallation shortcuts, as we want the
304 // uninstall to be managed entirely by the MSI machinery (accessible via the
305 // Add/Remove programs dialog).
306 void AddDeleteUninstallShortcutsForMSIWorkItems(
307 const InstallerState
& installer_state
,
308 const Product
& product
,
309 const base::FilePath
& temp_path
,
310 WorkItemList
* work_item_list
) {
311 DCHECK(installer_state
.is_msi())
312 << "This must only be called for MSI installations!";
314 // First attempt to delete the old installation's ARP dialog entry.
315 HKEY reg_root
= installer_state
.root_key();
316 base::string16
uninstall_reg(product
.distribution()->GetUninstallRegPath());
318 WorkItem
* delete_reg_key
= work_item_list
->AddDeleteRegKeyWorkItem(
319 reg_root
, uninstall_reg
, KEY_WOW64_32KEY
);
320 delete_reg_key
->set_ignore_failure(true);
322 // Then attempt to delete the old installation's start menu shortcut.
323 base::FilePath uninstall_link
;
324 if (installer_state
.system_install()) {
325 PathService::Get(base::DIR_COMMON_START_MENU
, &uninstall_link
);
327 PathService::Get(base::DIR_START_MENU
, &uninstall_link
);
330 if (uninstall_link
.empty()) {
331 LOG(ERROR
) << "Failed to get location for shortcut.";
333 uninstall_link
= uninstall_link
.Append(
334 product
.distribution()->GetStartMenuShortcutSubfolder(
335 BrowserDistribution::SUBFOLDER_CHROME
));
336 uninstall_link
= uninstall_link
.Append(
337 product
.distribution()->GetUninstallLinkName() + installer::kLnkExt
);
338 VLOG(1) << "Deleting old uninstall shortcut (if present): "
339 << uninstall_link
.value();
340 WorkItem
* delete_link
= work_item_list
->AddDeleteTreeWorkItem(
341 uninstall_link
, temp_path
);
342 delete_link
->set_ignore_failure(true);
343 delete_link
->set_log_message(
344 "Failed to delete old uninstall shortcut.");
348 // Adds Chrome specific install work items to |install_list|.
349 // |current_version| can be NULL to indicate no Chrome is currently installed.
350 void AddChromeWorkItems(const InstallationState
& original_state
,
351 const InstallerState
& installer_state
,
352 const base::FilePath
& setup_path
,
353 const base::FilePath
& archive_path
,
354 const base::FilePath
& src_path
,
355 const base::FilePath
& temp_path
,
356 const Version
* current_version
,
357 const Version
& new_version
,
358 WorkItemList
* install_list
) {
359 const base::FilePath
& target_path
= installer_state
.target_path();
361 if (current_version
) {
362 // Delete the archive from an existing install to save some disk space. We
363 // make this an unconditional work item since there's no need to roll this
364 // back; if installation fails we'll be moved to the "-full" channel anyway.
365 base::FilePath
old_installer_dir(
366 installer_state
.GetInstallerDirectory(*current_version
));
367 base::FilePath
old_archive(
368 old_installer_dir
.Append(installer::kChromeArchive
));
369 // Don't delete the archive that we are actually installing from.
370 if (archive_path
!= old_archive
) {
371 install_list
->AddDeleteTreeWorkItem(old_archive
, temp_path
)->
372 set_ignore_failure(true);
376 // Delete any new_chrome.exe if present (we will end up creating a new one
377 // if required) and then copy chrome.exe
378 base::FilePath
new_chrome_exe(target_path
.Append(installer::kChromeNewExe
));
380 install_list
->AddDeleteTreeWorkItem(new_chrome_exe
, temp_path
);
382 // TODO(grt): Remove this check in M35.
383 if (installer_state
.IsChromeFrameRunning(original_state
)) {
384 VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe";
385 install_list
->AddCopyTreeWorkItem(
386 src_path
.Append(installer::kChromeExe
).value(),
387 new_chrome_exe
.value(),
391 install_list
->AddCopyTreeWorkItem(
392 src_path
.Append(installer::kChromeExe
).value(),
393 target_path
.Append(installer::kChromeExe
).value(),
395 WorkItem::NEW_NAME_IF_IN_USE
,
396 new_chrome_exe
.value());
399 // Extra executable for 64 bit systems.
400 // NOTE: We check for "not disabled" so that if the API call fails, we play it
401 // safe and copy the executable anyway.
402 // NOTE: the file wow_helper.exe is only needed for Vista and below.
403 if (base::win::OSInfo::GetInstance()->wow64_status() !=
404 base::win::OSInfo::WOW64_DISABLED
&&
405 base::win::GetVersion() <= base::win::VERSION_VISTA
) {
406 install_list
->AddMoveTreeWorkItem(
407 src_path
.Append(installer::kWowHelperExe
).value(),
408 target_path
.Append(installer::kWowHelperExe
).value(),
410 WorkItem::ALWAYS_MOVE
);
413 // Install kVisualElementsManifest if it is present in |src_path|. No need to
414 // make this a conditional work item as if the file is not there now, it will
416 if (base::PathExists(
417 src_path
.Append(installer::kVisualElementsManifest
))) {
418 install_list
->AddMoveTreeWorkItem(
419 src_path
.Append(installer::kVisualElementsManifest
).value(),
420 target_path
.Append(installer::kVisualElementsManifest
).value(),
422 WorkItem::ALWAYS_MOVE
);
424 // We do not want to have an old VisualElementsManifest pointing to an old
425 // version directory. Delete it as there wasn't a new one to replace it.
426 install_list
->AddDeleteTreeWorkItem(
427 target_path
.Append(installer::kVisualElementsManifest
),
431 // In the past, we copied rather than moved for system level installs so that
432 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path|
433 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
434 // otherwise), there is no need to do this.
435 // Note that we pass true for check_duplicates to avoid failing on in-use
436 // repair runs if the current_version is the same as the new_version.
437 bool check_for_duplicates
= (current_version
&&
438 current_version
->Equals(new_version
));
439 install_list
->AddMoveTreeWorkItem(
440 src_path
.AppendASCII(new_version
.GetString()).value(),
441 target_path
.AppendASCII(new_version
.GetString()).value(),
443 check_for_duplicates
? WorkItem::CHECK_DUPLICATES
:
444 WorkItem::ALWAYS_MOVE
);
446 // Delete any old_chrome.exe if present (ignore failure if it's in use).
447 install_list
->AddDeleteTreeWorkItem(
448 target_path
.Append(installer::kChromeOldExe
), temp_path
)->
449 set_ignore_failure(true);
452 // Probes COM machinery to get an instance of delegate_execute.exe's
453 // CommandExecuteImpl class. This is required so that COM purges its cache of
454 // the path to the binary, which changes on updates. This callback
455 // unconditionally returns true since an install should not be aborted if the
457 bool ProbeCommandExecuteCallback(const base::string16
& command_execute_id
,
458 const CallbackWorkItem
& work_item
) {
460 if (work_item
.IsRollback())
465 HRESULT hr
= CLSIDFromString(command_execute_id
.c_str(), &class_id
);
467 LOG(DFATAL
) << "Failed converting \"" << command_execute_id
<< "\" to "
468 "CLSID; hr=0x" << std::hex
<< hr
;
470 base::win::ScopedComPtr
<IUnknown
> command_execute_impl
;
471 hr
= command_execute_impl
.CreateInstance(class_id
, NULL
,
472 CLSCTX_LOCAL_SERVER
);
473 if (hr
!= REGDB_E_CLASSNOTREG
) {
474 LOG(ERROR
) << "Unexpected result creating CommandExecuteImpl; hr=0x"
482 void AddUninstallDelegateExecuteWorkItems(
484 const base::string16
& delegate_execute_path
,
485 WorkItemList
* list
) {
486 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
488 // Delete both 64 and 32 keys to handle 32->64 or 64->32 migration.
489 list
->AddDeleteRegKeyWorkItem(root
, delegate_execute_path
, KEY_WOW64_32KEY
);
491 list
->AddDeleteRegKeyWorkItem(root
, delegate_execute_path
, KEY_WOW64_64KEY
);
493 // In the past, the ICommandExecuteImpl interface and a TypeLib were both
494 // registered. Remove these since this operation may be updating a machine
495 // that had the old registrations.
496 list
->AddDeleteRegKeyWorkItem(root
,
497 L
"Software\\Classes\\Interface\\"
498 L
"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}",
500 list
->AddDeleteRegKeyWorkItem(root
,
501 L
"Software\\Classes\\TypeLib\\"
502 #if defined(GOOGLE_CHROME_BUILD)
503 L
"{4E805ED8-EBA0-4601-9681-12815A56EBFD}",
505 L
"{7779FB70-B399-454A-AA1A-BAA850032B10}",
510 // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
511 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by
512 // mistake (with the same GUID as Chrome). The fix stopped registering the bad
513 // value, but didn't delete it. This is a problem for users who had installed
514 // Canary before 20.0.1106.0 and now have a system-level Chrome, as the
515 // left-behind Canary registrations in HKCU mask the HKLM registrations for the
516 // same GUID. Cleanup those registrations if they still exist and belong to this
517 // Canary (i.e., the registered delegate_execute's path is under |target_path|).
518 void CleanupBadCanaryDelegateExecuteRegistration(
519 const base::FilePath
& target_path
,
520 WorkItemList
* list
) {
521 base::string16
google_chrome_delegate_execute_path(
522 L
"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
523 base::string16
google_chrome_local_server_32(
524 google_chrome_delegate_execute_path
+ L
"\\LocalServer32");
526 RegKey local_server_32_key
;
527 base::string16 registered_server
;
528 if (local_server_32_key
.Open(HKEY_CURRENT_USER
,
529 google_chrome_local_server_32
.c_str(),
530 KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
531 local_server_32_key
.ReadValue(L
"ServerExecutable",
532 ®istered_server
) == ERROR_SUCCESS
&&
533 target_path
.IsParent(base::FilePath(registered_server
))) {
534 scoped_ptr
<WorkItemList
> no_rollback_list(
535 WorkItem::CreateNoRollbackWorkItemList());
536 AddUninstallDelegateExecuteWorkItems(
537 HKEY_CURRENT_USER
, google_chrome_delegate_execute_path
,
538 no_rollback_list
.get());
539 list
->AddWorkItem(no_rollback_list
.release());
540 VLOG(1) << "Added deletion items for bad Canary registrations.";
546 // This method adds work items to create (or update) Chrome uninstall entry in
547 // either the Control Panel->Add/Remove Programs list or in the Omaha client
548 // state key if running under an MSI installer.
549 void AddUninstallShortcutWorkItems(const InstallerState
& installer_state
,
550 const base::FilePath
& setup_path
,
551 const Version
& new_version
,
552 const Product
& product
,
553 WorkItemList
* install_list
) {
554 HKEY reg_root
= installer_state
.root_key();
555 BrowserDistribution
* browser_dist
= product
.distribution();
556 DCHECK(browser_dist
);
558 // When we are installed via an MSI, we need to store our uninstall strings
559 // in the Google Update client state key. We do this even for non-MSI
560 // managed installs to avoid breaking the edge case whereby an MSI-managed
561 // install is updated by a non-msi installer (which would confuse the MSI
562 // machinery if these strings were not also updated). The UninstallString
563 // value placed in the client state key is also used by the mini_installer to
564 // locate the setup.exe instance used for binary patching.
565 // Do not quote the command line for the MSI invocation.
566 base::FilePath
install_path(installer_state
.target_path());
567 base::FilePath
installer_path(
568 installer_state
.GetInstallerDirectory(new_version
));
569 installer_path
= installer_path
.Append(setup_path
.BaseName());
571 base::CommandLine
uninstall_arguments(base::CommandLine::NO_PROGRAM
);
572 AppendUninstallCommandLineFlags(installer_state
, product
,
573 &uninstall_arguments
);
575 base::string16
update_state_key(browser_dist
->GetStateKey());
576 install_list
->AddCreateRegKeyWorkItem(
577 reg_root
, update_state_key
, KEY_WOW64_32KEY
);
578 install_list
->AddSetRegValueWorkItem(reg_root
,
581 installer::kUninstallStringField
,
582 installer_path
.value(),
584 install_list
->AddSetRegValueWorkItem(
588 installer::kUninstallArgumentsField
,
589 uninstall_arguments
.GetCommandLineString(),
592 // MSI installations will manage their own uninstall shortcuts.
593 if (!installer_state
.is_msi() && product
.ShouldCreateUninstallEntry()) {
594 // We need to quote the command line for the Add/Remove Programs dialog.
595 base::CommandLine
quoted_uninstall_cmd(installer_path
);
596 DCHECK_EQ(quoted_uninstall_cmd
.GetCommandLineString()[0], '"');
597 quoted_uninstall_cmd
.AppendArguments(uninstall_arguments
, false);
599 base::string16 uninstall_reg
= browser_dist
->GetUninstallRegPath();
600 install_list
->AddCreateRegKeyWorkItem(
601 reg_root
, uninstall_reg
, KEY_WOW64_32KEY
);
602 install_list
->AddSetRegValueWorkItem(reg_root
,
605 installer::kUninstallDisplayNameField
,
606 browser_dist
->GetDisplayName(),
608 install_list
->AddSetRegValueWorkItem(
612 installer::kUninstallStringField
,
613 quoted_uninstall_cmd
.GetCommandLineString(),
615 install_list
->AddSetRegValueWorkItem(reg_root
,
619 install_path
.value(),
622 BrowserDistribution
* dist
= product
.distribution();
623 base::string16 chrome_icon
= ShellUtil::FormatIconLocation(
624 install_path
.Append(dist
->GetIconFilename()),
625 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
));
626 install_list
->AddSetRegValueWorkItem(reg_root
,
632 install_list
->AddSetRegValueWorkItem(reg_root
,
636 static_cast<DWORD
>(1),
638 install_list
->AddSetRegValueWorkItem(reg_root
,
642 static_cast<DWORD
>(1),
645 install_list
->AddSetRegValueWorkItem(reg_root
,
649 browser_dist
->GetPublisherName(),
651 install_list
->AddSetRegValueWorkItem(reg_root
,
655 ASCIIToUTF16(new_version
.GetString()),
657 install_list
->AddSetRegValueWorkItem(reg_root
,
661 ASCIIToUTF16(new_version
.GetString()),
663 // TODO(wfh): Ensure that this value is preserved in the 64-bit hive when
664 // 64-bit installs place the uninstall information into the 64-bit registry.
665 install_list
->AddSetRegValueWorkItem(reg_root
,
669 InstallUtil::GetCurrentDate(),
672 const std::vector
<uint16
>& version_components
= new_version
.components();
673 if (version_components
.size() == 4) {
674 // Our version should be in major.minor.build.rev.
675 install_list
->AddSetRegValueWorkItem(
680 static_cast<DWORD
>(version_components
[2]),
682 install_list
->AddSetRegValueWorkItem(
687 static_cast<DWORD
>(version_components
[3]),
693 // Create Version key for a product (if not already present) and sets the new
694 // product version as the last step.
695 void AddVersionKeyWorkItems(HKEY root
,
696 const base::string16
& version_key
,
697 const base::string16
& product_name
,
698 const Version
& new_version
,
699 bool add_language_identifier
,
700 WorkItemList
* list
) {
701 list
->AddCreateRegKeyWorkItem(root
, version_key
, KEY_WOW64_32KEY
);
703 list
->AddSetRegValueWorkItem(root
,
706 google_update::kRegNameField
,
708 true); // overwrite name also
709 list
->AddSetRegValueWorkItem(root
,
712 google_update::kRegOopcrashesField
,
713 static_cast<DWORD
>(1),
714 false); // set during first install
715 if (add_language_identifier
) {
716 // Write the language identifier of the current translation. Omaha's set of
717 // languages is a superset of Chrome's set of translations with this one
718 // exception: what Chrome calls "en-us", Omaha calls "en". sigh.
719 base::string16
language(GetCurrentTranslation());
720 if (LowerCaseEqualsASCII(language
, "en-us"))
722 list
->AddSetRegValueWorkItem(root
,
725 google_update::kRegLangField
,
727 false); // do not overwrite language
729 list
->AddSetRegValueWorkItem(root
,
732 google_update::kRegVersionField
,
733 ASCIIToUTF16(new_version
.GetString()),
734 true); // overwrite version
737 // Mirror oeminstall the first time anything is installed multi. There is no
738 // need to update the value on future install/update runs since this value never
739 // changes. Note that the value is removed by Google Update after EULA
740 // acceptance is processed.
741 void AddOemInstallWorkItems(const InstallationState
& original_state
,
742 const InstallerState
& installer_state
,
743 WorkItemList
* install_list
) {
744 DCHECK(installer_state
.is_multi_install());
745 const bool system_install
= installer_state
.system_install();
746 if (!original_state
.GetProductState(system_install
,
747 BrowserDistribution::CHROME_BINARIES
)) {
748 const HKEY root_key
= installer_state
.root_key();
749 base::string16
multi_key(
750 installer_state
.multi_package_binaries_distribution()->GetStateKey());
752 // Copy the value from Chrome unless Chrome isn't installed or being
754 BrowserDistribution::Type source_type
;
755 if (installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
)) {
756 source_type
= BrowserDistribution::CHROME_BROWSER
;
757 } else if (!installer_state
.products().empty()) {
758 // Pick a product, any product.
759 source_type
= installer_state
.products()[0]->distribution()->GetType();
761 // Nothing is being installed? Entirely unexpected, so do no harm.
762 LOG(ERROR
) << "No products found in AddOemInstallWorkItems";
765 const ProductState
* source_product
=
766 original_state
.GetNonVersionedProductState(system_install
, source_type
);
768 base::string16 oem_install
;
769 if (source_product
->GetOemInstall(&oem_install
)) {
770 VLOG(1) << "Mirroring oeminstall=\"" << oem_install
<< "\" from "
771 << BrowserDistribution::GetSpecificDistribution(source_type
)->
773 install_list
->AddCreateRegKeyWorkItem(
774 root_key
, multi_key
, KEY_WOW64_32KEY
);
775 // Always overwrite an old value.
776 install_list
->AddSetRegValueWorkItem(root_key
,
779 google_update::kRegOemInstallField
,
783 // Clear any old value.
784 install_list
->AddDeleteRegValueWorkItem(
788 google_update::kRegOemInstallField
);
793 // Mirror eulaaccepted the first time anything is installed multi. There is no
794 // need to update the value on future install/update runs since
795 // GoogleUpdateSettings::SetEULAConsent will modify the value for both the
796 // relevant product and for the binaries.
797 void AddEulaAcceptedWorkItems(const InstallationState
& original_state
,
798 const InstallerState
& installer_state
,
799 WorkItemList
* install_list
) {
800 DCHECK(installer_state
.is_multi_install());
801 const bool system_install
= installer_state
.system_install();
802 if (!original_state
.GetProductState(system_install
,
803 BrowserDistribution::CHROME_BINARIES
)) {
804 const HKEY root_key
= installer_state
.root_key();
805 base::string16
multi_key(
806 installer_state
.multi_package_binaries_distribution()->GetStateKey());
808 // Copy the value from the product with the greatest value.
809 bool have_eula_accepted
= false;
810 BrowserDistribution::Type product_type
= BrowserDistribution::NUM_TYPES
;
811 DWORD eula_accepted
= 0;
812 const Products
& products
= installer_state
.products();
813 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
815 const Product
& product
= **it
;
816 if (product
.is_chrome_binaries())
818 DWORD dword_value
= 0;
819 BrowserDistribution::Type this_type
= product
.distribution()->GetType();
820 const ProductState
* product_state
=
821 original_state
.GetNonVersionedProductState(
822 system_install
, this_type
);
823 if (product_state
->GetEulaAccepted(&dword_value
) &&
824 (!have_eula_accepted
|| dword_value
> eula_accepted
)) {
825 have_eula_accepted
= true;
826 eula_accepted
= dword_value
;
827 product_type
= this_type
;
831 if (have_eula_accepted
) {
832 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted
<< " from "
833 << BrowserDistribution::GetSpecificDistribution(product_type
)->
835 install_list
->AddCreateRegKeyWorkItem(
836 root_key
, multi_key
, KEY_WOW64_32KEY
);
837 install_list
->AddSetRegValueWorkItem(root_key
,
840 google_update::kRegEULAAceptedField
,
844 // Clear any old value.
845 install_list
->AddDeleteRegValueWorkItem(
849 google_update::kRegEULAAceptedField
);
854 // Adds work items that make registry adjustments for Google Update.
855 void AddGoogleUpdateWorkItems(const InstallationState
& original_state
,
856 const InstallerState
& installer_state
,
857 WorkItemList
* install_list
) {
858 // Is a multi-install product being installed or over-installed?
859 if (installer_state
.operation() != InstallerState::MULTI_INSTALL
&&
860 installer_state
.operation() != InstallerState::MULTI_UPDATE
) {
861 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state
.operation();
865 const bool system_install
= installer_state
.system_install();
866 const HKEY root_key
= installer_state
.root_key();
867 base::string16
multi_key(
868 installer_state
.multi_package_binaries_distribution()->GetStateKey());
870 // For system-level installs, make sure the ClientStateMedium key for the
872 if (system_install
) {
873 install_list
->AddCreateRegKeyWorkItem(
875 installer_state
.multi_package_binaries_distribution()
876 ->GetStateMediumKey()
881 // Creating the ClientState key for binaries, if we're migrating to multi then
882 // copy over Chrome's brand code if it has one.
883 if (installer_state
.state_type() != BrowserDistribution::CHROME_BINARIES
) {
884 const ProductState
* chrome_product_state
=
885 original_state
.GetNonVersionedProductState(
886 system_install
, BrowserDistribution::CHROME_BROWSER
);
888 const base::string16
& brand(chrome_product_state
->brand());
889 if (!brand
.empty()) {
890 install_list
->AddCreateRegKeyWorkItem(
891 root_key
, multi_key
, KEY_WOW64_32KEY
);
892 // Write Chrome's brand code to the multi key. Never overwrite the value
893 // if one is already present (although this shouldn't happen).
894 install_list
->AddSetRegValueWorkItem(root_key
,
897 google_update::kRegBrandField
,
903 AddOemInstallWorkItems(original_state
, installer_state
, install_list
);
904 AddEulaAcceptedWorkItems(original_state
, installer_state
, install_list
);
905 AddUsageStatsWorkItems(original_state
, installer_state
, install_list
);
907 // TODO(grt): check for other keys/values we should put in the package's
908 // ClientState and/or Clients key.
911 void AddUsageStatsWorkItems(const InstallationState
& original_state
,
912 const InstallerState
& installer_state
,
913 WorkItemList
* install_list
) {
914 DCHECK(installer_state
.operation() == InstallerState::MULTI_INSTALL
||
915 installer_state
.operation() == InstallerState::MULTI_UPDATE
);
917 HKEY root_key
= installer_state
.root_key();
918 bool value_found
= false;
919 DWORD usagestats
= 0;
920 const Products
& products
= installer_state
.products();
922 // Search for an existing usagestats value for any product.
923 for (Products::const_iterator scan
= products
.begin(), end
= products
.end();
924 !value_found
&& scan
!= end
; ++scan
) {
925 if ((*scan
)->is_chrome_binaries())
927 BrowserDistribution
* dist
= (*scan
)->distribution();
928 const ProductState
* product_state
=
929 original_state
.GetNonVersionedProductState(
930 installer_state
.system_install(), dist
->GetType());
931 value_found
= product_state
->GetUsageStats(&usagestats
);
934 // If a value was found, write it in the appropriate location for the
935 // binaries and remove all values from the products.
937 base::string16
state_key(
938 installer_state
.multi_package_binaries_distribution()->GetStateKey());
939 install_list
->AddCreateRegKeyWorkItem(root_key
, state_key
, KEY_WOW64_32KEY
);
940 // Overwrite any existing value so that overinstalls (where Omaha writes a
941 // new value into a product's state key) pick up the correct value.
942 install_list
->AddSetRegValueWorkItem(root_key
,
945 google_update::kRegUsageStatsField
,
949 for (Products::const_iterator scan
= products
.begin(), end
= products
.end();
950 scan
!= end
; ++scan
) {
951 if ((*scan
)->is_chrome_binaries())
953 BrowserDistribution
* dist
= (*scan
)->distribution();
954 if (installer_state
.system_install()) {
955 install_list
->AddDeleteRegValueWorkItem(
957 dist
->GetStateMediumKey(),
959 google_update::kRegUsageStatsField
);
960 // Previous versions of Chrome also wrote a value in HKCU even for
961 // system-level installs, so clean that up.
962 install_list
->AddDeleteRegValueWorkItem(
966 google_update::kRegUsageStatsField
);
968 install_list
->AddDeleteRegValueWorkItem(
972 google_update::kRegUsageStatsField
);
977 // Migrates the usagestats value from the binaries to Chrome when migrating
978 // multi-install Chrome to single-install.
979 void AddMigrateUsageStatesWorkItems(const InstallationState
& original_state
,
980 const InstallerState
& installer_state
,
981 WorkItemList
* install_list
) {
982 // Ensure that a non-multi install or update is being processed (i.e.,
983 // no "--multi-install" on the command line).
984 if (installer_state
.is_multi_install())
987 // Ensure that Chrome is the product being installed or updated (there are no
988 // other products, so it is especially unexpected for this to fail).
989 const Product
* chrome_product
=
990 installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
);
991 if (!chrome_product
) {
992 LOG(DFATAL
) << "Not operating on Chrome while migrating to single-install.";
996 const ProductState
* chrome_state
= original_state
.GetProductState(
997 installer_state
.system_install(),
998 BrowserDistribution::CHROME_BROWSER
);
999 // Bail out if there is not an existing multi-install Chrome that is being
1001 if (!chrome_state
|| !chrome_state
->is_multi_install()) {
1002 VLOG(1) << "No multi-install Chrome found to migrate to single-install.";
1006 const ProductState
* binaries_state
= original_state
.GetProductState(
1007 installer_state
.system_install(),
1008 BrowserDistribution::CHROME_BINARIES
);
1010 // There is nothing to be done if the binaries do not have stats.
1011 DWORD usagestats
= 0;
1012 if (!binaries_state
|| !binaries_state
->GetUsageStats(&usagestats
)) {
1013 VLOG(1) << "No usagestats value found to migrate to single-install.";
1017 VLOG(1) << "Migrating usagestats value from multi-install to single-install.";
1019 // Write the value that was read to Chrome's ClientState key.
1020 install_list
->AddSetRegValueWorkItem(
1021 installer_state
.root_key(),
1022 chrome_product
->distribution()->GetStateKey(),
1024 google_update::kRegUsageStatsField
,
1029 bool AppendPostInstallTasks(const InstallerState
& installer_state
,
1030 const base::FilePath
& setup_path
,
1031 const Version
* current_version
,
1032 const Version
& new_version
,
1033 const base::FilePath
& temp_path
,
1034 WorkItemList
* post_install_task_list
) {
1035 DCHECK(post_install_task_list
);
1037 HKEY root
= installer_state
.root_key();
1038 const Products
& products
= installer_state
.products();
1039 base::FilePath
new_chrome_exe(
1040 installer_state
.target_path().Append(installer::kChromeNewExe
));
1042 // Append work items that will only be executed if this was an update.
1043 // We update the 'opv' value with the current version that is active,
1044 // the 'cpv' value with the critical update version (if present), and the
1045 // 'cmd' value with the rename command to run.
1047 scoped_ptr
<WorkItemList
> in_use_update_work_items(
1048 WorkItem::CreateConditionalWorkItemList(
1049 new ConditionRunIfFileExists(new_chrome_exe
)));
1050 in_use_update_work_items
->set_log_message("InUseUpdateWorkItemList");
1052 // |critical_version| will be valid only if this in-use update includes a
1053 // version considered critical relative to the version being updated.
1054 Version
critical_version(installer_state
.DetermineCriticalVersion(
1055 current_version
, new_version
));
1056 base::FilePath
installer_path(
1057 installer_state
.GetInstallerDirectory(new_version
).Append(
1058 setup_path
.BaseName()));
1060 base::CommandLine
rename(installer_path
);
1061 rename
.AppendSwitch(switches::kRenameChromeExe
);
1062 if (installer_state
.system_install())
1063 rename
.AppendSwitch(switches::kSystemLevel
);
1065 if (installer_state
.verbose_logging())
1066 rename
.AppendSwitch(switches::kVerboseLogging
);
1068 base::string16 version_key
;
1069 for (size_t i
= 0; i
< products
.size(); ++i
) {
1070 BrowserDistribution
* dist
= products
[i
]->distribution();
1071 version_key
= dist
->GetVersionKey();
1073 if (current_version
) {
1074 in_use_update_work_items
->AddSetRegValueWorkItem(
1078 google_update::kRegOldVersionField
,
1079 ASCIIToUTF16(current_version
->GetString()),
1082 if (critical_version
.IsValid()) {
1083 in_use_update_work_items
->AddSetRegValueWorkItem(
1087 google_update::kRegCriticalVersionField
,
1088 ASCIIToUTF16(critical_version
.GetString()),
1091 in_use_update_work_items
->AddDeleteRegValueWorkItem(
1095 google_update::kRegCriticalVersionField
);
1098 // Adding this registry entry for all products (but the binaries) is
1099 // overkill. However, as it stands, we don't have a way to know which
1100 // product will check the key and run the command, so we add it for all.
1101 // The first to run it will perform the operation and clean up the other
1103 if (dist
->GetType() != BrowserDistribution::CHROME_BINARIES
) {
1104 base::CommandLine
product_rename_cmd(rename
);
1105 products
[i
]->AppendRenameFlags(&product_rename_cmd
);
1106 in_use_update_work_items
->AddSetRegValueWorkItem(
1110 google_update::kRegRenameCmdField
,
1111 product_rename_cmd
.GetCommandLineString(),
1116 post_install_task_list
->AddWorkItem(in_use_update_work_items
.release());
1119 // Append work items that will be executed if this was NOT an in-use update.
1121 scoped_ptr
<WorkItemList
> regular_update_work_items(
1122 WorkItem::CreateConditionalWorkItemList(
1123 new Not(new ConditionRunIfFileExists(new_chrome_exe
))));
1124 regular_update_work_items
->set_log_message("RegularUpdateWorkItemList");
1126 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1127 for (size_t i
= 0; i
< products
.size(); ++i
) {
1128 BrowserDistribution
* dist
= products
[i
]->distribution();
1129 base::string16
version_key(dist
->GetVersionKey());
1130 regular_update_work_items
->AddDeleteRegValueWorkItem(
1134 google_update::kRegOldVersionField
);
1135 regular_update_work_items
->AddDeleteRegValueWorkItem(
1139 google_update::kRegCriticalVersionField
);
1140 regular_update_work_items
->AddDeleteRegValueWorkItem(
1144 google_update::kRegRenameCmdField
);
1147 post_install_task_list
->AddWorkItem(regular_update_work_items
.release());
1150 AddRegisterComDllWorkItemsForPackage(installer_state
, current_version
,
1151 new_version
, post_install_task_list
);
1153 // If we're told that we're an MSI install, make sure to set the marker
1154 // in the client state key so that future updates do the right thing.
1155 if (installer_state
.is_msi()) {
1156 for (size_t i
= 0; i
< products
.size(); ++i
) {
1157 const Product
* product
= products
[i
];
1158 AddSetMsiMarkerWorkItem(installer_state
, product
->distribution(), true,
1159 post_install_task_list
);
1161 // We want MSI installs to take over the Add/Remove Programs shortcut.
1162 // Make a best-effort attempt to delete any shortcuts left over from
1163 // previous non-MSI installations for the same type of install (system or
1165 if (product
->ShouldCreateUninstallEntry()) {
1166 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state
, *product
,
1168 post_install_task_list
);
1176 void AddInstallWorkItems(const InstallationState
& original_state
,
1177 const InstallerState
& installer_state
,
1178 const base::FilePath
& setup_path
,
1179 const base::FilePath
& archive_path
,
1180 const base::FilePath
& src_path
,
1181 const base::FilePath
& temp_path
,
1182 const Version
* current_version
,
1183 const Version
& new_version
,
1184 WorkItemList
* install_list
) {
1185 DCHECK(install_list
);
1187 const base::FilePath
& target_path
= installer_state
.target_path();
1189 // A temp directory that work items need and the actual install directory.
1190 install_list
->AddCreateDirWorkItem(temp_path
);
1191 install_list
->AddCreateDirWorkItem(target_path
);
1193 if (installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
) ||
1194 installer_state
.FindProduct(BrowserDistribution::CHROME_BINARIES
)) {
1195 AddChromeWorkItems(original_state
,
1206 #if defined(GOOGLE_CHROME_BUILD)
1207 // For Chrome, unconditionally remove the legacy app_host.exe.
1208 if (!InstallUtil::IsChromeSxSProcess())
1209 AddRemoveLegacyAppHostExeWorkItems(target_path
, temp_path
, install_list
);
1210 #endif // GOOGLE_CHROME_BUILD
1212 // Copy installer in install directory
1213 AddInstallerCopyTasks(installer_state
, setup_path
, archive_path
, temp_path
,
1214 new_version
, install_list
);
1216 const HKEY root
= installer_state
.root_key();
1217 // Only set "lang" for user-level installs since for system-level, the install
1218 // language may not be related to a given user's runtime language.
1219 const bool add_language_identifier
= !installer_state
.system_install();
1221 const Products
& products
= installer_state
.products();
1222 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1224 const Product
& product
= **it
;
1226 AddUninstallShortcutWorkItems(installer_state
, setup_path
, new_version
,
1227 product
, install_list
);
1229 BrowserDistribution
* dist
= product
.distribution();
1230 AddVersionKeyWorkItems(root
,
1231 dist
->GetVersionKey(),
1232 dist
->GetDisplayName(),
1234 add_language_identifier
,
1237 AddDelegateExecuteWorkItems(installer_state
, target_path
, new_version
,
1238 product
, install_list
);
1240 AddActiveSetupWorkItems(installer_state
, setup_path
, new_version
, product
,
1244 // Ensure that the Clients key for the binaries is gone for single installs.
1245 if (!installer_state
.is_multi_install()) {
1246 BrowserDistribution
* binaries_dist
=
1247 BrowserDistribution::GetSpecificDistribution(
1248 BrowserDistribution::CHROME_BINARIES
);
1249 install_list
->AddDeleteRegKeyWorkItem(root
,
1250 binaries_dist
->GetVersionKey(),
1254 #if defined(GOOGLE_CHROME_BUILD)
1255 if (!InstallUtil::IsChromeSxSProcess())
1256 AddRemoveLegacyAppCommandsWorkItems(installer_state
, install_list
);
1257 #endif // GOOGLE_CHROME_BUILD
1259 // Add any remaining work items that involve special settings for
1261 AddProductSpecificWorkItems(original_state
,
1265 current_version
== NULL
,
1266 add_language_identifier
,
1269 // Copy over brand, usagestats, and other values.
1270 AddGoogleUpdateWorkItems(original_state
, installer_state
, install_list
);
1272 // Migrate usagestats back to Chrome.
1273 AddMigrateUsageStatesWorkItems(original_state
, installer_state
, install_list
);
1275 // Append the tasks that run after the installation.
1276 AppendPostInstallTasks(installer_state
,
1284 void AddRegisterComDllWorkItems(const base::FilePath
& dll_folder
,
1285 const std::vector
<base::FilePath
>& dll_list
,
1288 bool ignore_failures
,
1289 WorkItemList
* work_item_list
) {
1290 DCHECK(work_item_list
);
1291 if (dll_list
.empty()) {
1292 VLOG(1) << "No COM DLLs to register";
1294 std::vector
<base::FilePath
>::const_iterator
dll_iter(dll_list
.begin());
1295 for (; dll_iter
!= dll_list
.end(); ++dll_iter
) {
1296 base::FilePath dll_path
= dll_folder
.Append(*dll_iter
);
1297 WorkItem
* work_item
= work_item_list
->AddSelfRegWorkItem(
1298 dll_path
.value(), do_register
, !system_level
);
1300 work_item
->set_ignore_failure(ignore_failures
);
1305 void AddSetMsiMarkerWorkItem(const InstallerState
& installer_state
,
1306 BrowserDistribution
* dist
,
1308 WorkItemList
* work_item_list
) {
1309 DCHECK(work_item_list
);
1310 DWORD msi_value
= set
? 1 : 0;
1311 WorkItem
* set_msi_work_item
=
1312 work_item_list
->AddSetRegValueWorkItem(installer_state
.root_key(),
1313 dist
->GetStateKey(),
1315 google_update::kRegMSIField
,
1318 DCHECK(set_msi_work_item
);
1319 set_msi_work_item
->set_ignore_failure(true);
1320 set_msi_work_item
->set_log_message("Could not write MSI marker!");
1323 void AddDelegateExecuteWorkItems(const InstallerState
& installer_state
,
1324 const base::FilePath
& target_path
,
1325 const Version
& new_version
,
1326 const Product
& product
,
1327 WorkItemList
* list
) {
1328 base::string16 handler_class_uuid
;
1329 BrowserDistribution
* dist
= product
.distribution();
1330 if (!dist
->GetCommandExecuteImplClsid(&handler_class_uuid
)) {
1331 if (InstallUtil::IsChromeSxSProcess()) {
1332 CleanupBadCanaryDelegateExecuteRegistration(target_path
, list
);
1334 VLOG(1) << "No DelegateExecute verb handler processing to do for "
1335 << dist
->GetDisplayName();
1340 HKEY root
= installer_state
.root_key();
1341 base::string16
delegate_execute_path(L
"Software\\Classes\\CLSID\\");
1342 delegate_execute_path
.append(handler_class_uuid
);
1344 // Unconditionally remove registration regardless of whether or not it is
1345 // needed since builds after r132190 included it when it wasn't strictly
1346 // necessary. Do this removal before adding in the new key to ensure that
1347 // the COM probe/flush below does its job.
1348 AddUninstallDelegateExecuteWorkItems(root
, delegate_execute_path
, list
);
1350 // Add work items to register the handler iff it is present.
1351 // See also shell_util.cc's GetProgIdEntries.
1352 if (installer_state
.operation() != InstallerState::UNINSTALL
) {
1353 VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1355 // Force COM to flush its cache containing the path to the old handler.
1356 list
->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback
,
1357 handler_class_uuid
));
1359 // The path to the exe (in the version directory).
1360 base::FilePath
delegate_execute(target_path
);
1361 if (new_version
.IsValid())
1362 delegate_execute
= delegate_execute
.AppendASCII(new_version
.GetString());
1363 delegate_execute
= delegate_execute
.Append(kDelegateExecuteExe
);
1365 // Command-line featuring the quoted path to the exe.
1366 base::string16
command(1, L
'"');
1367 command
.append(delegate_execute
.value()).append(1, L
'"');
1369 // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1370 list
->AddCreateRegKeyWorkItem(
1371 root
, delegate_execute_path
, WorkItem::kWow64Default
);
1372 list
->AddSetRegValueWorkItem(root
,
1373 delegate_execute_path
,
1374 WorkItem::kWow64Default
,
1376 L
"CommandExecuteImpl Class",
1378 base::string16
subkey(delegate_execute_path
);
1379 subkey
.append(L
"\\LocalServer32");
1380 list
->AddCreateRegKeyWorkItem(root
, subkey
, WorkItem::kWow64Default
);
1381 list
->AddSetRegValueWorkItem(
1382 root
, subkey
, WorkItem::kWow64Default
, L
"", command
, true);
1383 list
->AddSetRegValueWorkItem(root
,
1385 WorkItem::kWow64Default
,
1386 L
"ServerExecutable",
1387 delegate_execute
.value(),
1390 subkey
.assign(delegate_execute_path
).append(L
"\\Programmable");
1391 list
->AddCreateRegKeyWorkItem(root
, subkey
, WorkItem::kWow64Default
);
1395 void AddActiveSetupWorkItems(const InstallerState
& installer_state
,
1396 const base::FilePath
& setup_path
,
1397 const Version
& new_version
,
1398 const Product
& product
,
1399 WorkItemList
* list
) {
1400 DCHECK(installer_state
.operation() != InstallerState::UNINSTALL
);
1401 BrowserDistribution
* dist
= product
.distribution();
1403 if (!product
.is_chrome() || !installer_state
.system_install()) {
1404 const char* install_level
=
1405 installer_state
.system_install() ? "system" : "user";
1406 VLOG(1) << "No Active Setup processing to do for " << install_level
1407 << "-level " << dist
->GetDisplayName();
1410 DCHECK(installer_state
.RequiresActiveSetup());
1412 const HKEY root
= HKEY_LOCAL_MACHINE
;
1413 const base::string16
active_setup_path(InstallUtil::GetActiveSetupPath(dist
));
1415 VLOG(1) << "Adding registration items for Active Setup.";
1416 list
->AddCreateRegKeyWorkItem(
1417 root
, active_setup_path
, WorkItem::kWow64Default
);
1418 list
->AddSetRegValueWorkItem(root
,
1420 WorkItem::kWow64Default
,
1422 dist
->GetDisplayName(),
1425 base::FilePath
active_setup_exe(installer_state
.GetInstallerDirectory(
1426 new_version
).Append(kActiveSetupExe
));
1427 base::CommandLine
cmd(active_setup_exe
);
1428 cmd
.AppendSwitch(installer::switches::kConfigureUserSettings
);
1429 cmd
.AppendSwitch(installer::switches::kVerboseLogging
);
1430 cmd
.AppendSwitch(installer::switches::kSystemLevel
);
1431 product
.AppendProductFlags(&cmd
);
1432 list
->AddSetRegValueWorkItem(root
,
1434 WorkItem::kWow64Default
,
1436 cmd
.GetCommandLineString(),
1439 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1441 list
->AddSetRegValueWorkItem(root
,
1443 WorkItem::kWow64Default
,
1445 dist
->GetDisplayName(),
1448 list
->AddSetRegValueWorkItem(root
,
1450 WorkItem::kWow64Default
,
1452 static_cast<DWORD
>(1U),
1455 list
->AddSetRegValueWorkItem(root
,
1457 WorkItem::kWow64Default
,
1459 kActiveSetupVersion
,
1463 void AddDeleteOldIELowRightsPolicyWorkItems(
1464 const InstallerState
& installer_state
,
1465 WorkItemList
* install_list
) {
1466 DCHECK(install_list
);
1468 base::string16 key_path
;
1469 GetOldIELowRightsElevationPolicyKeyPath(&key_path
);
1470 install_list
->AddDeleteRegKeyWorkItem(
1471 installer_state
.root_key(), key_path
, WorkItem::kWow64Default
);
1474 void AppendUninstallCommandLineFlags(const InstallerState
& installer_state
,
1475 const Product
& product
,
1476 base::CommandLine
* uninstall_cmd
) {
1477 DCHECK(uninstall_cmd
);
1479 uninstall_cmd
->AppendSwitch(installer::switches::kUninstall
);
1481 // Append the product-specific uninstall flags.
1482 product
.AppendProductFlags(uninstall_cmd
);
1483 if (installer_state
.is_msi())
1484 uninstall_cmd
->AppendSwitch(installer::switches::kMsi
);
1485 if (installer_state
.system_install())
1486 uninstall_cmd
->AppendSwitch(installer::switches::kSystemLevel
);
1487 if (installer_state
.verbose_logging())
1488 uninstall_cmd
->AppendSwitch(installer::switches::kVerboseLogging
);
1491 void RefreshElevationPolicy() {
1492 const wchar_t kIEFrameDll
[] = L
"ieframe.dll";
1493 const char kIERefreshPolicy
[] = "IERefreshElevationPolicy";
1495 HMODULE ieframe
= LoadLibrary(kIEFrameDll
);
1497 typedef HRESULT (__stdcall
*IERefreshPolicy
)();
1498 IERefreshPolicy ie_refresh_policy
= reinterpret_cast<IERefreshPolicy
>(
1499 GetProcAddress(ieframe
, kIERefreshPolicy
));
1501 if (ie_refresh_policy
) {
1502 ie_refresh_policy();
1504 VLOG(1) << kIERefreshPolicy
<< " not supported.";
1507 FreeLibrary(ieframe
);
1509 VLOG(1) << "Cannot load " << kIEFrameDll
;
1513 void AddOsUpgradeWorkItems(const InstallerState
& installer_state
,
1514 const base::FilePath
& setup_path
,
1515 const Version
& new_version
,
1516 const Product
& product
,
1517 WorkItemList
* install_list
) {
1518 const HKEY root_key
= installer_state
.root_key();
1519 base::string16
cmd_key(
1520 GetRegCommandKey(product
.distribution(), kCmdOnOsUpgrade
));
1522 if (installer_state
.operation() == InstallerState::UNINSTALL
) {
1523 install_list
->AddDeleteRegKeyWorkItem(root_key
, cmd_key
, KEY_WOW64_32KEY
)
1524 ->set_log_message("Removing OS upgrade command");
1526 // Register with Google Update to have setup.exe --on-os-upgrade called on
1528 base::CommandLine
cmd_line(
1529 installer_state
.GetInstallerDirectory(new_version
)
1530 .Append(setup_path
.BaseName()));
1531 // Add the main option to indicate OS upgrade flow.
1532 cmd_line
.AppendSwitch(installer::switches::kOnOsUpgrade
);
1533 // Add product-specific options.
1534 product
.AppendProductFlags(&cmd_line
);
1535 if (installer_state
.system_install())
1536 cmd_line
.AppendSwitch(installer::switches::kSystemLevel
);
1537 // Log everything for now.
1538 cmd_line
.AppendSwitch(installer::switches::kVerboseLogging
);
1540 AppCommand
cmd(cmd_line
.GetCommandLineString());
1541 cmd
.set_is_auto_run_on_os_upgrade(true);
1542 cmd
.AddWorkItems(installer_state
.root_key(), cmd_key
, install_list
);
1546 void AddQuickEnableChromeFrameWorkItems(const InstallerState
& installer_state
,
1547 WorkItemList
* work_item_list
) {
1548 DCHECK(work_item_list
);
1550 base::string16
cmd_key(
1551 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1552 BrowserDistribution::CHROME_BINARIES
),
1553 kCmdQuickEnableCf
));
1555 // Unconditionally remove the legacy Quick Enable command from the binaries.
1556 // Do this even if multi-install Chrome isn't installed to ensure that it is
1557 // not left behind in any case.
1558 work_item_list
->AddDeleteRegKeyWorkItem(
1559 installer_state
.root_key(), cmd_key
, KEY_WOW64_32KEY
)
1560 ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf
) +
1564 } // namespace installer