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