Convert browser_tests to Swarming.
[chromium-blink-merge.git] / chrome / installer / setup / install_worker.cc
blob4824016a21eafc096c1b9da61ad012fcbcf0dc18
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/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;
56 namespace installer {
58 namespace {
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"43,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()) {
103 if (old_version) {
104 base::FilePath old_dll_path(installer_state.target_path().AppendASCII(
105 old_version->GetString()));
107 installer::AddRegisterComDllWorkItems(old_dll_path,
108 com_dll_list,
109 installer_state.system_install(),
110 false, // Unregister
111 true, // May fail
112 work_item_list);
115 base::FilePath dll_path(installer_state.target_path().AppendASCII(
116 new_version.GetString()));
117 installer::AddRegisterComDllWorkItems(dll_path,
118 com_dll_list,
119 installer_state.system_install(),
120 true, // Register
121 false, // Must succeed.
122 work_item_list);
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(),
150 WorkItem::ALWAYS);
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
160 // copied.
161 if (temp_path.IsParent(archive_path)) {
162 install_list->AddMoveTreeWorkItem(archive_path.value(),
163 archive_dst.value(),
164 temp_path.value(),
165 WorkItem::ALWAYS_MOVE);
166 } else {
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(),
170 archive_dst.value(),
171 temp_path.value(),
172 WorkItem::ALWAYS);
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,
193 const wchar_t* app,
194 const char* command_with_parameter,
195 WorkItemList* work_item_list) {
196 DCHECK(command_key);
197 DCHECK(app);
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(),
206 full_cmd_key,
207 KEY_WOW64_32KEY)
208 ->set_log_message("removing " + base::UTF16ToASCII(command_key) +
209 " command");
210 } else {
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
226 // case.
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)
234 return true;
236 scoped_ptr<FirewallManager> manager =
237 FirewallManager::Create(dist, chrome_path);
238 if (!manager) {
239 LOG(ERROR) << "Failed creating a FirewallManager. Continuing with install.";
240 return true;
243 if (work_item.IsRollback()) {
244 manager->RemoveFirewallRules();
245 return true;
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.
254 return true;
257 // Adds work items to |list| to create firewall rules.
258 void AddFirewallRulesWorkItems(const InstallerState& installer_state,
259 BrowserDistribution* dist,
260 bool is_new_install,
261 WorkItemList* list) {
262 list->AddCallbackWorkItem(
263 base::Bind(&AddFirewallRulesCallback,
264 installer_state.system_install(),
265 dist,
266 installer_state.target_path().Append(kChromeExe),
267 is_new_install));
270 void AddProductSpecificWorkItems(const InstallationState& original_state,
271 const InstallerState& installer_state,
272 const base::FilePath& setup_path,
273 const Version& new_version,
274 bool is_new_install,
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();
279 ++it) {
280 const Product& p = **it;
281 if (p.is_chrome()) {
282 AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p,
283 list);
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);
326 } else {
327 PathService::Get(base::DIR_START_MENU, &uninstall_link);
330 if (uninstall_link.empty()) {
331 LOG(ERROR) << "Failed to get location for shortcut.";
332 } else {
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(),
388 temp_path.value(),
389 WorkItem::ALWAYS);
390 } else {
391 install_list->AddCopyTreeWorkItem(
392 src_path.Append(installer::kChromeExe).value(),
393 target_path.Append(installer::kChromeExe).value(),
394 temp_path.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(),
409 temp_path.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
415 // never be.
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(),
421 temp_path.value(),
422 WorkItem::ALWAYS_MOVE);
423 } else {
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),
428 temp_path);
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(),
442 temp_path.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
456 // probe fails.
457 bool ProbeCommandExecuteCallback(const base::string16& command_execute_id,
458 const CallbackWorkItem& work_item) {
459 // Noop on rollback.
460 if (work_item.IsRollback())
461 return true;
463 CLSID class_id = {};
465 HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
466 if (FAILED(hr)) {
467 LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
468 "CLSID; hr=0x" << std::hex << hr;
469 } else {
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"
475 << std::hex << hr;
479 return true;
482 void AddUninstallDelegateExecuteWorkItems(
483 HKEY root,
484 const base::string16& delegate_execute_path,
485 WorkItemList* list) {
486 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
487 << root;
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}",
499 KEY_WOW64_32KEY);
500 list->AddDeleteRegKeyWorkItem(root,
501 L"Software\\Classes\\TypeLib\\"
502 #if defined(GOOGLE_CHROME_BUILD)
503 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}",
504 #else
505 L"{7779FB70-B399-454A-AA1A-BAA850032B10}",
506 #endif
507 KEY_WOW64_32KEY);
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 &registered_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.";
544 } // namespace
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,
579 update_state_key,
580 KEY_WOW64_32KEY,
581 installer::kUninstallStringField,
582 installer_path.value(),
583 true);
584 install_list->AddSetRegValueWorkItem(
585 reg_root,
586 update_state_key,
587 KEY_WOW64_32KEY,
588 installer::kUninstallArgumentsField,
589 uninstall_arguments.GetCommandLineString(),
590 true);
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,
603 uninstall_reg,
604 KEY_WOW64_32KEY,
605 installer::kUninstallDisplayNameField,
606 browser_dist->GetDisplayName(),
607 true);
608 install_list->AddSetRegValueWorkItem(
609 reg_root,
610 uninstall_reg,
611 KEY_WOW64_32KEY,
612 installer::kUninstallStringField,
613 quoted_uninstall_cmd.GetCommandLineString(),
614 true);
615 install_list->AddSetRegValueWorkItem(reg_root,
616 uninstall_reg,
617 KEY_WOW64_32KEY,
618 L"InstallLocation",
619 install_path.value(),
620 true);
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,
627 uninstall_reg,
628 KEY_WOW64_32KEY,
629 L"DisplayIcon",
630 chrome_icon,
631 true);
632 install_list->AddSetRegValueWorkItem(reg_root,
633 uninstall_reg,
634 KEY_WOW64_32KEY,
635 L"NoModify",
636 static_cast<DWORD>(1),
637 true);
638 install_list->AddSetRegValueWorkItem(reg_root,
639 uninstall_reg,
640 KEY_WOW64_32KEY,
641 L"NoRepair",
642 static_cast<DWORD>(1),
643 true);
645 install_list->AddSetRegValueWorkItem(reg_root,
646 uninstall_reg,
647 KEY_WOW64_32KEY,
648 L"Publisher",
649 browser_dist->GetPublisherName(),
650 true);
651 install_list->AddSetRegValueWorkItem(reg_root,
652 uninstall_reg,
653 KEY_WOW64_32KEY,
654 L"Version",
655 ASCIIToUTF16(new_version.GetString()),
656 true);
657 install_list->AddSetRegValueWorkItem(reg_root,
658 uninstall_reg,
659 KEY_WOW64_32KEY,
660 L"DisplayVersion",
661 ASCIIToUTF16(new_version.GetString()),
662 true);
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,
666 uninstall_reg,
667 KEY_WOW64_32KEY,
668 L"InstallDate",
669 InstallUtil::GetCurrentDate(),
670 false);
672 const std::vector<uint32_t>& 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(
676 reg_root,
677 uninstall_reg,
678 KEY_WOW64_32KEY,
679 L"VersionMajor",
680 static_cast<DWORD>(version_components[2]),
681 true);
682 install_list->AddSetRegValueWorkItem(
683 reg_root,
684 uninstall_reg,
685 KEY_WOW64_32KEY,
686 L"VersionMinor",
687 static_cast<DWORD>(version_components[3]),
688 true);
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,
704 version_key,
705 KEY_WOW64_32KEY,
706 google_update::kRegNameField,
707 product_name,
708 true); // overwrite name also
709 list->AddSetRegValueWorkItem(root,
710 version_key,
711 KEY_WOW64_32KEY,
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 (base::LowerCaseEqualsASCII(language, "en-us"))
721 language.resize(2);
722 list->AddSetRegValueWorkItem(root,
723 version_key,
724 KEY_WOW64_32KEY,
725 google_update::kRegLangField,
726 language,
727 false); // do not overwrite language
729 list->AddSetRegValueWorkItem(root,
730 version_key,
731 KEY_WOW64_32KEY,
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
753 // installed.
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();
760 } else {
761 // Nothing is being installed? Entirely unexpected, so do no harm.
762 LOG(ERROR) << "No products found in AddOemInstallWorkItems";
763 return;
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)->
772 GetDisplayName();
773 install_list->AddCreateRegKeyWorkItem(
774 root_key, multi_key, KEY_WOW64_32KEY);
775 // Always overwrite an old value.
776 install_list->AddSetRegValueWorkItem(root_key,
777 multi_key,
778 KEY_WOW64_32KEY,
779 google_update::kRegOemInstallField,
780 oem_install,
781 true);
782 } else {
783 // Clear any old value.
784 install_list->AddDeleteRegValueWorkItem(
785 root_key,
786 multi_key,
787 KEY_WOW64_32KEY,
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();
814 ++it) {
815 const Product& product = **it;
816 if (product.is_chrome_binaries())
817 continue;
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)->
834 GetDisplayName();
835 install_list->AddCreateRegKeyWorkItem(
836 root_key, multi_key, KEY_WOW64_32KEY);
837 install_list->AddSetRegValueWorkItem(root_key,
838 multi_key,
839 KEY_WOW64_32KEY,
840 google_update::kRegEULAAceptedField,
841 eula_accepted,
842 true);
843 } else {
844 // Clear any old value.
845 install_list->AddDeleteRegValueWorkItem(
846 root_key,
847 multi_key,
848 KEY_WOW64_32KEY,
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();
862 return;
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
871 // binaries exists.
872 if (system_install) {
873 install_list->AddCreateRegKeyWorkItem(
874 root_key,
875 installer_state.multi_package_binaries_distribution()
876 ->GetStateMediumKey()
877 .c_str(),
878 KEY_WOW64_32KEY);
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,
895 multi_key,
896 KEY_WOW64_32KEY,
897 google_update::kRegBrandField,
898 brand,
899 false);
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())
926 continue;
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.
936 if (value_found) {
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,
943 state_key,
944 KEY_WOW64_32KEY,
945 google_update::kRegUsageStatsField,
946 usagestats,
947 true);
949 for (Products::const_iterator scan = products.begin(), end = products.end();
950 scan != end; ++scan) {
951 if ((*scan)->is_chrome_binaries())
952 continue;
953 BrowserDistribution* dist = (*scan)->distribution();
954 if (installer_state.system_install()) {
955 install_list->AddDeleteRegValueWorkItem(
956 root_key,
957 dist->GetStateMediumKey(),
958 KEY_WOW64_32KEY,
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(
963 HKEY_CURRENT_USER,
964 dist->GetStateKey(),
965 KEY_WOW64_32KEY,
966 google_update::kRegUsageStatsField);
968 install_list->AddDeleteRegValueWorkItem(
969 root_key,
970 dist->GetStateKey(),
971 KEY_WOW64_32KEY,
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())
985 return;
987 // This operation doesn't apply to SxS Chrome.
988 if (InstallUtil::IsChromeSxSProcess())
989 return;
991 // Ensure that Chrome is the product being installed or updated (there are no
992 // other products, so it is especially unexpected for this to fail).
993 const Product* chrome_product =
994 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
995 if (!chrome_product) {
996 LOG(DFATAL) << "Not operating on Chrome while migrating to single-install.";
997 return;
1000 const ProductState* chrome_state = original_state.GetProductState(
1001 installer_state.system_install(),
1002 BrowserDistribution::CHROME_BROWSER);
1003 // Bail out if there is not an existing multi-install Chrome that is being
1004 // updated.
1005 if (!chrome_state || !chrome_state->is_multi_install()) {
1006 VLOG(1) << "No multi-install Chrome found to migrate to single-install.";
1007 return;
1010 const ProductState* binaries_state = original_state.GetProductState(
1011 installer_state.system_install(),
1012 BrowserDistribution::CHROME_BINARIES);
1014 // There is nothing to be done if the binaries do not have stats.
1015 DWORD usagestats = 0;
1016 if (!binaries_state || !binaries_state->GetUsageStats(&usagestats)) {
1017 VLOG(1) << "No usagestats value found to migrate to single-install.";
1018 return;
1021 VLOG(1) << "Migrating usagestats value from multi-install to single-install.";
1023 // Write the value that was read to Chrome's ClientState key.
1024 install_list->AddSetRegValueWorkItem(
1025 installer_state.root_key(),
1026 chrome_product->distribution()->GetStateKey(),
1027 KEY_WOW64_32KEY,
1028 google_update::kRegUsageStatsField,
1029 usagestats,
1030 true);
1033 bool AppendPostInstallTasks(const InstallerState& installer_state,
1034 const base::FilePath& setup_path,
1035 const Version* current_version,
1036 const Version& new_version,
1037 const base::FilePath& temp_path,
1038 WorkItemList* post_install_task_list) {
1039 DCHECK(post_install_task_list);
1041 HKEY root = installer_state.root_key();
1042 const Products& products = installer_state.products();
1043 base::FilePath new_chrome_exe(
1044 installer_state.target_path().Append(installer::kChromeNewExe));
1046 // Append work items that will only be executed if this was an update.
1047 // We update the 'opv' value with the current version that is active,
1048 // the 'cpv' value with the critical update version (if present), and the
1049 // 'cmd' value with the rename command to run.
1051 scoped_ptr<WorkItemList> in_use_update_work_items(
1052 WorkItem::CreateConditionalWorkItemList(
1053 new ConditionRunIfFileExists(new_chrome_exe)));
1054 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
1056 // |critical_version| will be valid only if this in-use update includes a
1057 // version considered critical relative to the version being updated.
1058 Version critical_version(installer_state.DetermineCriticalVersion(
1059 current_version, new_version));
1060 base::FilePath installer_path(
1061 installer_state.GetInstallerDirectory(new_version).Append(
1062 setup_path.BaseName()));
1064 base::CommandLine rename(installer_path);
1065 rename.AppendSwitch(switches::kRenameChromeExe);
1066 if (installer_state.system_install())
1067 rename.AppendSwitch(switches::kSystemLevel);
1069 if (installer_state.verbose_logging())
1070 rename.AppendSwitch(switches::kVerboseLogging);
1072 base::string16 version_key;
1073 for (size_t i = 0; i < products.size(); ++i) {
1074 BrowserDistribution* dist = products[i]->distribution();
1075 version_key = dist->GetVersionKey();
1077 if (current_version) {
1078 in_use_update_work_items->AddSetRegValueWorkItem(
1079 root,
1080 version_key,
1081 KEY_WOW64_32KEY,
1082 google_update::kRegOldVersionField,
1083 ASCIIToUTF16(current_version->GetString()),
1084 true);
1086 if (critical_version.IsValid()) {
1087 in_use_update_work_items->AddSetRegValueWorkItem(
1088 root,
1089 version_key,
1090 KEY_WOW64_32KEY,
1091 google_update::kRegCriticalVersionField,
1092 ASCIIToUTF16(critical_version.GetString()),
1093 true);
1094 } else {
1095 in_use_update_work_items->AddDeleteRegValueWorkItem(
1096 root,
1097 version_key,
1098 KEY_WOW64_32KEY,
1099 google_update::kRegCriticalVersionField);
1102 // Adding this registry entry for all products (but the binaries) is
1103 // overkill. However, as it stands, we don't have a way to know which
1104 // product will check the key and run the command, so we add it for all.
1105 // The first to run it will perform the operation and clean up the other
1106 // values.
1107 if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
1108 base::CommandLine product_rename_cmd(rename);
1109 products[i]->AppendRenameFlags(&product_rename_cmd);
1110 in_use_update_work_items->AddSetRegValueWorkItem(
1111 root,
1112 version_key,
1113 KEY_WOW64_32KEY,
1114 google_update::kRegRenameCmdField,
1115 product_rename_cmd.GetCommandLineString(),
1116 true);
1120 post_install_task_list->AddWorkItem(in_use_update_work_items.release());
1123 // Append work items that will be executed if this was NOT an in-use update.
1125 scoped_ptr<WorkItemList> regular_update_work_items(
1126 WorkItem::CreateConditionalWorkItemList(
1127 new Not(new ConditionRunIfFileExists(new_chrome_exe))));
1128 regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
1130 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1131 for (size_t i = 0; i < products.size(); ++i) {
1132 BrowserDistribution* dist = products[i]->distribution();
1133 base::string16 version_key(dist->GetVersionKey());
1134 regular_update_work_items->AddDeleteRegValueWorkItem(
1135 root,
1136 version_key,
1137 KEY_WOW64_32KEY,
1138 google_update::kRegOldVersionField);
1139 regular_update_work_items->AddDeleteRegValueWorkItem(
1140 root,
1141 version_key,
1142 KEY_WOW64_32KEY,
1143 google_update::kRegCriticalVersionField);
1144 regular_update_work_items->AddDeleteRegValueWorkItem(
1145 root,
1146 version_key,
1147 KEY_WOW64_32KEY,
1148 google_update::kRegRenameCmdField);
1151 post_install_task_list->AddWorkItem(regular_update_work_items.release());
1154 AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
1155 new_version, post_install_task_list);
1157 // If we're told that we're an MSI install, make sure to set the marker
1158 // in the client state key so that future updates do the right thing.
1159 if (installer_state.is_msi()) {
1160 for (size_t i = 0; i < products.size(); ++i) {
1161 const Product* product = products[i];
1162 AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
1163 post_install_task_list);
1165 // We want MSI installs to take over the Add/Remove Programs shortcut.
1166 // Make a best-effort attempt to delete any shortcuts left over from
1167 // previous non-MSI installations for the same type of install (system or
1168 // per user).
1169 if (product->ShouldCreateUninstallEntry()) {
1170 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
1171 temp_path,
1172 post_install_task_list);
1177 return true;
1180 void AddInstallWorkItems(const InstallationState& original_state,
1181 const InstallerState& installer_state,
1182 const base::FilePath& setup_path,
1183 const base::FilePath& archive_path,
1184 const base::FilePath& src_path,
1185 const base::FilePath& temp_path,
1186 const Version* current_version,
1187 const Version& new_version,
1188 WorkItemList* install_list) {
1189 DCHECK(install_list);
1191 const base::FilePath& target_path = installer_state.target_path();
1193 // A temp directory that work items need and the actual install directory.
1194 install_list->AddCreateDirWorkItem(temp_path);
1195 install_list->AddCreateDirWorkItem(target_path);
1197 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
1198 installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
1199 AddChromeWorkItems(original_state,
1200 installer_state,
1201 setup_path,
1202 archive_path,
1203 src_path,
1204 temp_path,
1205 current_version,
1206 new_version,
1207 install_list);
1210 #if defined(GOOGLE_CHROME_BUILD)
1211 // For Chrome, unconditionally remove the legacy app_host.exe.
1212 if (!InstallUtil::IsChromeSxSProcess())
1213 AddRemoveLegacyAppHostExeWorkItems(target_path, temp_path, install_list);
1214 #endif // GOOGLE_CHROME_BUILD
1216 // Copy installer in install directory
1217 AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
1218 new_version, install_list);
1220 const HKEY root = installer_state.root_key();
1221 // Only set "lang" for user-level installs since for system-level, the install
1222 // language may not be related to a given user's runtime language.
1223 const bool add_language_identifier = !installer_state.system_install();
1225 const Products& products = installer_state.products();
1226 for (Products::const_iterator it = products.begin(); it < products.end();
1227 ++it) {
1228 const Product& product = **it;
1230 AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
1231 product, install_list);
1233 BrowserDistribution* dist = product.distribution();
1234 AddVersionKeyWorkItems(root,
1235 dist->GetVersionKey(),
1236 dist->GetDisplayName(),
1237 new_version,
1238 add_language_identifier,
1239 install_list);
1241 AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
1242 product, install_list);
1244 AddActiveSetupWorkItems(installer_state, setup_path, new_version, product,
1245 install_list);
1248 // Ensure that the Clients key for the binaries is gone for single installs
1249 // (but not for SxS Chrome).
1250 if (!installer_state.is_multi_install() &&
1251 !InstallUtil::IsChromeSxSProcess()) {
1252 BrowserDistribution* binaries_dist =
1253 BrowserDistribution::GetSpecificDistribution(
1254 BrowserDistribution::CHROME_BINARIES);
1255 install_list->AddDeleteRegKeyWorkItem(root,
1256 binaries_dist->GetVersionKey(),
1257 KEY_WOW64_32KEY);
1260 #if defined(GOOGLE_CHROME_BUILD)
1261 if (!InstallUtil::IsChromeSxSProcess())
1262 AddRemoveLegacyAppCommandsWorkItems(installer_state, install_list);
1263 #endif // GOOGLE_CHROME_BUILD
1265 // Add any remaining work items that involve special settings for
1266 // each product.
1267 AddProductSpecificWorkItems(original_state,
1268 installer_state,
1269 setup_path,
1270 new_version,
1271 current_version == NULL,
1272 add_language_identifier,
1273 install_list);
1275 // Copy over brand, usagestats, and other values.
1276 AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
1278 // Migrate usagestats back to Chrome.
1279 AddMigrateUsageStatesWorkItems(original_state, installer_state, install_list);
1281 // Append the tasks that run after the installation.
1282 AppendPostInstallTasks(installer_state,
1283 setup_path,
1284 current_version,
1285 new_version,
1286 temp_path,
1287 install_list);
1290 void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
1291 const std::vector<base::FilePath>& dll_list,
1292 bool system_level,
1293 bool do_register,
1294 bool ignore_failures,
1295 WorkItemList* work_item_list) {
1296 DCHECK(work_item_list);
1297 if (dll_list.empty()) {
1298 VLOG(1) << "No COM DLLs to register";
1299 } else {
1300 std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
1301 for (; dll_iter != dll_list.end(); ++dll_iter) {
1302 base::FilePath dll_path = dll_folder.Append(*dll_iter);
1303 WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
1304 dll_path.value(), do_register, !system_level);
1305 DCHECK(work_item);
1306 work_item->set_ignore_failure(ignore_failures);
1311 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
1312 BrowserDistribution* dist,
1313 bool set,
1314 WorkItemList* work_item_list) {
1315 DCHECK(work_item_list);
1316 DWORD msi_value = set ? 1 : 0;
1317 WorkItem* set_msi_work_item =
1318 work_item_list->AddSetRegValueWorkItem(installer_state.root_key(),
1319 dist->GetStateKey(),
1320 KEY_WOW64_32KEY,
1321 google_update::kRegMSIField,
1322 msi_value,
1323 true);
1324 DCHECK(set_msi_work_item);
1325 set_msi_work_item->set_ignore_failure(true);
1326 set_msi_work_item->set_log_message("Could not write MSI marker!");
1329 void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
1330 const base::FilePath& target_path,
1331 const Version& new_version,
1332 const Product& product,
1333 WorkItemList* list) {
1334 base::string16 handler_class_uuid;
1335 BrowserDistribution* dist = product.distribution();
1336 if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) {
1337 if (InstallUtil::IsChromeSxSProcess()) {
1338 CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
1339 } else {
1340 VLOG(1) << "No DelegateExecute verb handler processing to do for "
1341 << dist->GetDisplayName();
1343 return;
1346 HKEY root = installer_state.root_key();
1347 base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
1348 delegate_execute_path.append(handler_class_uuid);
1350 // Unconditionally remove registration regardless of whether or not it is
1351 // needed since builds after r132190 included it when it wasn't strictly
1352 // necessary. Do this removal before adding in the new key to ensure that
1353 // the COM probe/flush below does its job.
1354 AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
1356 // Add work items to register the handler iff it is present.
1357 // See also shell_util.cc's GetProgIdEntries.
1358 if (installer_state.operation() != InstallerState::UNINSTALL) {
1359 VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1361 // Force COM to flush its cache containing the path to the old handler.
1362 list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
1363 handler_class_uuid));
1365 // The path to the exe (in the version directory).
1366 base::FilePath delegate_execute(target_path);
1367 if (new_version.IsValid())
1368 delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
1369 delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
1371 // Command-line featuring the quoted path to the exe.
1372 base::string16 command(1, L'"');
1373 command.append(delegate_execute.value()).append(1, L'"');
1375 // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1376 list->AddCreateRegKeyWorkItem(
1377 root, delegate_execute_path, WorkItem::kWow64Default);
1378 list->AddSetRegValueWorkItem(root,
1379 delegate_execute_path,
1380 WorkItem::kWow64Default,
1381 L"",
1382 L"CommandExecuteImpl Class",
1383 true);
1384 base::string16 subkey(delegate_execute_path);
1385 subkey.append(L"\\LocalServer32");
1386 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1387 list->AddSetRegValueWorkItem(
1388 root, subkey, WorkItem::kWow64Default, L"", command, true);
1389 list->AddSetRegValueWorkItem(root,
1390 subkey,
1391 WorkItem::kWow64Default,
1392 L"ServerExecutable",
1393 delegate_execute.value(),
1394 true);
1396 subkey.assign(delegate_execute_path).append(L"\\Programmable");
1397 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1401 void AddActiveSetupWorkItems(const InstallerState& installer_state,
1402 const base::FilePath& setup_path,
1403 const Version& new_version,
1404 const Product& product,
1405 WorkItemList* list) {
1406 DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
1407 BrowserDistribution* dist = product.distribution();
1409 if (!product.is_chrome() || !installer_state.system_install()) {
1410 const char* install_level =
1411 installer_state.system_install() ? "system" : "user";
1412 VLOG(1) << "No Active Setup processing to do for " << install_level
1413 << "-level " << dist->GetDisplayName();
1414 return;
1416 DCHECK(installer_state.RequiresActiveSetup());
1418 const HKEY root = HKEY_LOCAL_MACHINE;
1419 const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist));
1421 VLOG(1) << "Adding registration items for Active Setup.";
1422 list->AddCreateRegKeyWorkItem(
1423 root, active_setup_path, WorkItem::kWow64Default);
1424 list->AddSetRegValueWorkItem(root,
1425 active_setup_path,
1426 WorkItem::kWow64Default,
1427 L"",
1428 dist->GetDisplayName(),
1429 true);
1431 base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
1432 new_version).Append(kActiveSetupExe));
1433 base::CommandLine cmd(active_setup_exe);
1434 cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
1435 cmd.AppendSwitch(installer::switches::kVerboseLogging);
1436 cmd.AppendSwitch(installer::switches::kSystemLevel);
1437 product.AppendProductFlags(&cmd);
1438 list->AddSetRegValueWorkItem(root,
1439 active_setup_path,
1440 WorkItem::kWow64Default,
1441 L"StubPath",
1442 cmd.GetCommandLineString(),
1443 true);
1445 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1446 // resource.
1447 list->AddSetRegValueWorkItem(root,
1448 active_setup_path,
1449 WorkItem::kWow64Default,
1450 L"Localized Name",
1451 dist->GetDisplayName(),
1452 true);
1454 list->AddSetRegValueWorkItem(root,
1455 active_setup_path,
1456 WorkItem::kWow64Default,
1457 L"IsInstalled",
1458 static_cast<DWORD>(1U),
1459 true);
1461 list->AddSetRegValueWorkItem(root,
1462 active_setup_path,
1463 WorkItem::kWow64Default,
1464 L"Version",
1465 kActiveSetupVersion,
1466 true);
1469 void AddDeleteOldIELowRightsPolicyWorkItems(
1470 const InstallerState& installer_state,
1471 WorkItemList* install_list) {
1472 DCHECK(install_list);
1474 base::string16 key_path;
1475 GetOldIELowRightsElevationPolicyKeyPath(&key_path);
1476 install_list->AddDeleteRegKeyWorkItem(
1477 installer_state.root_key(), key_path, WorkItem::kWow64Default);
1480 void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
1481 const Product& product,
1482 base::CommandLine* uninstall_cmd) {
1483 DCHECK(uninstall_cmd);
1485 uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
1487 // Append the product-specific uninstall flags.
1488 product.AppendProductFlags(uninstall_cmd);
1489 if (installer_state.is_msi())
1490 uninstall_cmd->AppendSwitch(installer::switches::kMsi);
1491 if (installer_state.system_install())
1492 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
1493 if (installer_state.verbose_logging())
1494 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
1497 void RefreshElevationPolicy() {
1498 const wchar_t kIEFrameDll[] = L"ieframe.dll";
1499 const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
1501 HMODULE ieframe = LoadLibrary(kIEFrameDll);
1502 if (ieframe) {
1503 typedef HRESULT (__stdcall *IERefreshPolicy)();
1504 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
1505 GetProcAddress(ieframe, kIERefreshPolicy));
1507 if (ie_refresh_policy) {
1508 ie_refresh_policy();
1509 } else {
1510 VLOG(1) << kIERefreshPolicy << " not supported.";
1513 FreeLibrary(ieframe);
1514 } else {
1515 VLOG(1) << "Cannot load " << kIEFrameDll;
1519 void AddOsUpgradeWorkItems(const InstallerState& installer_state,
1520 const base::FilePath& setup_path,
1521 const Version& new_version,
1522 const Product& product,
1523 WorkItemList* install_list) {
1524 const HKEY root_key = installer_state.root_key();
1525 base::string16 cmd_key(
1526 GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
1528 if (installer_state.operation() == InstallerState::UNINSTALL) {
1529 install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
1530 ->set_log_message("Removing OS upgrade command");
1531 } else {
1532 // Register with Google Update to have setup.exe --on-os-upgrade called on
1533 // OS upgrade.
1534 base::CommandLine cmd_line(
1535 installer_state.GetInstallerDirectory(new_version)
1536 .Append(setup_path.BaseName()));
1537 // Add the main option to indicate OS upgrade flow.
1538 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
1539 // Add product-specific options.
1540 product.AppendProductFlags(&cmd_line);
1541 if (installer_state.system_install())
1542 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1543 // Log everything for now.
1544 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1546 AppCommand cmd(cmd_line.GetCommandLineString());
1547 cmd.set_is_auto_run_on_os_upgrade(true);
1548 cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
1552 void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
1553 WorkItemList* work_item_list) {
1554 DCHECK(work_item_list);
1556 base::string16 cmd_key(
1557 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1558 BrowserDistribution::CHROME_BINARIES),
1559 kCmdQuickEnableCf));
1561 // Unconditionally remove the legacy Quick Enable command from the binaries.
1562 // Do this even if multi-install Chrome isn't installed to ensure that it is
1563 // not left behind in any case.
1564 work_item_list->AddDeleteRegKeyWorkItem(
1565 installer_state.root_key(), cmd_key, KEY_WOW64_32KEY)
1566 ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf) +
1567 " command");
1570 } // namespace installer