Add ICU message format support
[chromium-blink-merge.git] / chrome / installer / setup / install_worker.cc
blob4ed8a72c4cf009045a9c7ef6994ccb0a6fce3076
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 // A callback invoked by |work_item| that adds firewall rules for Chrome. Rules
178 // are left in-place on rollback unless |remove_on_rollback| is true. This is
179 // the case for new installs only. Updates and overinstalls leave the rule
180 // in-place on rollback since a previous install of Chrome will be used in that
181 // case.
182 bool AddFirewallRulesCallback(bool system_level,
183 BrowserDistribution* dist,
184 const base::FilePath& chrome_path,
185 bool remove_on_rollback,
186 const CallbackWorkItem& work_item) {
187 // There is no work to do on rollback if this is not a new install.
188 if (work_item.IsRollback() && !remove_on_rollback)
189 return true;
191 scoped_ptr<FirewallManager> manager =
192 FirewallManager::Create(dist, chrome_path);
193 if (!manager) {
194 LOG(ERROR) << "Failed creating a FirewallManager. Continuing with install.";
195 return true;
198 if (work_item.IsRollback()) {
199 manager->RemoveFirewallRules();
200 return true;
203 // Adding the firewall rule is expected to fail for user-level installs on
204 // Vista+. Try anyway in case the installer is running elevated.
205 if (!manager->AddFirewallRules())
206 LOG(ERROR) << "Failed creating a firewall rules. Continuing with install.";
208 // Don't abort installation if the firewall rule couldn't be added.
209 return true;
212 // Adds work items to |list| to create firewall rules.
213 void AddFirewallRulesWorkItems(const InstallerState& installer_state,
214 BrowserDistribution* dist,
215 bool is_new_install,
216 WorkItemList* list) {
217 list->AddCallbackWorkItem(
218 base::Bind(&AddFirewallRulesCallback,
219 installer_state.system_install(),
220 dist,
221 installer_state.target_path().Append(kChromeExe),
222 is_new_install));
225 void AddProductSpecificWorkItems(const InstallationState& original_state,
226 const InstallerState& installer_state,
227 const base::FilePath& setup_path,
228 const Version& new_version,
229 bool is_new_install,
230 bool add_language_identifier,
231 WorkItemList* list) {
232 const Products& products = installer_state.products();
233 for (Products::const_iterator it = products.begin(); it < products.end();
234 ++it) {
235 const Product& p = **it;
236 if (p.is_chrome()) {
237 AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p,
238 list);
239 AddFirewallRulesWorkItems(
240 installer_state, p.distribution(), is_new_install, list);
242 #if defined(GOOGLE_CHROME_BUILD)
243 if (!InstallUtil::IsChromeSxSProcess()) {
244 // Add items to set up the App Launcher's version key if Google Chrome
245 // is being installed or updated.
246 AddAppLauncherVersionKeyWorkItems(installer_state.root_key(),
247 new_version, add_language_identifier, list);
249 #endif // GOOGLE_CHROME_BUILD
251 if (p.is_chrome_binaries())
252 AddQuickEnableChromeFrameWorkItems(installer_state, list);
256 // This is called when an MSI installation is run. It may be that a user is
257 // attempting to install the MSI on top of a non-MSI managed installation.
258 // If so, try and remove any existing uninstallation shortcuts, as we want the
259 // uninstall to be managed entirely by the MSI machinery (accessible via the
260 // Add/Remove programs dialog).
261 void AddDeleteUninstallShortcutsForMSIWorkItems(
262 const InstallerState& installer_state,
263 const Product& product,
264 const base::FilePath& temp_path,
265 WorkItemList* work_item_list) {
266 DCHECK(installer_state.is_msi())
267 << "This must only be called for MSI installations!";
269 // First attempt to delete the old installation's ARP dialog entry.
270 HKEY reg_root = installer_state.root_key();
271 base::string16 uninstall_reg(product.distribution()->GetUninstallRegPath());
273 WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
274 reg_root, uninstall_reg, KEY_WOW64_32KEY);
275 delete_reg_key->set_ignore_failure(true);
277 // Then attempt to delete the old installation's start menu shortcut.
278 base::FilePath uninstall_link;
279 if (installer_state.system_install()) {
280 PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
281 } else {
282 PathService::Get(base::DIR_START_MENU, &uninstall_link);
285 if (uninstall_link.empty()) {
286 LOG(ERROR) << "Failed to get location for shortcut.";
287 } else {
288 uninstall_link = uninstall_link.Append(
289 product.distribution()->GetStartMenuShortcutSubfolder(
290 BrowserDistribution::SUBFOLDER_CHROME));
291 uninstall_link = uninstall_link.Append(
292 product.distribution()->GetUninstallLinkName() + installer::kLnkExt);
293 VLOG(1) << "Deleting old uninstall shortcut (if present): "
294 << uninstall_link.value();
295 WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
296 uninstall_link, temp_path);
297 delete_link->set_ignore_failure(true);
298 delete_link->set_log_message(
299 "Failed to delete old uninstall shortcut.");
303 // Adds Chrome specific install work items to |install_list|.
304 // |current_version| can be NULL to indicate no Chrome is currently installed.
305 void AddChromeWorkItems(const InstallationState& original_state,
306 const InstallerState& installer_state,
307 const base::FilePath& setup_path,
308 const base::FilePath& archive_path,
309 const base::FilePath& src_path,
310 const base::FilePath& temp_path,
311 const Version* current_version,
312 const Version& new_version,
313 WorkItemList* install_list) {
314 const base::FilePath& target_path = installer_state.target_path();
316 if (current_version) {
317 // Delete the archive from an existing install to save some disk space. We
318 // make this an unconditional work item since there's no need to roll this
319 // back; if installation fails we'll be moved to the "-full" channel anyway.
320 base::FilePath old_installer_dir(
321 installer_state.GetInstallerDirectory(*current_version));
322 base::FilePath old_archive(
323 old_installer_dir.Append(installer::kChromeArchive));
324 // Don't delete the archive that we are actually installing from.
325 if (archive_path != old_archive) {
326 install_list->AddDeleteTreeWorkItem(old_archive, temp_path)->
327 set_ignore_failure(true);
331 // Delete any new_chrome.exe if present (we will end up creating a new one
332 // if required) and then copy chrome.exe
333 base::FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe));
335 install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path);
337 // TODO(grt): Remove this check in M35.
338 if (installer_state.IsChromeFrameRunning(original_state)) {
339 VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe";
340 install_list->AddCopyTreeWorkItem(
341 src_path.Append(installer::kChromeExe).value(),
342 new_chrome_exe.value(),
343 temp_path.value(),
344 WorkItem::ALWAYS);
345 } else {
346 install_list->AddCopyTreeWorkItem(
347 src_path.Append(installer::kChromeExe).value(),
348 target_path.Append(installer::kChromeExe).value(),
349 temp_path.value(),
350 WorkItem::NEW_NAME_IF_IN_USE,
351 new_chrome_exe.value());
354 // Extra executable for 64 bit systems.
355 // NOTE: We check for "not disabled" so that if the API call fails, we play it
356 // safe and copy the executable anyway.
357 // NOTE: the file wow_helper.exe is only needed for Vista and below.
358 if (base::win::OSInfo::GetInstance()->wow64_status() !=
359 base::win::OSInfo::WOW64_DISABLED &&
360 base::win::GetVersion() <= base::win::VERSION_VISTA) {
361 install_list->AddMoveTreeWorkItem(
362 src_path.Append(installer::kWowHelperExe).value(),
363 target_path.Append(installer::kWowHelperExe).value(),
364 temp_path.value(),
365 WorkItem::ALWAYS_MOVE);
368 // Install kVisualElementsManifest if it is present in |src_path|. No need to
369 // make this a conditional work item as if the file is not there now, it will
370 // never be.
371 if (base::PathExists(
372 src_path.Append(installer::kVisualElementsManifest))) {
373 install_list->AddMoveTreeWorkItem(
374 src_path.Append(installer::kVisualElementsManifest).value(),
375 target_path.Append(installer::kVisualElementsManifest).value(),
376 temp_path.value(),
377 WorkItem::ALWAYS_MOVE);
378 } else {
379 // We do not want to have an old VisualElementsManifest pointing to an old
380 // version directory. Delete it as there wasn't a new one to replace it.
381 install_list->AddDeleteTreeWorkItem(
382 target_path.Append(installer::kVisualElementsManifest),
383 temp_path);
386 // In the past, we copied rather than moved for system level installs so that
387 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path|
388 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
389 // otherwise), there is no need to do this.
390 // Note that we pass true for check_duplicates to avoid failing on in-use
391 // repair runs if the current_version is the same as the new_version.
392 bool check_for_duplicates = (current_version &&
393 current_version->Equals(new_version));
394 install_list->AddMoveTreeWorkItem(
395 src_path.AppendASCII(new_version.GetString()).value(),
396 target_path.AppendASCII(new_version.GetString()).value(),
397 temp_path.value(),
398 check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
399 WorkItem::ALWAYS_MOVE);
401 // Delete any old_chrome.exe if present (ignore failure if it's in use).
402 install_list->AddDeleteTreeWorkItem(
403 target_path.Append(installer::kChromeOldExe), temp_path)->
404 set_ignore_failure(true);
407 // Probes COM machinery to get an instance of delegate_execute.exe's
408 // CommandExecuteImpl class. This is required so that COM purges its cache of
409 // the path to the binary, which changes on updates. This callback
410 // unconditionally returns true since an install should not be aborted if the
411 // probe fails.
412 bool ProbeCommandExecuteCallback(const base::string16& command_execute_id,
413 const CallbackWorkItem& work_item) {
414 // Noop on rollback.
415 if (work_item.IsRollback())
416 return true;
418 CLSID class_id = {};
420 HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
421 if (FAILED(hr)) {
422 LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
423 "CLSID; hr=0x" << std::hex << hr;
424 } else {
425 base::win::ScopedComPtr<IUnknown> command_execute_impl;
426 hr = command_execute_impl.CreateInstance(class_id, NULL,
427 CLSCTX_LOCAL_SERVER);
428 if (hr != REGDB_E_CLASSNOTREG) {
429 LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x"
430 << std::hex << hr;
434 return true;
437 void AddUninstallDelegateExecuteWorkItems(
438 HKEY root,
439 const base::string16& delegate_execute_path,
440 WorkItemList* list) {
441 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
442 << root;
443 // Delete both 64 and 32 keys to handle 32->64 or 64->32 migration.
444 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_32KEY);
446 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_64KEY);
448 // In the past, the ICommandExecuteImpl interface and a TypeLib were both
449 // registered. Remove these since this operation may be updating a machine
450 // that had the old registrations.
451 list->AddDeleteRegKeyWorkItem(root,
452 L"Software\\Classes\\Interface\\"
453 L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}",
454 KEY_WOW64_32KEY);
455 list->AddDeleteRegKeyWorkItem(root,
456 L"Software\\Classes\\TypeLib\\"
457 #if defined(GOOGLE_CHROME_BUILD)
458 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}",
459 #else
460 L"{7779FB70-B399-454A-AA1A-BAA850032B10}",
461 #endif
462 KEY_WOW64_32KEY);
465 // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
466 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by
467 // mistake (with the same GUID as Chrome). The fix stopped registering the bad
468 // value, but didn't delete it. This is a problem for users who had installed
469 // Canary before 20.0.1106.0 and now have a system-level Chrome, as the
470 // left-behind Canary registrations in HKCU mask the HKLM registrations for the
471 // same GUID. Cleanup those registrations if they still exist and belong to this
472 // Canary (i.e., the registered delegate_execute's path is under |target_path|).
473 void CleanupBadCanaryDelegateExecuteRegistration(
474 const base::FilePath& target_path,
475 WorkItemList* list) {
476 base::string16 google_chrome_delegate_execute_path(
477 L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
478 base::string16 google_chrome_local_server_32(
479 google_chrome_delegate_execute_path + L"\\LocalServer32");
481 RegKey local_server_32_key;
482 base::string16 registered_server;
483 if (local_server_32_key.Open(HKEY_CURRENT_USER,
484 google_chrome_local_server_32.c_str(),
485 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
486 local_server_32_key.ReadValue(L"ServerExecutable",
487 &registered_server) == ERROR_SUCCESS &&
488 target_path.IsParent(base::FilePath(registered_server))) {
489 scoped_ptr<WorkItemList> no_rollback_list(
490 WorkItem::CreateNoRollbackWorkItemList());
491 AddUninstallDelegateExecuteWorkItems(
492 HKEY_CURRENT_USER, google_chrome_delegate_execute_path,
493 no_rollback_list.get());
494 list->AddWorkItem(no_rollback_list.release());
495 VLOG(1) << "Added deletion items for bad Canary registrations.";
499 } // namespace
501 // This method adds work items to create (or update) Chrome uninstall entry in
502 // either the Control Panel->Add/Remove Programs list or in the Omaha client
503 // state key if running under an MSI installer.
504 void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
505 const base::FilePath& setup_path,
506 const Version& new_version,
507 const Product& product,
508 WorkItemList* install_list) {
509 HKEY reg_root = installer_state.root_key();
510 BrowserDistribution* browser_dist = product.distribution();
511 DCHECK(browser_dist);
513 // When we are installed via an MSI, we need to store our uninstall strings
514 // in the Google Update client state key. We do this even for non-MSI
515 // managed installs to avoid breaking the edge case whereby an MSI-managed
516 // install is updated by a non-msi installer (which would confuse the MSI
517 // machinery if these strings were not also updated). The UninstallString
518 // value placed in the client state key is also used by the mini_installer to
519 // locate the setup.exe instance used for binary patching.
520 // Do not quote the command line for the MSI invocation.
521 base::FilePath install_path(installer_state.target_path());
522 base::FilePath installer_path(
523 installer_state.GetInstallerDirectory(new_version));
524 installer_path = installer_path.Append(setup_path.BaseName());
526 base::CommandLine uninstall_arguments(base::CommandLine::NO_PROGRAM);
527 AppendUninstallCommandLineFlags(installer_state, product,
528 &uninstall_arguments);
530 base::string16 update_state_key(browser_dist->GetStateKey());
531 install_list->AddCreateRegKeyWorkItem(
532 reg_root, update_state_key, KEY_WOW64_32KEY);
533 install_list->AddSetRegValueWorkItem(reg_root,
534 update_state_key,
535 KEY_WOW64_32KEY,
536 installer::kUninstallStringField,
537 installer_path.value(),
538 true);
539 install_list->AddSetRegValueWorkItem(
540 reg_root,
541 update_state_key,
542 KEY_WOW64_32KEY,
543 installer::kUninstallArgumentsField,
544 uninstall_arguments.GetCommandLineString(),
545 true);
547 // MSI installations will manage their own uninstall shortcuts.
548 if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
549 // We need to quote the command line for the Add/Remove Programs dialog.
550 base::CommandLine quoted_uninstall_cmd(installer_path);
551 DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"');
552 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
554 base::string16 uninstall_reg = browser_dist->GetUninstallRegPath();
555 install_list->AddCreateRegKeyWorkItem(
556 reg_root, uninstall_reg, KEY_WOW64_32KEY);
557 install_list->AddSetRegValueWorkItem(reg_root,
558 uninstall_reg,
559 KEY_WOW64_32KEY,
560 installer::kUninstallDisplayNameField,
561 browser_dist->GetDisplayName(),
562 true);
563 install_list->AddSetRegValueWorkItem(
564 reg_root,
565 uninstall_reg,
566 KEY_WOW64_32KEY,
567 installer::kUninstallStringField,
568 quoted_uninstall_cmd.GetCommandLineString(),
569 true);
570 install_list->AddSetRegValueWorkItem(reg_root,
571 uninstall_reg,
572 KEY_WOW64_32KEY,
573 L"InstallLocation",
574 install_path.value(),
575 true);
577 BrowserDistribution* dist = product.distribution();
578 base::string16 chrome_icon = ShellUtil::FormatIconLocation(
579 install_path.Append(dist->GetIconFilename()),
580 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
581 install_list->AddSetRegValueWorkItem(reg_root,
582 uninstall_reg,
583 KEY_WOW64_32KEY,
584 L"DisplayIcon",
585 chrome_icon,
586 true);
587 install_list->AddSetRegValueWorkItem(reg_root,
588 uninstall_reg,
589 KEY_WOW64_32KEY,
590 L"NoModify",
591 static_cast<DWORD>(1),
592 true);
593 install_list->AddSetRegValueWorkItem(reg_root,
594 uninstall_reg,
595 KEY_WOW64_32KEY,
596 L"NoRepair",
597 static_cast<DWORD>(1),
598 true);
600 install_list->AddSetRegValueWorkItem(reg_root,
601 uninstall_reg,
602 KEY_WOW64_32KEY,
603 L"Publisher",
604 browser_dist->GetPublisherName(),
605 true);
606 install_list->AddSetRegValueWorkItem(reg_root,
607 uninstall_reg,
608 KEY_WOW64_32KEY,
609 L"Version",
610 ASCIIToUTF16(new_version.GetString()),
611 true);
612 install_list->AddSetRegValueWorkItem(reg_root,
613 uninstall_reg,
614 KEY_WOW64_32KEY,
615 L"DisplayVersion",
616 ASCIIToUTF16(new_version.GetString()),
617 true);
618 // TODO(wfh): Ensure that this value is preserved in the 64-bit hive when
619 // 64-bit installs place the uninstall information into the 64-bit registry.
620 install_list->AddSetRegValueWorkItem(reg_root,
621 uninstall_reg,
622 KEY_WOW64_32KEY,
623 L"InstallDate",
624 InstallUtil::GetCurrentDate(),
625 false);
627 const std::vector<uint32_t>& version_components = new_version.components();
628 if (version_components.size() == 4) {
629 // Our version should be in major.minor.build.rev.
630 install_list->AddSetRegValueWorkItem(
631 reg_root,
632 uninstall_reg,
633 KEY_WOW64_32KEY,
634 L"VersionMajor",
635 static_cast<DWORD>(version_components[2]),
636 true);
637 install_list->AddSetRegValueWorkItem(
638 reg_root,
639 uninstall_reg,
640 KEY_WOW64_32KEY,
641 L"VersionMinor",
642 static_cast<DWORD>(version_components[3]),
643 true);
648 // Create Version key for a product (if not already present) and sets the new
649 // product version as the last step.
650 void AddVersionKeyWorkItems(HKEY root,
651 const base::string16& version_key,
652 const base::string16& product_name,
653 const Version& new_version,
654 bool add_language_identifier,
655 WorkItemList* list) {
656 list->AddCreateRegKeyWorkItem(root, version_key, KEY_WOW64_32KEY);
658 list->AddSetRegValueWorkItem(root,
659 version_key,
660 KEY_WOW64_32KEY,
661 google_update::kRegNameField,
662 product_name,
663 true); // overwrite name also
664 list->AddSetRegValueWorkItem(root,
665 version_key,
666 KEY_WOW64_32KEY,
667 google_update::kRegOopcrashesField,
668 static_cast<DWORD>(1),
669 false); // set during first install
670 if (add_language_identifier) {
671 // Write the language identifier of the current translation. Omaha's set of
672 // languages is a superset of Chrome's set of translations with this one
673 // exception: what Chrome calls "en-us", Omaha calls "en". sigh.
674 base::string16 language(GetCurrentTranslation());
675 if (base::LowerCaseEqualsASCII(language, "en-us"))
676 language.resize(2);
677 list->AddSetRegValueWorkItem(root,
678 version_key,
679 KEY_WOW64_32KEY,
680 google_update::kRegLangField,
681 language,
682 false); // do not overwrite language
684 list->AddSetRegValueWorkItem(root,
685 version_key,
686 KEY_WOW64_32KEY,
687 google_update::kRegVersionField,
688 ASCIIToUTF16(new_version.GetString()),
689 true); // overwrite version
692 // Mirror oeminstall the first time anything is installed multi. There is no
693 // need to update the value on future install/update runs since this value never
694 // changes. Note that the value is removed by Google Update after EULA
695 // acceptance is processed.
696 void AddOemInstallWorkItems(const InstallationState& original_state,
697 const InstallerState& installer_state,
698 WorkItemList* install_list) {
699 DCHECK(installer_state.is_multi_install());
700 const bool system_install = installer_state.system_install();
701 if (!original_state.GetProductState(system_install,
702 BrowserDistribution::CHROME_BINARIES)) {
703 const HKEY root_key = installer_state.root_key();
704 base::string16 multi_key(
705 installer_state.multi_package_binaries_distribution()->GetStateKey());
707 // Copy the value from Chrome unless Chrome isn't installed or being
708 // installed.
709 BrowserDistribution::Type source_type;
710 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) {
711 source_type = BrowserDistribution::CHROME_BROWSER;
712 } else if (!installer_state.products().empty()) {
713 // Pick a product, any product.
714 source_type = installer_state.products()[0]->distribution()->GetType();
715 } else {
716 // Nothing is being installed? Entirely unexpected, so do no harm.
717 LOG(ERROR) << "No products found in AddOemInstallWorkItems";
718 return;
720 const ProductState* source_product =
721 original_state.GetNonVersionedProductState(system_install, source_type);
723 base::string16 oem_install;
724 if (source_product->GetOemInstall(&oem_install)) {
725 VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from "
726 << BrowserDistribution::GetSpecificDistribution(source_type)->
727 GetDisplayName();
728 install_list->AddCreateRegKeyWorkItem(
729 root_key, multi_key, KEY_WOW64_32KEY);
730 // Always overwrite an old value.
731 install_list->AddSetRegValueWorkItem(root_key,
732 multi_key,
733 KEY_WOW64_32KEY,
734 google_update::kRegOemInstallField,
735 oem_install,
736 true);
737 } else {
738 // Clear any old value.
739 install_list->AddDeleteRegValueWorkItem(
740 root_key,
741 multi_key,
742 KEY_WOW64_32KEY,
743 google_update::kRegOemInstallField);
748 // Mirror eulaaccepted the first time anything is installed multi. There is no
749 // need to update the value on future install/update runs since
750 // GoogleUpdateSettings::SetEULAConsent will modify the value for both the
751 // relevant product and for the binaries.
752 void AddEulaAcceptedWorkItems(const InstallationState& original_state,
753 const InstallerState& installer_state,
754 WorkItemList* install_list) {
755 DCHECK(installer_state.is_multi_install());
756 const bool system_install = installer_state.system_install();
757 if (!original_state.GetProductState(system_install,
758 BrowserDistribution::CHROME_BINARIES)) {
759 const HKEY root_key = installer_state.root_key();
760 base::string16 multi_key(
761 installer_state.multi_package_binaries_distribution()->GetStateKey());
763 // Copy the value from the product with the greatest value.
764 bool have_eula_accepted = false;
765 BrowserDistribution::Type product_type = BrowserDistribution::NUM_TYPES;
766 DWORD eula_accepted = 0;
767 const Products& products = installer_state.products();
768 for (Products::const_iterator it = products.begin(); it < products.end();
769 ++it) {
770 const Product& product = **it;
771 if (product.is_chrome_binaries())
772 continue;
773 DWORD dword_value = 0;
774 BrowserDistribution::Type this_type = product.distribution()->GetType();
775 const ProductState* product_state =
776 original_state.GetNonVersionedProductState(
777 system_install, this_type);
778 if (product_state->GetEulaAccepted(&dword_value) &&
779 (!have_eula_accepted || dword_value > eula_accepted)) {
780 have_eula_accepted = true;
781 eula_accepted = dword_value;
782 product_type = this_type;
786 if (have_eula_accepted) {
787 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from "
788 << BrowserDistribution::GetSpecificDistribution(product_type)->
789 GetDisplayName();
790 install_list->AddCreateRegKeyWorkItem(
791 root_key, multi_key, KEY_WOW64_32KEY);
792 install_list->AddSetRegValueWorkItem(root_key,
793 multi_key,
794 KEY_WOW64_32KEY,
795 google_update::kRegEULAAceptedField,
796 eula_accepted,
797 true);
798 } else {
799 // Clear any old value.
800 install_list->AddDeleteRegValueWorkItem(
801 root_key,
802 multi_key,
803 KEY_WOW64_32KEY,
804 google_update::kRegEULAAceptedField);
809 // Adds work items that make registry adjustments for Google Update.
810 void AddGoogleUpdateWorkItems(const InstallationState& original_state,
811 const InstallerState& installer_state,
812 WorkItemList* install_list) {
813 // Is a multi-install product being installed or over-installed?
814 if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
815 installer_state.operation() != InstallerState::MULTI_UPDATE) {
816 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
817 return;
820 const bool system_install = installer_state.system_install();
821 const HKEY root_key = installer_state.root_key();
822 base::string16 multi_key(
823 installer_state.multi_package_binaries_distribution()->GetStateKey());
825 // For system-level installs, make sure the ClientStateMedium key for the
826 // binaries exists.
827 if (system_install) {
828 install_list->AddCreateRegKeyWorkItem(
829 root_key,
830 installer_state.multi_package_binaries_distribution()
831 ->GetStateMediumKey()
832 .c_str(),
833 KEY_WOW64_32KEY);
836 // Creating the ClientState key for binaries, if we're migrating to multi then
837 // copy over Chrome's brand code if it has one.
838 if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
839 const ProductState* chrome_product_state =
840 original_state.GetNonVersionedProductState(
841 system_install, BrowserDistribution::CHROME_BROWSER);
843 const base::string16& brand(chrome_product_state->brand());
844 if (!brand.empty()) {
845 install_list->AddCreateRegKeyWorkItem(
846 root_key, multi_key, KEY_WOW64_32KEY);
847 // Write Chrome's brand code to the multi key. Never overwrite the value
848 // if one is already present (although this shouldn't happen).
849 install_list->AddSetRegValueWorkItem(root_key,
850 multi_key,
851 KEY_WOW64_32KEY,
852 google_update::kRegBrandField,
853 brand,
854 false);
858 AddOemInstallWorkItems(original_state, installer_state, install_list);
859 AddEulaAcceptedWorkItems(original_state, installer_state, install_list);
860 AddUsageStatsWorkItems(original_state, installer_state, install_list);
862 // TODO(grt): check for other keys/values we should put in the package's
863 // ClientState and/or Clients key.
866 void AddUsageStatsWorkItems(const InstallationState& original_state,
867 const InstallerState& installer_state,
868 WorkItemList* install_list) {
869 DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
870 installer_state.operation() == InstallerState::MULTI_UPDATE);
872 HKEY root_key = installer_state.root_key();
873 bool value_found = false;
874 DWORD usagestats = 0;
875 const Products& products = installer_state.products();
877 // Search for an existing usagestats value for any product.
878 for (Products::const_iterator scan = products.begin(), end = products.end();
879 !value_found && scan != end; ++scan) {
880 if ((*scan)->is_chrome_binaries())
881 continue;
882 BrowserDistribution* dist = (*scan)->distribution();
883 const ProductState* product_state =
884 original_state.GetNonVersionedProductState(
885 installer_state.system_install(), dist->GetType());
886 value_found = product_state->GetUsageStats(&usagestats);
889 // If a value was found, write it in the appropriate location for the
890 // binaries and remove all values from the products.
891 if (value_found) {
892 base::string16 state_key(
893 installer_state.multi_package_binaries_distribution()->GetStateKey());
894 install_list->AddCreateRegKeyWorkItem(root_key, state_key, KEY_WOW64_32KEY);
895 // Overwrite any existing value so that overinstalls (where Omaha writes a
896 // new value into a product's state key) pick up the correct value.
897 install_list->AddSetRegValueWorkItem(root_key,
898 state_key,
899 KEY_WOW64_32KEY,
900 google_update::kRegUsageStatsField,
901 usagestats,
902 true);
904 for (Products::const_iterator scan = products.begin(), end = products.end();
905 scan != end; ++scan) {
906 if ((*scan)->is_chrome_binaries())
907 continue;
908 BrowserDistribution* dist = (*scan)->distribution();
909 if (installer_state.system_install()) {
910 install_list->AddDeleteRegValueWorkItem(
911 root_key,
912 dist->GetStateMediumKey(),
913 KEY_WOW64_32KEY,
914 google_update::kRegUsageStatsField);
915 // Previous versions of Chrome also wrote a value in HKCU even for
916 // system-level installs, so clean that up.
917 install_list->AddDeleteRegValueWorkItem(
918 HKEY_CURRENT_USER,
919 dist->GetStateKey(),
920 KEY_WOW64_32KEY,
921 google_update::kRegUsageStatsField);
923 install_list->AddDeleteRegValueWorkItem(
924 root_key,
925 dist->GetStateKey(),
926 KEY_WOW64_32KEY,
927 google_update::kRegUsageStatsField);
932 // Migrates the usagestats value from the binaries to Chrome when migrating
933 // multi-install Chrome to single-install.
934 void AddMigrateUsageStatesWorkItems(const InstallationState& original_state,
935 const InstallerState& installer_state,
936 WorkItemList* install_list) {
937 // Ensure that a non-multi install or update is being processed (i.e.,
938 // no "--multi-install" on the command line).
939 if (installer_state.is_multi_install())
940 return;
942 // This operation doesn't apply to SxS Chrome.
943 if (InstallUtil::IsChromeSxSProcess())
944 return;
946 // Ensure that Chrome is the product being installed or updated (there are no
947 // other products, so it is especially unexpected for this to fail).
948 const Product* chrome_product =
949 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
950 if (!chrome_product) {
951 LOG(DFATAL) << "Not operating on Chrome while migrating to single-install.";
952 return;
955 const ProductState* chrome_state = original_state.GetProductState(
956 installer_state.system_install(),
957 BrowserDistribution::CHROME_BROWSER);
958 // Bail out if there is not an existing multi-install Chrome that is being
959 // updated.
960 if (!chrome_state || !chrome_state->is_multi_install()) {
961 VLOG(1) << "No multi-install Chrome found to migrate to single-install.";
962 return;
965 const ProductState* binaries_state = original_state.GetProductState(
966 installer_state.system_install(),
967 BrowserDistribution::CHROME_BINARIES);
969 // There is nothing to be done if the binaries do not have stats.
970 DWORD usagestats = 0;
971 if (!binaries_state || !binaries_state->GetUsageStats(&usagestats)) {
972 VLOG(1) << "No usagestats value found to migrate to single-install.";
973 return;
976 VLOG(1) << "Migrating usagestats value from multi-install to single-install.";
978 // Write the value that was read to Chrome's ClientState key.
979 install_list->AddSetRegValueWorkItem(
980 installer_state.root_key(),
981 chrome_product->distribution()->GetStateKey(),
982 KEY_WOW64_32KEY,
983 google_update::kRegUsageStatsField,
984 usagestats,
985 true);
988 bool AppendPostInstallTasks(const InstallerState& installer_state,
989 const base::FilePath& setup_path,
990 const Version* current_version,
991 const Version& new_version,
992 const base::FilePath& temp_path,
993 WorkItemList* post_install_task_list) {
994 DCHECK(post_install_task_list);
996 HKEY root = installer_state.root_key();
997 const Products& products = installer_state.products();
998 base::FilePath new_chrome_exe(
999 installer_state.target_path().Append(installer::kChromeNewExe));
1001 // Append work items that will only be executed if this was an update.
1002 // We update the 'opv' value with the current version that is active,
1003 // the 'cpv' value with the critical update version (if present), and the
1004 // 'cmd' value with the rename command to run.
1006 scoped_ptr<WorkItemList> in_use_update_work_items(
1007 WorkItem::CreateConditionalWorkItemList(
1008 new ConditionRunIfFileExists(new_chrome_exe)));
1009 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
1011 // |critical_version| will be valid only if this in-use update includes a
1012 // version considered critical relative to the version being updated.
1013 Version critical_version(installer_state.DetermineCriticalVersion(
1014 current_version, new_version));
1015 base::FilePath installer_path(
1016 installer_state.GetInstallerDirectory(new_version).Append(
1017 setup_path.BaseName()));
1019 base::CommandLine rename(installer_path);
1020 rename.AppendSwitch(switches::kRenameChromeExe);
1021 if (installer_state.system_install())
1022 rename.AppendSwitch(switches::kSystemLevel);
1024 if (installer_state.verbose_logging())
1025 rename.AppendSwitch(switches::kVerboseLogging);
1027 base::string16 version_key;
1028 for (size_t i = 0; i < products.size(); ++i) {
1029 BrowserDistribution* dist = products[i]->distribution();
1030 version_key = dist->GetVersionKey();
1032 if (current_version) {
1033 in_use_update_work_items->AddSetRegValueWorkItem(
1034 root,
1035 version_key,
1036 KEY_WOW64_32KEY,
1037 google_update::kRegOldVersionField,
1038 ASCIIToUTF16(current_version->GetString()),
1039 true);
1041 if (critical_version.IsValid()) {
1042 in_use_update_work_items->AddSetRegValueWorkItem(
1043 root,
1044 version_key,
1045 KEY_WOW64_32KEY,
1046 google_update::kRegCriticalVersionField,
1047 ASCIIToUTF16(critical_version.GetString()),
1048 true);
1049 } else {
1050 in_use_update_work_items->AddDeleteRegValueWorkItem(
1051 root,
1052 version_key,
1053 KEY_WOW64_32KEY,
1054 google_update::kRegCriticalVersionField);
1057 // Adding this registry entry for all products (but the binaries) is
1058 // overkill. However, as it stands, we don't have a way to know which
1059 // product will check the key and run the command, so we add it for all.
1060 // The first to run it will perform the operation and clean up the other
1061 // values.
1062 if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
1063 base::CommandLine product_rename_cmd(rename);
1064 products[i]->AppendRenameFlags(&product_rename_cmd);
1065 in_use_update_work_items->AddSetRegValueWorkItem(
1066 root,
1067 version_key,
1068 KEY_WOW64_32KEY,
1069 google_update::kRegRenameCmdField,
1070 product_rename_cmd.GetCommandLineString(),
1071 true);
1075 post_install_task_list->AddWorkItem(in_use_update_work_items.release());
1078 // Append work items that will be executed if this was NOT an in-use update.
1080 scoped_ptr<WorkItemList> regular_update_work_items(
1081 WorkItem::CreateConditionalWorkItemList(
1082 new Not(new ConditionRunIfFileExists(new_chrome_exe))));
1083 regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
1085 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1086 for (size_t i = 0; i < products.size(); ++i) {
1087 BrowserDistribution* dist = products[i]->distribution();
1088 base::string16 version_key(dist->GetVersionKey());
1089 regular_update_work_items->AddDeleteRegValueWorkItem(
1090 root,
1091 version_key,
1092 KEY_WOW64_32KEY,
1093 google_update::kRegOldVersionField);
1094 regular_update_work_items->AddDeleteRegValueWorkItem(
1095 root,
1096 version_key,
1097 KEY_WOW64_32KEY,
1098 google_update::kRegCriticalVersionField);
1099 regular_update_work_items->AddDeleteRegValueWorkItem(
1100 root,
1101 version_key,
1102 KEY_WOW64_32KEY,
1103 google_update::kRegRenameCmdField);
1106 post_install_task_list->AddWorkItem(regular_update_work_items.release());
1109 AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
1110 new_version, post_install_task_list);
1112 // If we're told that we're an MSI install, make sure to set the marker
1113 // in the client state key so that future updates do the right thing.
1114 if (installer_state.is_msi()) {
1115 for (size_t i = 0; i < products.size(); ++i) {
1116 const Product* product = products[i];
1117 AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
1118 post_install_task_list);
1120 // We want MSI installs to take over the Add/Remove Programs shortcut.
1121 // Make a best-effort attempt to delete any shortcuts left over from
1122 // previous non-MSI installations for the same type of install (system or
1123 // per user).
1124 if (product->ShouldCreateUninstallEntry()) {
1125 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
1126 temp_path,
1127 post_install_task_list);
1132 return true;
1135 void AddInstallWorkItems(const InstallationState& original_state,
1136 const InstallerState& installer_state,
1137 const base::FilePath& setup_path,
1138 const base::FilePath& archive_path,
1139 const base::FilePath& src_path,
1140 const base::FilePath& temp_path,
1141 const Version* current_version,
1142 const Version& new_version,
1143 WorkItemList* install_list) {
1144 DCHECK(install_list);
1146 const base::FilePath& target_path = installer_state.target_path();
1148 // A temp directory that work items need and the actual install directory.
1149 install_list->AddCreateDirWorkItem(temp_path);
1150 install_list->AddCreateDirWorkItem(target_path);
1152 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
1153 installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
1154 AddChromeWorkItems(original_state,
1155 installer_state,
1156 setup_path,
1157 archive_path,
1158 src_path,
1159 temp_path,
1160 current_version,
1161 new_version,
1162 install_list);
1165 #if defined(GOOGLE_CHROME_BUILD)
1166 // For Chrome, unconditionally remove the legacy app_host.exe.
1167 if (!InstallUtil::IsChromeSxSProcess())
1168 AddRemoveLegacyAppHostExeWorkItems(target_path, temp_path, install_list);
1169 #endif // GOOGLE_CHROME_BUILD
1171 // Copy installer in install directory
1172 AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
1173 new_version, install_list);
1175 const HKEY root = installer_state.root_key();
1176 // Only set "lang" for user-level installs since for system-level, the install
1177 // language may not be related to a given user's runtime language.
1178 const bool add_language_identifier = !installer_state.system_install();
1180 const Products& products = installer_state.products();
1181 for (Products::const_iterator it = products.begin(); it < products.end();
1182 ++it) {
1183 const Product& product = **it;
1185 AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
1186 product, install_list);
1188 BrowserDistribution* dist = product.distribution();
1189 AddVersionKeyWorkItems(root,
1190 dist->GetVersionKey(),
1191 dist->GetDisplayName(),
1192 new_version,
1193 add_language_identifier,
1194 install_list);
1196 AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
1197 product, install_list);
1199 AddActiveSetupWorkItems(installer_state, new_version, product,
1200 install_list);
1203 // Ensure that the Clients key for the binaries is gone for single installs
1204 // (but not for SxS Chrome).
1205 if (!installer_state.is_multi_install() &&
1206 !InstallUtil::IsChromeSxSProcess()) {
1207 BrowserDistribution* binaries_dist =
1208 BrowserDistribution::GetSpecificDistribution(
1209 BrowserDistribution::CHROME_BINARIES);
1210 install_list->AddDeleteRegKeyWorkItem(root,
1211 binaries_dist->GetVersionKey(),
1212 KEY_WOW64_32KEY);
1215 #if defined(GOOGLE_CHROME_BUILD)
1216 if (!InstallUtil::IsChromeSxSProcess())
1217 AddRemoveLegacyAppCommandsWorkItems(installer_state, install_list);
1218 #endif // GOOGLE_CHROME_BUILD
1220 // Add any remaining work items that involve special settings for
1221 // each product.
1222 AddProductSpecificWorkItems(original_state,
1223 installer_state,
1224 setup_path,
1225 new_version,
1226 current_version == NULL,
1227 add_language_identifier,
1228 install_list);
1230 // Copy over brand, usagestats, and other values.
1231 AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
1233 // Migrate usagestats back to Chrome.
1234 AddMigrateUsageStatesWorkItems(original_state, installer_state, install_list);
1236 // Append the tasks that run after the installation.
1237 AppendPostInstallTasks(installer_state,
1238 setup_path,
1239 current_version,
1240 new_version,
1241 temp_path,
1242 install_list);
1245 void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
1246 const std::vector<base::FilePath>& dll_list,
1247 bool system_level,
1248 bool do_register,
1249 bool ignore_failures,
1250 WorkItemList* work_item_list) {
1251 DCHECK(work_item_list);
1252 if (dll_list.empty()) {
1253 VLOG(1) << "No COM DLLs to register";
1254 } else {
1255 std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
1256 for (; dll_iter != dll_list.end(); ++dll_iter) {
1257 base::FilePath dll_path = dll_folder.Append(*dll_iter);
1258 WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
1259 dll_path.value(), do_register, !system_level);
1260 DCHECK(work_item);
1261 work_item->set_ignore_failure(ignore_failures);
1266 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
1267 BrowserDistribution* dist,
1268 bool set,
1269 WorkItemList* work_item_list) {
1270 DCHECK(work_item_list);
1271 DWORD msi_value = set ? 1 : 0;
1272 WorkItem* set_msi_work_item =
1273 work_item_list->AddSetRegValueWorkItem(installer_state.root_key(),
1274 dist->GetStateKey(),
1275 KEY_WOW64_32KEY,
1276 google_update::kRegMSIField,
1277 msi_value,
1278 true);
1279 DCHECK(set_msi_work_item);
1280 set_msi_work_item->set_ignore_failure(true);
1281 set_msi_work_item->set_log_message("Could not write MSI marker!");
1284 void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
1285 const base::FilePath& target_path,
1286 const Version& new_version,
1287 const Product& product,
1288 WorkItemList* list) {
1289 base::string16 handler_class_uuid;
1290 BrowserDistribution* dist = product.distribution();
1291 if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) {
1292 if (InstallUtil::IsChromeSxSProcess()) {
1293 CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
1294 } else {
1295 VLOG(1) << "No DelegateExecute verb handler processing to do for "
1296 << dist->GetDisplayName();
1298 return;
1301 HKEY root = installer_state.root_key();
1302 base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
1303 delegate_execute_path.append(handler_class_uuid);
1305 // Unconditionally remove registration regardless of whether or not it is
1306 // needed since builds after r132190 included it when it wasn't strictly
1307 // necessary. Do this removal before adding in the new key to ensure that
1308 // the COM probe/flush below does its job.
1309 AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
1311 // Add work items to register the handler iff it is present.
1312 // See also shell_util.cc's GetProgIdEntries.
1313 if (installer_state.operation() != InstallerState::UNINSTALL) {
1314 VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1316 // Force COM to flush its cache containing the path to the old handler.
1317 list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
1318 handler_class_uuid));
1320 // The path to the exe (in the version directory).
1321 base::FilePath delegate_execute(target_path);
1322 if (new_version.IsValid())
1323 delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
1324 delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
1326 // Command-line featuring the quoted path to the exe.
1327 base::string16 command(1, L'"');
1328 command.append(delegate_execute.value()).append(1, L'"');
1330 // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1331 list->AddCreateRegKeyWorkItem(
1332 root, delegate_execute_path, WorkItem::kWow64Default);
1333 list->AddSetRegValueWorkItem(root,
1334 delegate_execute_path,
1335 WorkItem::kWow64Default,
1336 L"",
1337 L"CommandExecuteImpl Class",
1338 true);
1339 base::string16 subkey(delegate_execute_path);
1340 subkey.append(L"\\LocalServer32");
1341 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1342 list->AddSetRegValueWorkItem(
1343 root, subkey, WorkItem::kWow64Default, L"", command, true);
1344 list->AddSetRegValueWorkItem(root,
1345 subkey,
1346 WorkItem::kWow64Default,
1347 L"ServerExecutable",
1348 delegate_execute.value(),
1349 true);
1351 subkey.assign(delegate_execute_path).append(L"\\Programmable");
1352 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1356 void AddActiveSetupWorkItems(const InstallerState& installer_state,
1357 const Version& new_version,
1358 const Product& product,
1359 WorkItemList* list) {
1360 DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
1361 BrowserDistribution* dist = product.distribution();
1363 if (!product.is_chrome() || !installer_state.system_install()) {
1364 const char* install_level =
1365 installer_state.system_install() ? "system" : "user";
1366 VLOG(1) << "No Active Setup processing to do for " << install_level
1367 << "-level " << dist->GetDisplayName();
1368 return;
1370 DCHECK(installer_state.RequiresActiveSetup());
1372 const HKEY root = HKEY_LOCAL_MACHINE;
1373 const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist));
1375 VLOG(1) << "Adding registration items for Active Setup.";
1376 list->AddCreateRegKeyWorkItem(
1377 root, active_setup_path, WorkItem::kWow64Default);
1378 list->AddSetRegValueWorkItem(root,
1379 active_setup_path,
1380 WorkItem::kWow64Default,
1381 L"",
1382 dist->GetDisplayName(),
1383 true);
1385 base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
1386 new_version).Append(kActiveSetupExe));
1387 base::CommandLine cmd(active_setup_exe);
1388 cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
1389 cmd.AppendSwitch(installer::switches::kVerboseLogging);
1390 cmd.AppendSwitch(installer::switches::kSystemLevel);
1391 product.AppendProductFlags(&cmd);
1392 list->AddSetRegValueWorkItem(root,
1393 active_setup_path,
1394 WorkItem::kWow64Default,
1395 L"StubPath",
1396 cmd.GetCommandLineString(),
1397 true);
1399 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1400 // resource.
1401 list->AddSetRegValueWorkItem(root,
1402 active_setup_path,
1403 WorkItem::kWow64Default,
1404 L"Localized Name",
1405 dist->GetDisplayName(),
1406 true);
1408 list->AddSetRegValueWorkItem(root,
1409 active_setup_path,
1410 WorkItem::kWow64Default,
1411 L"IsInstalled",
1412 static_cast<DWORD>(1U),
1413 true);
1415 list->AddWorkItem(new UpdateActiveSetupVersionWorkItem(
1416 active_setup_path, UpdateActiveSetupVersionWorkItem::UPDATE));
1419 void AddDeleteOldIELowRightsPolicyWorkItems(
1420 const InstallerState& installer_state,
1421 WorkItemList* install_list) {
1422 DCHECK(install_list);
1424 base::string16 key_path;
1425 GetOldIELowRightsElevationPolicyKeyPath(&key_path);
1426 install_list->AddDeleteRegKeyWorkItem(
1427 installer_state.root_key(), key_path, WorkItem::kWow64Default);
1430 void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
1431 const Product& product,
1432 base::CommandLine* uninstall_cmd) {
1433 DCHECK(uninstall_cmd);
1435 uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
1437 // Append the product-specific uninstall flags.
1438 product.AppendProductFlags(uninstall_cmd);
1439 if (installer_state.is_msi())
1440 uninstall_cmd->AppendSwitch(installer::switches::kMsi);
1441 if (installer_state.system_install())
1442 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
1443 if (installer_state.verbose_logging())
1444 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
1447 void RefreshElevationPolicy() {
1448 const wchar_t kIEFrameDll[] = L"ieframe.dll";
1449 const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
1451 HMODULE ieframe = LoadLibrary(kIEFrameDll);
1452 if (ieframe) {
1453 typedef HRESULT (__stdcall *IERefreshPolicy)();
1454 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
1455 GetProcAddress(ieframe, kIERefreshPolicy));
1457 if (ie_refresh_policy) {
1458 ie_refresh_policy();
1459 } else {
1460 VLOG(1) << kIERefreshPolicy << " not supported.";
1463 FreeLibrary(ieframe);
1464 } else {
1465 VLOG(1) << "Cannot load " << kIEFrameDll;
1469 void AddOsUpgradeWorkItems(const InstallerState& installer_state,
1470 const base::FilePath& setup_path,
1471 const Version& new_version,
1472 const Product& product,
1473 WorkItemList* install_list) {
1474 const HKEY root_key = installer_state.root_key();
1475 base::string16 cmd_key(
1476 GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
1478 if (installer_state.operation() == InstallerState::UNINSTALL) {
1479 install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
1480 ->set_log_message("Removing OS upgrade command");
1481 } else {
1482 // Register with Google Update to have setup.exe --on-os-upgrade called on
1483 // OS upgrade.
1484 base::CommandLine cmd_line(
1485 installer_state.GetInstallerDirectory(new_version)
1486 .Append(setup_path.BaseName()));
1487 // Add the main option to indicate OS upgrade flow.
1488 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
1489 // Add product-specific options.
1490 product.AppendProductFlags(&cmd_line);
1491 if (installer_state.system_install())
1492 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1493 // Log everything for now.
1494 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1496 AppCommand cmd(cmd_line.GetCommandLineString());
1497 cmd.set_is_auto_run_on_os_upgrade(true);
1498 cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
1502 void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
1503 WorkItemList* work_item_list) {
1504 DCHECK(work_item_list);
1506 base::string16 cmd_key(
1507 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1508 BrowserDistribution::CHROME_BINARIES),
1509 kCmdQuickEnableCf));
1511 // Unconditionally remove the legacy Quick Enable command from the binaries.
1512 // Do this even if multi-install Chrome isn't installed to ensure that it is
1513 // not left behind in any case.
1514 work_item_list->AddDeleteRegKeyWorkItem(
1515 installer_state.root_key(), cmd_key, KEY_WOW64_32KEY)
1516 ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf) +
1517 " command");
1520 } // namespace installer