Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / installer / setup / install_worker.cc
blob275b54dd9ad57fb4014069a0d26a39fe716b62ba
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/install.h"
32 #include "chrome/installer/setup/setup_constants.h"
33 #include "chrome/installer/setup/setup_util.h"
34 #include "chrome/installer/util/app_registration_data.h"
35 #include "chrome/installer/util/browser_distribution.h"
36 #include "chrome/installer/util/callback_work_item.h"
37 #include "chrome/installer/util/conditional_work_item_list.h"
38 #include "chrome/installer/util/create_reg_key_work_item.h"
39 #include "chrome/installer/util/firewall_manager_win.h"
40 #include "chrome/installer/util/google_update_constants.h"
41 #include "chrome/installer/util/helper.h"
42 #include "chrome/installer/util/install_util.h"
43 #include "chrome/installer/util/installation_state.h"
44 #include "chrome/installer/util/installer_state.h"
45 #include "chrome/installer/util/l10n_string_util.h"
46 #include "chrome/installer/util/product.h"
47 #include "chrome/installer/util/set_reg_value_work_item.h"
48 #include "chrome/installer/util/shell_util.h"
49 #include "chrome/installer/util/updating_app_registration_data.h"
50 #include "chrome/installer/util/util_constants.h"
51 #include "chrome/installer/util/work_item_list.h"
53 using base::ASCIIToWide;
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"24,0,0,0";
66 // Although the UUID of the ChromeFrame class is used for the "current" value,
67 // this is done only as a convenience; there is no need for the GUID of the Low
68 // Rights policies to match the ChromeFrame class's GUID. Hence, it is safe to
69 // use this completely unrelated GUID for the "old" policies.
70 const wchar_t kIELowRightsPolicyOldGuid[] =
71 L"{6C288DD7-76FB-4721-B628-56FAC252E199}";
73 const wchar_t kElevationPolicyKeyPath[] =
74 L"SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\";
76 // The legacy command ids for installing an application or extension. These are
77 // only here so they can be removed from the registry.
78 const wchar_t kLegacyCmdInstallApp[] = L"install-application";
79 const wchar_t kLegacyCmdInstallExtension[] = L"install-extension";
81 void GetOldIELowRightsElevationPolicyKeyPath(base::string16* key_path) {
82 key_path->assign(kElevationPolicyKeyPath,
83 arraysize(kElevationPolicyKeyPath) - 1);
84 key_path->append(kIELowRightsPolicyOldGuid,
85 arraysize(kIELowRightsPolicyOldGuid)- 1);
88 // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
89 // products managed by a given package.
90 // |old_version| can be NULL to indicate no Chrome is currently installed.
91 void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state,
92 const Version* old_version,
93 const Version& new_version,
94 WorkItemList* work_item_list) {
95 // First collect the list of DLLs to be registered from each product.
96 std::vector<base::FilePath> com_dll_list;
97 installer_state.AddComDllList(&com_dll_list);
99 // Then, if we got some, attempt to unregister the DLLs from the old
100 // version directory and then re-register them in the new one.
101 // Note that if we are migrating the install directory then we will not
102 // successfully unregister the old DLLs.
103 // TODO(robertshield): See whether we need to fix the migration case.
104 // TODO(robertshield): If we ever remove a DLL from a product, this will
105 // not unregister it on update. We should build the unregistration list from
106 // saved state instead of assuming it is the same as the registration list.
107 if (!com_dll_list.empty()) {
108 if (old_version) {
109 base::FilePath old_dll_path(installer_state.target_path().AppendASCII(
110 old_version->GetString()));
112 installer::AddRegisterComDllWorkItems(old_dll_path,
113 com_dll_list,
114 installer_state.system_install(),
115 false, // Unregister
116 true, // May fail
117 work_item_list);
120 base::FilePath dll_path(installer_state.target_path().AppendASCII(
121 new_version.GetString()));
122 installer::AddRegisterComDllWorkItems(dll_path,
123 com_dll_list,
124 installer_state.system_install(),
125 true, // Register
126 false, // Must succeed.
127 work_item_list);
131 void AddInstallerCopyTasks(const InstallerState& installer_state,
132 const base::FilePath& setup_path,
133 const base::FilePath& archive_path,
134 const base::FilePath& temp_path,
135 const Version& new_version,
136 WorkItemList* install_list) {
137 DCHECK(install_list);
138 base::FilePath installer_dir(
139 installer_state.GetInstallerDirectory(new_version));
140 install_list->AddCreateDirWorkItem(installer_dir);
142 base::FilePath exe_dst(installer_dir.Append(setup_path.BaseName()));
144 if (exe_dst != setup_path) {
145 install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(),
146 temp_path.value(), WorkItem::ALWAYS);
149 if (installer_state.RequiresActiveSetup()) {
150 // Make a copy of setup.exe with a different name so that Active Setup
151 // doesn't require an admin on XP thanks to Application Compatibility.
152 base::FilePath active_setup_exe(installer_dir.Append(kActiveSetupExe));
153 install_list->AddCopyTreeWorkItem(
154 setup_path.value(), active_setup_exe.value(), temp_path.value(),
155 WorkItem::ALWAYS);
158 // If only the App Host (not even the Chrome Binaries) is being installed,
159 // this must be a user-level App Host piggybacking on system-level Chrome
160 // Binaries. Only setup.exe is required, and only for uninstall.
161 if (installer_state.products().size() != 1 ||
162 !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
163 base::FilePath archive_dst(installer_dir.Append(archive_path.BaseName()));
164 if (archive_path != archive_dst) {
165 // In the past, we copied rather than moved for system level installs so
166 // that the permissions of %ProgramFiles% would be picked up. Now that
167 // |temp_path| is in %ProgramFiles% for system level installs (and in
168 // %LOCALAPPDATA% otherwise), there is no need to do this for the archive.
169 // Setup.exe, on the other hand, is created elsewhere so it must always be
170 // copied.
171 if (temp_path.IsParent(archive_path)) {
172 install_list->AddMoveTreeWorkItem(archive_path.value(),
173 archive_dst.value(),
174 temp_path.value(),
175 WorkItem::ALWAYS_MOVE);
176 } else {
177 // This may occur when setup is run out of an existing installation
178 // directory. For example, when quick-enabling user-level App Launcher
179 // from system-level Binaries. We can't (and don't want to) remove the
180 // system-level archive.
181 install_list->AddCopyTreeWorkItem(archive_path.value(),
182 archive_dst.value(),
183 temp_path.value(),
184 WorkItem::ALWAYS);
190 base::string16 GetRegistrationDataCommandKey(
191 const AppRegistrationData& reg_data,
192 const wchar_t* name) {
193 base::string16 cmd_key(reg_data.GetVersionKey());
194 cmd_key.append(1, base::FilePath::kSeparators[0])
195 .append(google_update::kRegCommandsKey)
196 .append(1, base::FilePath::kSeparators[0])
197 .append(name);
198 return cmd_key;
201 base::string16 GetRegCommandKey(BrowserDistribution* dist,
202 const wchar_t* name) {
203 return GetRegistrationDataCommandKey(dist->GetAppRegistrationData(), name);
206 // Adds work items to create (or delete if uninstalling) app commands to launch
207 // the app with a switch. The following criteria should be true:
208 // 1. The switch takes one parameter.
209 // 2. The command send pings.
210 // 3. The command is web accessible.
211 // 4. The command is run as the user.
212 void AddCommandWithParameterWorkItems(const InstallerState& installer_state,
213 const InstallationState& machine_state,
214 const Version& new_version,
215 const Product& product,
216 const wchar_t* command_key,
217 const wchar_t* app,
218 const char* command_with_parameter,
219 WorkItemList* work_item_list) {
220 DCHECK(command_key);
221 DCHECK(app);
222 DCHECK(command_with_parameter);
223 DCHECK(work_item_list);
225 base::string16 full_cmd_key(
226 GetRegCommandKey(product.distribution(), command_key));
228 if (installer_state.operation() == InstallerState::UNINSTALL) {
229 work_item_list->AddDeleteRegKeyWorkItem(installer_state.root_key(),
230 full_cmd_key,
231 KEY_WOW64_32KEY)
232 ->set_log_message("removing " + base::UTF16ToASCII(command_key) +
233 " command");
234 } else {
235 CommandLine cmd_line(installer_state.target_path().Append(app));
236 cmd_line.AppendSwitchASCII(command_with_parameter, "%1");
238 AppCommand cmd(cmd_line.GetCommandLineString());
239 cmd.set_sends_pings(true);
240 cmd.set_is_web_accessible(true);
241 cmd.set_is_run_as_user(true);
242 cmd.AddWorkItems(installer_state.root_key(), full_cmd_key, work_item_list);
246 void AddLegacyAppCommandRemovalItem(const InstallerState& installer_state,
247 const AppRegistrationData& reg_data,
248 const wchar_t* name,
249 WorkItemList* work_item_list) {
250 // These failures are ignored because this is a clean-up operation that
251 // shouldn't block an install or update on failing.
252 work_item_list->AddDeleteRegKeyWorkItem(
253 installer_state.root_key(),
254 GetRegistrationDataCommandKey(reg_data, name),
255 KEY_WOW64_32KEY)->set_ignore_failure(true);
258 // A callback invoked by |work_item| that adds firewall rules for Chrome. Rules
259 // are left in-place on rollback unless |remove_on_rollback| is true. This is
260 // the case for new installs only. Updates and overinstalls leave the rule
261 // in-place on rollback since a previous install of Chrome will be used in that
262 // case.
263 bool AddFirewallRulesCallback(bool system_level,
264 BrowserDistribution* dist,
265 const base::FilePath& chrome_path,
266 bool remove_on_rollback,
267 const CallbackWorkItem& work_item) {
268 // There is no work to do on rollback if this is not a new install.
269 if (work_item.IsRollback() && !remove_on_rollback)
270 return true;
272 scoped_ptr<FirewallManager> manager =
273 FirewallManager::Create(dist, chrome_path);
274 if (!manager) {
275 LOG(ERROR) << "Failed creating a FirewallManager. Continuing with install.";
276 return true;
279 if (work_item.IsRollback()) {
280 manager->RemoveFirewallRules();
281 return true;
284 // Adding the firewall rule is expected to fail for user-level installs on
285 // Vista+. Try anyway in case the installer is running elevated.
286 if (!manager->AddFirewallRules())
287 LOG(ERROR) << "Failed creating a firewall rules. Continuing with install.";
289 // Don't abort installation if the firewall rule couldn't be added.
290 return true;
293 // Adds work items to |list| to create firewall rules.
294 void AddFirewallRulesWorkItems(const InstallerState& installer_state,
295 BrowserDistribution* dist,
296 bool is_new_install,
297 WorkItemList* list) {
298 list->AddCallbackWorkItem(
299 base::Bind(&AddFirewallRulesCallback,
300 installer_state.system_install(),
301 dist,
302 installer_state.target_path().Append(kChromeExe),
303 is_new_install));
306 // Returns the basic CommandLine to setup.exe for a quick-enable operation on
307 // the binaries. This will unconditionally include --multi-install as well as
308 // --verbose-logging if the current installation was launched with
309 // --verbose-logging. |setup_path| and |new_version| are optional only when
310 // the operation is an uninstall.
311 CommandLine GetGenericQuickEnableCommand(
312 const InstallerState& installer_state,
313 const InstallationState& machine_state,
314 const base::FilePath& setup_path,
315 const Version& new_version) {
316 // Only valid for multi-install operations.
317 DCHECK(installer_state.is_multi_install());
318 // Only valid when Chrome Binaries aren't being uninstalled.
319 DCHECK(installer_state.operation() != InstallerState::UNINSTALL ||
320 !installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES));
321 // setup_path and new_version are required when not uninstalling.
322 DCHECK(installer_state.operation() == InstallerState::UNINSTALL ||
323 (!setup_path.empty() && new_version.IsValid()));
325 // The path to setup.exe contains the version of the Chrome Binaries, so it
326 // takes a little work to get it right.
327 base::FilePath binaries_setup_path;
328 if (installer_state.operation() == InstallerState::UNINSTALL) {
329 // One or more products are being uninstalled, but not Chrome Binaries.
330 // Use the path to the currently installed Chrome Binaries' setup.exe.
331 const ProductState* product_state = machine_state.GetProductState(
332 installer_state.system_install(),
333 BrowserDistribution::CHROME_BINARIES);
334 DCHECK(product_state);
335 binaries_setup_path = product_state->uninstall_command().GetProgram();
336 } else {
337 // Chrome Binaries are being installed, updated, or otherwise operated on.
338 // Use the path to the given |setup_path| in the normal location of
339 // multi-install Chrome Binaries of the given |version|.
340 binaries_setup_path = installer_state.GetInstallerDirectory(new_version)
341 .Append(setup_path.BaseName());
343 DCHECK(!binaries_setup_path.empty());
345 CommandLine cmd_line(binaries_setup_path);
346 cmd_line.AppendSwitch(switches::kMultiInstall);
347 if (installer_state.verbose_logging())
348 cmd_line.AppendSwitch(switches::kVerboseLogging);
349 return cmd_line;
352 // Adds work items to add the "quick-enable-application-host" command to the
353 // multi-installer binaries' version key on the basis of the current operation
354 // (represented in |installer_state|) and the pre-existing machine configuration
355 // (represented in |machine_state|).
356 void AddQuickEnableApplicationLauncherWorkItems(
357 const InstallerState& installer_state,
358 const InstallationState& machine_state,
359 const base::FilePath& setup_path,
360 const Version& new_version,
361 WorkItemList* work_item_list) {
362 DCHECK(work_item_list);
364 bool will_have_chrome_binaries =
365 WillProductBePresentAfterSetup(installer_state, machine_state,
366 BrowserDistribution::CHROME_BINARIES);
368 // For system-level binaries there is no way to keep the command state in sync
369 // with the installation/uninstallation of the Application Launcher (which is
370 // always at user-level). So we do not try to remove the command, i.e., it
371 // will always be installed if the Chrome Binaries are installed.
372 if (will_have_chrome_binaries) {
373 base::string16 cmd_key(
374 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
375 BrowserDistribution::CHROME_BINARIES),
376 kCmdQuickEnableApplicationHost));
377 CommandLine cmd_line(GetGenericQuickEnableCommand(installer_state,
378 machine_state,
379 setup_path,
380 new_version));
381 // kMultiInstall and kVerboseLogging were processed above.
382 cmd_line.AppendSwitch(switches::kChromeAppLauncher);
383 cmd_line.AppendSwitch(switches::kEnsureGoogleUpdatePresent);
384 AppCommand cmd(cmd_line.GetCommandLineString());
385 cmd.set_sends_pings(true);
386 cmd.set_is_web_accessible(true);
387 cmd.set_is_run_as_user(true);
388 cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
392 void AddProductSpecificWorkItems(const InstallationState& original_state,
393 const InstallerState& installer_state,
394 const base::FilePath& setup_path,
395 const Version& new_version,
396 bool is_new_install,
397 WorkItemList* list) {
398 const Products& products = installer_state.products();
399 for (Products::const_iterator it = products.begin(); it < products.end();
400 ++it) {
401 const Product& p = **it;
402 if (p.is_chrome()) {
403 AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p,
404 list);
405 AddFirewallRulesWorkItems(
406 installer_state, p.distribution(), is_new_install, list);
407 AddLegacyAppCommandRemovalItem(
408 installer_state,
409 p.distribution()->GetAppRegistrationData(),
410 kLegacyCmdInstallExtension,
411 list);
413 if (p.distribution()->AppHostIsSupported()) {
414 // Unconditionally remove the "install-application" command from the app
415 // hosts's key.
416 UpdatingAppRegistrationData app_launcher_reg_data(
417 installer::kAppLauncherGuid);
418 AddLegacyAppCommandRemovalItem(installer_state, app_launcher_reg_data,
419 kLegacyCmdInstallApp, list);
422 if (p.is_chrome_binaries()) {
423 AddQueryEULAAcceptanceWorkItems(
424 installer_state, setup_path, new_version, p, list);
425 AddQuickEnableChromeFrameWorkItems(installer_state, list);
426 AddQuickEnableApplicationLauncherWorkItems(
427 installer_state, original_state, setup_path, new_version, list);
432 // This is called when an MSI installation is run. It may be that a user is
433 // attempting to install the MSI on top of a non-MSI managed installation.
434 // If so, try and remove any existing uninstallation shortcuts, as we want the
435 // uninstall to be managed entirely by the MSI machinery (accessible via the
436 // Add/Remove programs dialog).
437 void AddDeleteUninstallShortcutsForMSIWorkItems(
438 const InstallerState& installer_state,
439 const Product& product,
440 const base::FilePath& temp_path,
441 WorkItemList* work_item_list) {
442 DCHECK(installer_state.is_msi())
443 << "This must only be called for MSI installations!";
445 // First attempt to delete the old installation's ARP dialog entry.
446 HKEY reg_root = installer_state.root_key();
447 base::string16 uninstall_reg(product.distribution()->GetUninstallRegPath());
449 WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
450 reg_root, uninstall_reg, KEY_WOW64_32KEY);
451 delete_reg_key->set_ignore_failure(true);
453 // Then attempt to delete the old installation's start menu shortcut.
454 base::FilePath uninstall_link;
455 if (installer_state.system_install()) {
456 PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
457 } else {
458 PathService::Get(base::DIR_START_MENU, &uninstall_link);
461 if (uninstall_link.empty()) {
462 LOG(ERROR) << "Failed to get location for shortcut.";
463 } else {
464 uninstall_link = uninstall_link.Append(
465 product.distribution()->GetStartMenuShortcutSubfolder(
466 BrowserDistribution::SUBFOLDER_CHROME));
467 uninstall_link = uninstall_link.Append(
468 product.distribution()->GetUninstallLinkName() + installer::kLnkExt);
469 VLOG(1) << "Deleting old uninstall shortcut (if present): "
470 << uninstall_link.value();
471 WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
472 uninstall_link, temp_path);
473 delete_link->set_ignore_failure(true);
474 delete_link->set_log_message(
475 "Failed to delete old uninstall shortcut.");
479 // Adds Chrome specific install work items to |install_list|.
480 // |current_version| can be NULL to indicate no Chrome is currently installed.
481 void AddChromeWorkItems(const InstallationState& original_state,
482 const InstallerState& installer_state,
483 const base::FilePath& setup_path,
484 const base::FilePath& archive_path,
485 const base::FilePath& src_path,
486 const base::FilePath& temp_path,
487 const Version* current_version,
488 const Version& new_version,
489 WorkItemList* install_list) {
490 const base::FilePath& target_path = installer_state.target_path();
492 if (current_version) {
493 // Delete the archive from an existing install to save some disk space. We
494 // make this an unconditional work item since there's no need to roll this
495 // back; if installation fails we'll be moved to the "-full" channel anyway.
496 base::FilePath old_installer_dir(
497 installer_state.GetInstallerDirectory(*current_version));
498 base::FilePath old_archive(
499 old_installer_dir.Append(installer::kChromeArchive));
500 // Don't delete the archive that we are actually installing from.
501 if (archive_path != old_archive) {
502 install_list->AddDeleteTreeWorkItem(old_archive, temp_path)->
503 set_ignore_failure(true);
507 // Delete any new_chrome.exe if present (we will end up creating a new one
508 // if required) and then copy chrome.exe
509 base::FilePath new_chrome_exe(target_path.Append(installer::kChromeNewExe));
511 install_list->AddDeleteTreeWorkItem(new_chrome_exe, temp_path);
513 // TODO(grt): Remove this check in M35.
514 if (installer_state.IsChromeFrameRunning(original_state)) {
515 VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe";
516 install_list->AddCopyTreeWorkItem(
517 src_path.Append(installer::kChromeExe).value(),
518 new_chrome_exe.value(),
519 temp_path.value(),
520 WorkItem::ALWAYS);
521 } else {
522 install_list->AddCopyTreeWorkItem(
523 src_path.Append(installer::kChromeExe).value(),
524 target_path.Append(installer::kChromeExe).value(),
525 temp_path.value(),
526 WorkItem::NEW_NAME_IF_IN_USE,
527 new_chrome_exe.value());
530 // Extra executable for 64 bit systems.
531 // NOTE: We check for "not disabled" so that if the API call fails, we play it
532 // safe and copy the executable anyway.
533 // NOTE: the file wow_helper.exe is only needed for Vista and below.
534 if (base::win::OSInfo::GetInstance()->wow64_status() !=
535 base::win::OSInfo::WOW64_DISABLED &&
536 base::win::GetVersion() <= base::win::VERSION_VISTA) {
537 install_list->AddMoveTreeWorkItem(
538 src_path.Append(installer::kWowHelperExe).value(),
539 target_path.Append(installer::kWowHelperExe).value(),
540 temp_path.value(),
541 WorkItem::ALWAYS_MOVE);
544 // Install kVisualElementsManifest if it is present in |src_path|. No need to
545 // make this a conditional work item as if the file is not there now, it will
546 // never be.
547 if (base::PathExists(
548 src_path.Append(installer::kVisualElementsManifest))) {
549 install_list->AddMoveTreeWorkItem(
550 src_path.Append(installer::kVisualElementsManifest).value(),
551 target_path.Append(installer::kVisualElementsManifest).value(),
552 temp_path.value(),
553 WorkItem::ALWAYS_MOVE);
554 } else {
555 // We do not want to have an old VisualElementsManifest pointing to an old
556 // version directory. Delete it as there wasn't a new one to replace it.
557 install_list->AddDeleteTreeWorkItem(
558 target_path.Append(installer::kVisualElementsManifest),
559 temp_path);
562 // In the past, we copied rather than moved for system level installs so that
563 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path|
564 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
565 // otherwise), there is no need to do this.
566 // Note that we pass true for check_duplicates to avoid failing on in-use
567 // repair runs if the current_version is the same as the new_version.
568 bool check_for_duplicates = (current_version &&
569 current_version->Equals(new_version));
570 install_list->AddMoveTreeWorkItem(
571 src_path.AppendASCII(new_version.GetString()).value(),
572 target_path.AppendASCII(new_version.GetString()).value(),
573 temp_path.value(),
574 check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
575 WorkItem::ALWAYS_MOVE);
577 // Delete any old_chrome.exe if present (ignore failure if it's in use).
578 install_list->AddDeleteTreeWorkItem(
579 target_path.Append(installer::kChromeOldExe), temp_path)->
580 set_ignore_failure(true);
583 // Probes COM machinery to get an instance of delegate_execute.exe's
584 // CommandExecuteImpl class. This is required so that COM purges its cache of
585 // the path to the binary, which changes on updates. This callback
586 // unconditionally returns true since an install should not be aborted if the
587 // probe fails.
588 bool ProbeCommandExecuteCallback(const base::string16& command_execute_id,
589 const CallbackWorkItem& work_item) {
590 // Noop on rollback.
591 if (work_item.IsRollback())
592 return true;
594 CLSID class_id = {};
596 HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
597 if (FAILED(hr)) {
598 LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
599 "CLSID; hr=0x" << std::hex << hr;
600 } else {
601 base::win::ScopedComPtr<IUnknown> command_execute_impl;
602 hr = command_execute_impl.CreateInstance(class_id, NULL,
603 CLSCTX_LOCAL_SERVER);
604 if (hr != REGDB_E_CLASSNOTREG) {
605 LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x"
606 << std::hex << hr;
610 return true;
613 void AddUninstallDelegateExecuteWorkItems(
614 HKEY root,
615 const base::string16& delegate_execute_path,
616 WorkItemList* list) {
617 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
618 << root;
619 // Delete both 64 and 32 keys to handle 32->64 or 64->32 migration.
620 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_32KEY);
622 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path, KEY_WOW64_64KEY);
624 // In the past, the ICommandExecuteImpl interface and a TypeLib were both
625 // registered. Remove these since this operation may be updating a machine
626 // that had the old registrations.
627 list->AddDeleteRegKeyWorkItem(root,
628 L"Software\\Classes\\Interface\\"
629 L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}",
630 KEY_WOW64_32KEY);
631 list->AddDeleteRegKeyWorkItem(root,
632 L"Software\\Classes\\TypeLib\\"
633 #if defined(GOOGLE_CHROME_BUILD)
634 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}",
635 #else
636 L"{7779FB70-B399-454A-AA1A-BAA850032B10}",
637 #endif
638 KEY_WOW64_32KEY);
641 // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
642 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by
643 // mistake (with the same GUID as Chrome). The fix stopped registering the bad
644 // value, but didn't delete it. This is a problem for users who had installed
645 // Canary before 20.0.1106.0 and now have a system-level Chrome, as the
646 // left-behind Canary registrations in HKCU mask the HKLM registrations for the
647 // same GUID. Cleanup those registrations if they still exist and belong to this
648 // Canary (i.e., the registered delegate_execute's path is under |target_path|).
649 void CleanupBadCanaryDelegateExecuteRegistration(
650 const base::FilePath& target_path,
651 WorkItemList* list) {
652 base::string16 google_chrome_delegate_execute_path(
653 L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
654 base::string16 google_chrome_local_server_32(
655 google_chrome_delegate_execute_path + L"\\LocalServer32");
657 RegKey local_server_32_key;
658 base::string16 registered_server;
659 if (local_server_32_key.Open(HKEY_CURRENT_USER,
660 google_chrome_local_server_32.c_str(),
661 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
662 local_server_32_key.ReadValue(L"ServerExecutable",
663 &registered_server) == ERROR_SUCCESS &&
664 target_path.IsParent(base::FilePath(registered_server))) {
665 scoped_ptr<WorkItemList> no_rollback_list(
666 WorkItem::CreateNoRollbackWorkItemList());
667 AddUninstallDelegateExecuteWorkItems(
668 HKEY_CURRENT_USER, google_chrome_delegate_execute_path,
669 no_rollback_list.get());
670 list->AddWorkItem(no_rollback_list.release());
671 VLOG(1) << "Added deletion items for bad Canary registrations.";
675 } // namespace
677 // This method adds work items to create (or update) Chrome uninstall entry in
678 // either the Control Panel->Add/Remove Programs list or in the Omaha client
679 // state key if running under an MSI installer.
680 void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
681 const base::FilePath& setup_path,
682 const Version& new_version,
683 const Product& product,
684 WorkItemList* install_list) {
685 HKEY reg_root = installer_state.root_key();
686 BrowserDistribution* browser_dist = product.distribution();
687 DCHECK(browser_dist);
689 // When we are installed via an MSI, we need to store our uninstall strings
690 // in the Google Update client state key. We do this even for non-MSI
691 // managed installs to avoid breaking the edge case whereby an MSI-managed
692 // install is updated by a non-msi installer (which would confuse the MSI
693 // machinery if these strings were not also updated). The UninstallString
694 // value placed in the client state key is also used by the mini_installer to
695 // locate the setup.exe instance used for binary patching.
696 // Do not quote the command line for the MSI invocation.
697 base::FilePath install_path(installer_state.target_path());
698 base::FilePath installer_path(
699 installer_state.GetInstallerDirectory(new_version));
700 installer_path = installer_path.Append(setup_path.BaseName());
702 CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
703 AppendUninstallCommandLineFlags(installer_state, product,
704 &uninstall_arguments);
706 base::string16 update_state_key(browser_dist->GetStateKey());
707 install_list->AddCreateRegKeyWorkItem(
708 reg_root, update_state_key, KEY_WOW64_32KEY);
709 install_list->AddSetRegValueWorkItem(reg_root,
710 update_state_key,
711 KEY_WOW64_32KEY,
712 installer::kUninstallStringField,
713 installer_path.value(),
714 true);
715 install_list->AddSetRegValueWorkItem(
716 reg_root,
717 update_state_key,
718 KEY_WOW64_32KEY,
719 installer::kUninstallArgumentsField,
720 uninstall_arguments.GetCommandLineString(),
721 true);
723 // MSI installations will manage their own uninstall shortcuts.
724 if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
725 // We need to quote the command line for the Add/Remove Programs dialog.
726 CommandLine quoted_uninstall_cmd(installer_path);
727 DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"');
728 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
730 base::string16 uninstall_reg = browser_dist->GetUninstallRegPath();
731 install_list->AddCreateRegKeyWorkItem(
732 reg_root, uninstall_reg, KEY_WOW64_32KEY);
733 install_list->AddSetRegValueWorkItem(reg_root,
734 uninstall_reg,
735 KEY_WOW64_32KEY,
736 installer::kUninstallDisplayNameField,
737 browser_dist->GetDisplayName(),
738 true);
739 install_list->AddSetRegValueWorkItem(
740 reg_root,
741 uninstall_reg,
742 KEY_WOW64_32KEY,
743 installer::kUninstallStringField,
744 quoted_uninstall_cmd.GetCommandLineString(),
745 true);
746 install_list->AddSetRegValueWorkItem(reg_root,
747 uninstall_reg,
748 KEY_WOW64_32KEY,
749 L"InstallLocation",
750 install_path.value(),
751 true);
753 BrowserDistribution* dist = product.distribution();
754 base::string16 chrome_icon = ShellUtil::FormatIconLocation(
755 install_path.Append(dist->GetIconFilename()).value(),
756 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
757 install_list->AddSetRegValueWorkItem(reg_root,
758 uninstall_reg,
759 KEY_WOW64_32KEY,
760 L"DisplayIcon",
761 chrome_icon,
762 true);
763 install_list->AddSetRegValueWorkItem(reg_root,
764 uninstall_reg,
765 KEY_WOW64_32KEY,
766 L"NoModify",
767 static_cast<DWORD>(1),
768 true);
769 install_list->AddSetRegValueWorkItem(reg_root,
770 uninstall_reg,
771 KEY_WOW64_32KEY,
772 L"NoRepair",
773 static_cast<DWORD>(1),
774 true);
776 install_list->AddSetRegValueWorkItem(reg_root,
777 uninstall_reg,
778 KEY_WOW64_32KEY,
779 L"Publisher",
780 browser_dist->GetPublisherName(),
781 true);
782 install_list->AddSetRegValueWorkItem(reg_root,
783 uninstall_reg,
784 KEY_WOW64_32KEY,
785 L"Version",
786 ASCIIToWide(new_version.GetString()),
787 true);
788 install_list->AddSetRegValueWorkItem(reg_root,
789 uninstall_reg,
790 KEY_WOW64_32KEY,
791 L"DisplayVersion",
792 ASCIIToWide(new_version.GetString()),
793 true);
794 // TODO(wfh): Ensure that this value is preserved in the 64-bit hive when
795 // 64-bit installs place the uninstall information into the 64-bit registry.
796 install_list->AddSetRegValueWorkItem(reg_root,
797 uninstall_reg,
798 KEY_WOW64_32KEY,
799 L"InstallDate",
800 InstallUtil::GetCurrentDate(),
801 false);
803 const std::vector<uint16>& version_components = new_version.components();
804 if (version_components.size() == 4) {
805 // Our version should be in major.minor.build.rev.
806 install_list->AddSetRegValueWorkItem(
807 reg_root,
808 uninstall_reg,
809 KEY_WOW64_32KEY,
810 L"VersionMajor",
811 static_cast<DWORD>(version_components[2]),
812 true);
813 install_list->AddSetRegValueWorkItem(
814 reg_root,
815 uninstall_reg,
816 KEY_WOW64_32KEY,
817 L"VersionMinor",
818 static_cast<DWORD>(version_components[3]),
819 true);
824 // Create Version key for a product (if not already present) and sets the new
825 // product version as the last step.
826 void AddVersionKeyWorkItems(HKEY root,
827 const base::string16& version_key,
828 const base::string16& product_name,
829 const Version& new_version,
830 bool add_language_identifier,
831 WorkItemList* list) {
832 list->AddCreateRegKeyWorkItem(root, version_key, KEY_WOW64_32KEY);
834 list->AddSetRegValueWorkItem(root,
835 version_key,
836 KEY_WOW64_32KEY,
837 google_update::kRegNameField,
838 product_name,
839 true); // overwrite name also
840 list->AddSetRegValueWorkItem(root,
841 version_key,
842 KEY_WOW64_32KEY,
843 google_update::kRegOopcrashesField,
844 static_cast<DWORD>(1),
845 false); // set during first install
846 if (add_language_identifier) {
847 // Write the language identifier of the current translation. Omaha's set of
848 // languages is a superset of Chrome's set of translations with this one
849 // exception: what Chrome calls "en-us", Omaha calls "en". sigh.
850 base::string16 language(GetCurrentTranslation());
851 if (LowerCaseEqualsASCII(language, "en-us"))
852 language.resize(2);
853 list->AddSetRegValueWorkItem(root,
854 version_key,
855 KEY_WOW64_32KEY,
856 google_update::kRegLangField,
857 language,
858 false); // do not overwrite language
860 list->AddSetRegValueWorkItem(root,
861 version_key,
862 KEY_WOW64_32KEY,
863 google_update::kRegVersionField,
864 ASCIIToWide(new_version.GetString()),
865 true); // overwrite version
868 // Mirror oeminstall the first time anything is installed multi. There is no
869 // need to update the value on future install/update runs since this value never
870 // changes. Note that the value is removed by Google Update after EULA
871 // acceptance is processed.
872 void AddOemInstallWorkItems(const InstallationState& original_state,
873 const InstallerState& installer_state,
874 WorkItemList* install_list) {
875 DCHECK(installer_state.is_multi_install());
876 const bool system_install = installer_state.system_install();
877 if (!original_state.GetProductState(system_install,
878 BrowserDistribution::CHROME_BINARIES)) {
879 const HKEY root_key = installer_state.root_key();
880 base::string16 multi_key(
881 installer_state.multi_package_binaries_distribution()->GetStateKey());
883 // Copy the value from Chrome unless Chrome isn't installed or being
884 // installed.
885 BrowserDistribution::Type source_type;
886 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) {
887 source_type = BrowserDistribution::CHROME_BROWSER;
888 } else if (!installer_state.products().empty()) {
889 // Pick a product, any product.
890 source_type = installer_state.products()[0]->distribution()->GetType();
891 } else {
892 // Nothing is being installed? Entirely unexpected, so do no harm.
893 LOG(ERROR) << "No products found in AddOemInstallWorkItems";
894 return;
896 const ProductState* source_product =
897 original_state.GetNonVersionedProductState(system_install, source_type);
899 base::string16 oem_install;
900 if (source_product->GetOemInstall(&oem_install)) {
901 VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from "
902 << BrowserDistribution::GetSpecificDistribution(source_type)->
903 GetDisplayName();
904 install_list->AddCreateRegKeyWorkItem(
905 root_key, multi_key, KEY_WOW64_32KEY);
906 // Always overwrite an old value.
907 install_list->AddSetRegValueWorkItem(root_key,
908 multi_key,
909 KEY_WOW64_32KEY,
910 google_update::kRegOemInstallField,
911 oem_install,
912 true);
913 } else {
914 // Clear any old value.
915 install_list->AddDeleteRegValueWorkItem(
916 root_key,
917 multi_key,
918 KEY_WOW64_32KEY,
919 google_update::kRegOemInstallField);
924 // Mirror eulaaccepted the first time anything is installed multi. There is no
925 // need to update the value on future install/update runs since
926 // GoogleUpdateSettings::SetEULAConsent will modify the value for both the
927 // relevant product and for the binaries.
928 void AddEulaAcceptedWorkItems(const InstallationState& original_state,
929 const InstallerState& installer_state,
930 WorkItemList* install_list) {
931 DCHECK(installer_state.is_multi_install());
932 const bool system_install = installer_state.system_install();
933 if (!original_state.GetProductState(system_install,
934 BrowserDistribution::CHROME_BINARIES)) {
935 const HKEY root_key = installer_state.root_key();
936 base::string16 multi_key(
937 installer_state.multi_package_binaries_distribution()->GetStateKey());
939 // Copy the value from the product with the greatest value.
940 bool have_eula_accepted = false;
941 BrowserDistribution::Type product_type = BrowserDistribution::NUM_TYPES;
942 DWORD eula_accepted = 0;
943 const Products& products = installer_state.products();
944 for (Products::const_iterator it = products.begin(); it < products.end();
945 ++it) {
946 const Product& product = **it;
947 if (product.is_chrome_binaries())
948 continue;
949 DWORD dword_value = 0;
950 BrowserDistribution::Type this_type = product.distribution()->GetType();
951 const ProductState* product_state =
952 original_state.GetNonVersionedProductState(
953 system_install, this_type);
954 if (product_state->GetEulaAccepted(&dword_value) &&
955 (!have_eula_accepted || dword_value > eula_accepted)) {
956 have_eula_accepted = true;
957 eula_accepted = dword_value;
958 product_type = this_type;
962 if (have_eula_accepted) {
963 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from "
964 << BrowserDistribution::GetSpecificDistribution(product_type)->
965 GetDisplayName();
966 install_list->AddCreateRegKeyWorkItem(
967 root_key, multi_key, KEY_WOW64_32KEY);
968 install_list->AddSetRegValueWorkItem(root_key,
969 multi_key,
970 KEY_WOW64_32KEY,
971 google_update::kRegEULAAceptedField,
972 eula_accepted,
973 true);
974 } else {
975 // Clear any old value.
976 install_list->AddDeleteRegValueWorkItem(
977 root_key,
978 multi_key,
979 KEY_WOW64_32KEY,
980 google_update::kRegEULAAceptedField);
985 // Adds work items that make registry adjustments for Google Update.
986 void AddGoogleUpdateWorkItems(const InstallationState& original_state,
987 const InstallerState& installer_state,
988 WorkItemList* install_list) {
989 // Is a multi-install product being installed or over-installed?
990 if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
991 installer_state.operation() != InstallerState::MULTI_UPDATE) {
992 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
993 return;
996 const bool system_install = installer_state.system_install();
997 const HKEY root_key = installer_state.root_key();
998 base::string16 multi_key(
999 installer_state.multi_package_binaries_distribution()->GetStateKey());
1001 // For system-level installs, make sure the ClientStateMedium key for the
1002 // binaries exists.
1003 if (system_install) {
1004 install_list->AddCreateRegKeyWorkItem(
1005 root_key,
1006 installer_state.multi_package_binaries_distribution()
1007 ->GetStateMediumKey()
1008 .c_str(),
1009 KEY_WOW64_32KEY);
1012 // Creating the ClientState key for binaries, if we're migrating to multi then
1013 // copy over Chrome's brand code if it has one.
1014 if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
1015 const ProductState* chrome_product_state =
1016 original_state.GetNonVersionedProductState(
1017 system_install, BrowserDistribution::CHROME_BROWSER);
1019 const base::string16& brand(chrome_product_state->brand());
1020 if (!brand.empty()) {
1021 install_list->AddCreateRegKeyWorkItem(
1022 root_key, multi_key, KEY_WOW64_32KEY);
1023 // Write Chrome's brand code to the multi key. Never overwrite the value
1024 // if one is already present (although this shouldn't happen).
1025 install_list->AddSetRegValueWorkItem(root_key,
1026 multi_key,
1027 KEY_WOW64_32KEY,
1028 google_update::kRegBrandField,
1029 brand,
1030 false);
1034 AddOemInstallWorkItems(original_state, installer_state, install_list);
1035 AddEulaAcceptedWorkItems(original_state, installer_state, install_list);
1036 AddUsageStatsWorkItems(original_state, installer_state, install_list);
1038 // TODO(grt): check for other keys/values we should put in the package's
1039 // ClientState and/or Clients key.
1042 void AddUsageStatsWorkItems(const InstallationState& original_state,
1043 const InstallerState& installer_state,
1044 WorkItemList* install_list) {
1045 DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
1046 installer_state.operation() == InstallerState::MULTI_UPDATE);
1048 HKEY root_key = installer_state.root_key();
1049 bool value_found = false;
1050 DWORD usagestats = 0;
1051 const Products& products = installer_state.products();
1053 // Search for an existing usagestats value for any product.
1054 for (Products::const_iterator scan = products.begin(), end = products.end();
1055 !value_found && scan != end; ++scan) {
1056 if ((*scan)->is_chrome_binaries())
1057 continue;
1058 BrowserDistribution* dist = (*scan)->distribution();
1059 const ProductState* product_state =
1060 original_state.GetNonVersionedProductState(
1061 installer_state.system_install(), dist->GetType());
1062 value_found = product_state->GetUsageStats(&usagestats);
1065 // If a value was found, write it in the appropriate location for the
1066 // binaries and remove all values from the products.
1067 if (value_found) {
1068 base::string16 state_key(
1069 installer_state.multi_package_binaries_distribution()->GetStateKey());
1070 install_list->AddCreateRegKeyWorkItem(root_key, state_key, KEY_WOW64_32KEY);
1071 // Overwrite any existing value so that overinstalls (where Omaha writes a
1072 // new value into a product's state key) pick up the correct value.
1073 install_list->AddSetRegValueWorkItem(root_key,
1074 state_key,
1075 KEY_WOW64_32KEY,
1076 google_update::kRegUsageStatsField,
1077 usagestats,
1078 true);
1080 for (Products::const_iterator scan = products.begin(), end = products.end();
1081 scan != end; ++scan) {
1082 if ((*scan)->is_chrome_binaries())
1083 continue;
1084 BrowserDistribution* dist = (*scan)->distribution();
1085 if (installer_state.system_install()) {
1086 install_list->AddDeleteRegValueWorkItem(
1087 root_key,
1088 dist->GetStateMediumKey(),
1089 KEY_WOW64_32KEY,
1090 google_update::kRegUsageStatsField);
1091 // Previous versions of Chrome also wrote a value in HKCU even for
1092 // system-level installs, so clean that up.
1093 install_list->AddDeleteRegValueWorkItem(
1094 HKEY_CURRENT_USER,
1095 dist->GetStateKey(),
1096 KEY_WOW64_32KEY,
1097 google_update::kRegUsageStatsField);
1099 install_list->AddDeleteRegValueWorkItem(
1100 root_key,
1101 dist->GetStateKey(),
1102 KEY_WOW64_32KEY,
1103 google_update::kRegUsageStatsField);
1108 bool AppendPostInstallTasks(const InstallerState& installer_state,
1109 const base::FilePath& setup_path,
1110 const Version* current_version,
1111 const Version& new_version,
1112 const base::FilePath& temp_path,
1113 WorkItemList* post_install_task_list) {
1114 DCHECK(post_install_task_list);
1116 HKEY root = installer_state.root_key();
1117 const Products& products = installer_state.products();
1118 base::FilePath new_chrome_exe(
1119 installer_state.target_path().Append(installer::kChromeNewExe));
1121 // Append work items that will only be executed if this was an update.
1122 // We update the 'opv' value with the current version that is active,
1123 // the 'cpv' value with the critical update version (if present), and the
1124 // 'cmd' value with the rename command to run.
1126 scoped_ptr<WorkItemList> in_use_update_work_items(
1127 WorkItem::CreateConditionalWorkItemList(
1128 new ConditionRunIfFileExists(new_chrome_exe)));
1129 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
1131 // |critical_version| will be valid only if this in-use update includes a
1132 // version considered critical relative to the version being updated.
1133 Version critical_version(installer_state.DetermineCriticalVersion(
1134 current_version, new_version));
1135 base::FilePath installer_path(
1136 installer_state.GetInstallerDirectory(new_version).Append(
1137 setup_path.BaseName()));
1139 CommandLine rename(installer_path);
1140 rename.AppendSwitch(switches::kRenameChromeExe);
1141 if (installer_state.system_install())
1142 rename.AppendSwitch(switches::kSystemLevel);
1144 if (installer_state.verbose_logging())
1145 rename.AppendSwitch(switches::kVerboseLogging);
1147 base::string16 version_key;
1148 for (size_t i = 0; i < products.size(); ++i) {
1149 BrowserDistribution* dist = products[i]->distribution();
1150 version_key = dist->GetVersionKey();
1152 if (current_version) {
1153 in_use_update_work_items->AddSetRegValueWorkItem(
1154 root,
1155 version_key,
1156 KEY_WOW64_32KEY,
1157 google_update::kRegOldVersionField,
1158 ASCIIToWide(current_version->GetString()),
1159 true);
1161 if (critical_version.IsValid()) {
1162 in_use_update_work_items->AddSetRegValueWorkItem(
1163 root,
1164 version_key,
1165 KEY_WOW64_32KEY,
1166 google_update::kRegCriticalVersionField,
1167 ASCIIToWide(critical_version.GetString()),
1168 true);
1169 } else {
1170 in_use_update_work_items->AddDeleteRegValueWorkItem(
1171 root,
1172 version_key,
1173 KEY_WOW64_32KEY,
1174 google_update::kRegCriticalVersionField);
1177 // Adding this registry entry for all products (but the binaries) is
1178 // overkill. However, as it stands, we don't have a way to know which
1179 // product will check the key and run the command, so we add it for all.
1180 // The first to run it will perform the operation and clean up the other
1181 // values.
1182 if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
1183 CommandLine product_rename_cmd(rename);
1184 products[i]->AppendRenameFlags(&product_rename_cmd);
1185 in_use_update_work_items->AddSetRegValueWorkItem(
1186 root,
1187 version_key,
1188 KEY_WOW64_32KEY,
1189 google_update::kRegRenameCmdField,
1190 product_rename_cmd.GetCommandLineString(),
1191 true);
1195 post_install_task_list->AddWorkItem(in_use_update_work_items.release());
1198 // Append work items that will be executed if this was NOT an in-use update.
1200 scoped_ptr<WorkItemList> regular_update_work_items(
1201 WorkItem::CreateConditionalWorkItemList(
1202 new Not(new ConditionRunIfFileExists(new_chrome_exe))));
1203 regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
1205 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1206 for (size_t i = 0; i < products.size(); ++i) {
1207 BrowserDistribution* dist = products[i]->distribution();
1208 base::string16 version_key(dist->GetVersionKey());
1209 regular_update_work_items->AddDeleteRegValueWorkItem(
1210 root,
1211 version_key,
1212 KEY_WOW64_32KEY,
1213 google_update::kRegOldVersionField);
1214 regular_update_work_items->AddDeleteRegValueWorkItem(
1215 root,
1216 version_key,
1217 KEY_WOW64_32KEY,
1218 google_update::kRegCriticalVersionField);
1219 regular_update_work_items->AddDeleteRegValueWorkItem(
1220 root,
1221 version_key,
1222 KEY_WOW64_32KEY,
1223 google_update::kRegRenameCmdField);
1226 post_install_task_list->AddWorkItem(regular_update_work_items.release());
1229 AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
1230 new_version, post_install_task_list);
1232 // If we're told that we're an MSI install, make sure to set the marker
1233 // in the client state key so that future updates do the right thing.
1234 if (installer_state.is_msi()) {
1235 for (size_t i = 0; i < products.size(); ++i) {
1236 const Product* product = products[i];
1237 AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
1238 post_install_task_list);
1240 // We want MSI installs to take over the Add/Remove Programs shortcut.
1241 // Make a best-effort attempt to delete any shortcuts left over from
1242 // previous non-MSI installations for the same type of install (system or
1243 // per user).
1244 if (product->ShouldCreateUninstallEntry()) {
1245 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
1246 temp_path,
1247 post_install_task_list);
1252 return true;
1255 void AddInstallWorkItems(const InstallationState& original_state,
1256 const InstallerState& installer_state,
1257 const base::FilePath& setup_path,
1258 const base::FilePath& archive_path,
1259 const base::FilePath& src_path,
1260 const base::FilePath& temp_path,
1261 const Version* current_version,
1262 const Version& new_version,
1263 WorkItemList* install_list) {
1264 DCHECK(install_list);
1266 const base::FilePath& target_path = installer_state.target_path();
1268 // A temp directory that work items need and the actual install directory.
1269 install_list->AddCreateDirWorkItem(temp_path);
1270 install_list->AddCreateDirWorkItem(target_path);
1272 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
1273 installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
1274 AddChromeWorkItems(original_state,
1275 installer_state,
1276 setup_path,
1277 archive_path,
1278 src_path,
1279 temp_path,
1280 current_version,
1281 new_version,
1282 install_list);
1285 if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1286 install_list->AddCopyTreeWorkItem(
1287 src_path.Append(installer::kChromeAppHostExe).value(),
1288 target_path.Append(installer::kChromeAppHostExe).value(),
1289 temp_path.value(),
1290 WorkItem::ALWAYS,
1291 L"");
1294 // Copy installer in install directory
1295 AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
1296 new_version, install_list);
1298 const HKEY root = installer_state.root_key();
1299 // Only set "lang" for user-level installs since for system-level, the install
1300 // language may not be related to a given user's runtime language.
1301 const bool add_language_identifier = !installer_state.system_install();
1303 const Products& products = installer_state.products();
1304 for (Products::const_iterator it = products.begin(); it < products.end();
1305 ++it) {
1306 const Product& product = **it;
1308 AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
1309 product, install_list);
1311 BrowserDistribution* dist = product.distribution();
1312 AddVersionKeyWorkItems(root,
1313 dist->GetVersionKey(),
1314 dist->GetDisplayName(),
1315 new_version,
1316 add_language_identifier,
1317 install_list);
1319 AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
1320 product, install_list);
1322 AddActiveSetupWorkItems(installer_state, setup_path, new_version, product,
1323 install_list);
1326 // TODO(huangs): Implement actual migration code and remove the hack below.
1327 // If installing Chrome without the legacy stand-alone App Launcher (to be
1328 // handled later), add "shadow" App Launcher registry keys so Google Update
1329 // would recognize the "dr" value in the App Launcher ClientState key.
1330 // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome.
1331 if (installer_state.is_multi_install() &&
1332 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) &&
1333 !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1334 BrowserDistribution* shadow_app_launcher_dist =
1335 BrowserDistribution::GetSpecificDistribution(
1336 BrowserDistribution::CHROME_APP_HOST);
1337 AddVersionKeyWorkItems(root,
1338 shadow_app_launcher_dist->GetVersionKey(),
1339 shadow_app_launcher_dist->GetDisplayName(),
1340 new_version,
1341 add_language_identifier,
1342 install_list);
1345 // Add any remaining work items that involve special settings for
1346 // each product.
1347 AddProductSpecificWorkItems(original_state,
1348 installer_state,
1349 setup_path,
1350 new_version,
1351 current_version == NULL,
1352 install_list);
1354 // Copy over brand, usagestats, and other values.
1355 AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
1357 // Append the tasks that run after the installation.
1358 AppendPostInstallTasks(installer_state,
1359 setup_path,
1360 current_version,
1361 new_version,
1362 temp_path,
1363 install_list);
1366 void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
1367 const std::vector<base::FilePath>& dll_list,
1368 bool system_level,
1369 bool do_register,
1370 bool ignore_failures,
1371 WorkItemList* work_item_list) {
1372 DCHECK(work_item_list);
1373 if (dll_list.empty()) {
1374 VLOG(1) << "No COM DLLs to register";
1375 } else {
1376 std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
1377 for (; dll_iter != dll_list.end(); ++dll_iter) {
1378 base::FilePath dll_path = dll_folder.Append(*dll_iter);
1379 WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
1380 dll_path.value(), do_register, !system_level);
1381 DCHECK(work_item);
1382 work_item->set_ignore_failure(ignore_failures);
1387 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
1388 BrowserDistribution* dist,
1389 bool set,
1390 WorkItemList* work_item_list) {
1391 DCHECK(work_item_list);
1392 DWORD msi_value = set ? 1 : 0;
1393 WorkItem* set_msi_work_item =
1394 work_item_list->AddSetRegValueWorkItem(installer_state.root_key(),
1395 dist->GetStateKey(),
1396 KEY_WOW64_32KEY,
1397 google_update::kRegMSIField,
1398 msi_value,
1399 true);
1400 DCHECK(set_msi_work_item);
1401 set_msi_work_item->set_ignore_failure(true);
1402 set_msi_work_item->set_log_message("Could not write MSI marker!");
1405 void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
1406 const base::FilePath& target_path,
1407 const Version& new_version,
1408 const Product& product,
1409 WorkItemList* list) {
1410 base::string16 handler_class_uuid;
1411 BrowserDistribution* dist = product.distribution();
1412 if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) {
1413 if (InstallUtil::IsChromeSxSProcess()) {
1414 CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
1415 } else {
1416 VLOG(1) << "No DelegateExecute verb handler processing to do for "
1417 << dist->GetDisplayName();
1419 return;
1422 HKEY root = installer_state.root_key();
1423 base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
1424 delegate_execute_path.append(handler_class_uuid);
1426 // Unconditionally remove registration regardless of whether or not it is
1427 // needed since builds after r132190 included it when it wasn't strictly
1428 // necessary. Do this removal before adding in the new key to ensure that
1429 // the COM probe/flush below does its job.
1430 AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
1432 // Add work items to register the handler iff it is present.
1433 // See also shell_util.cc's GetProgIdEntries.
1434 if (installer_state.operation() != InstallerState::UNINSTALL) {
1435 VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1437 // Force COM to flush its cache containing the path to the old handler.
1438 list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
1439 handler_class_uuid));
1441 // The path to the exe (in the version directory).
1442 base::FilePath delegate_execute(target_path);
1443 if (new_version.IsValid())
1444 delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
1445 delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
1447 // Command-line featuring the quoted path to the exe.
1448 base::string16 command(1, L'"');
1449 command.append(delegate_execute.value()).append(1, L'"');
1451 // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1452 list->AddCreateRegKeyWorkItem(
1453 root, delegate_execute_path, WorkItem::kWow64Default);
1454 list->AddSetRegValueWorkItem(root,
1455 delegate_execute_path,
1456 WorkItem::kWow64Default,
1457 L"",
1458 L"CommandExecuteImpl Class",
1459 true);
1460 base::string16 subkey(delegate_execute_path);
1461 subkey.append(L"\\LocalServer32");
1462 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1463 list->AddSetRegValueWorkItem(
1464 root, subkey, WorkItem::kWow64Default, L"", command, true);
1465 list->AddSetRegValueWorkItem(root,
1466 subkey,
1467 WorkItem::kWow64Default,
1468 L"ServerExecutable",
1469 delegate_execute.value(),
1470 true);
1472 subkey.assign(delegate_execute_path).append(L"\\Programmable");
1473 list->AddCreateRegKeyWorkItem(root, subkey, WorkItem::kWow64Default);
1477 void AddActiveSetupWorkItems(const InstallerState& installer_state,
1478 const base::FilePath& setup_path,
1479 const Version& new_version,
1480 const Product& product,
1481 WorkItemList* list) {
1482 DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
1483 BrowserDistribution* dist = product.distribution();
1485 if (!product.is_chrome() || !installer_state.system_install()) {
1486 const char* install_level =
1487 installer_state.system_install() ? "system" : "user";
1488 VLOG(1) << "No Active Setup processing to do for " << install_level
1489 << "-level " << dist->GetDisplayName();
1490 return;
1492 DCHECK(installer_state.RequiresActiveSetup());
1494 const HKEY root = HKEY_LOCAL_MACHINE;
1495 const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist));
1497 VLOG(1) << "Adding registration items for Active Setup.";
1498 list->AddCreateRegKeyWorkItem(
1499 root, active_setup_path, WorkItem::kWow64Default);
1500 list->AddSetRegValueWorkItem(root,
1501 active_setup_path,
1502 WorkItem::kWow64Default,
1503 L"",
1504 dist->GetDisplayName(),
1505 true);
1507 base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
1508 new_version).Append(kActiveSetupExe));
1509 CommandLine cmd(active_setup_exe);
1510 cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
1511 cmd.AppendSwitch(installer::switches::kVerboseLogging);
1512 cmd.AppendSwitch(installer::switches::kSystemLevel);
1513 product.AppendProductFlags(&cmd);
1514 list->AddSetRegValueWorkItem(root,
1515 active_setup_path,
1516 WorkItem::kWow64Default,
1517 L"StubPath",
1518 cmd.GetCommandLineString(),
1519 true);
1521 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1522 // resource.
1523 list->AddSetRegValueWorkItem(root,
1524 active_setup_path,
1525 WorkItem::kWow64Default,
1526 L"Localized Name",
1527 dist->GetDisplayName(),
1528 true);
1530 list->AddSetRegValueWorkItem(root,
1531 active_setup_path,
1532 WorkItem::kWow64Default,
1533 L"IsInstalled",
1534 static_cast<DWORD>(1U),
1535 true);
1537 list->AddSetRegValueWorkItem(root,
1538 active_setup_path,
1539 WorkItem::kWow64Default,
1540 L"Version",
1541 kActiveSetupVersion,
1542 true);
1545 void AddDeleteOldIELowRightsPolicyWorkItems(
1546 const InstallerState& installer_state,
1547 WorkItemList* install_list) {
1548 DCHECK(install_list);
1550 base::string16 key_path;
1551 GetOldIELowRightsElevationPolicyKeyPath(&key_path);
1552 install_list->AddDeleteRegKeyWorkItem(
1553 installer_state.root_key(), key_path, WorkItem::kWow64Default);
1556 void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
1557 const Product& product,
1558 CommandLine* uninstall_cmd) {
1559 DCHECK(uninstall_cmd);
1561 uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
1563 // Append the product-specific uninstall flags.
1564 product.AppendProductFlags(uninstall_cmd);
1565 if (installer_state.is_msi())
1566 uninstall_cmd->AppendSwitch(installer::switches::kMsi);
1567 if (installer_state.system_install())
1568 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
1569 if (installer_state.verbose_logging())
1570 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
1573 void RefreshElevationPolicy() {
1574 const wchar_t kIEFrameDll[] = L"ieframe.dll";
1575 const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
1577 HMODULE ieframe = LoadLibrary(kIEFrameDll);
1578 if (ieframe) {
1579 typedef HRESULT (__stdcall *IERefreshPolicy)();
1580 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
1581 GetProcAddress(ieframe, kIERefreshPolicy));
1583 if (ie_refresh_policy) {
1584 ie_refresh_policy();
1585 } else {
1586 VLOG(1) << kIERefreshPolicy << " not supported.";
1589 FreeLibrary(ieframe);
1590 } else {
1591 VLOG(1) << "Cannot load " << kIEFrameDll;
1595 void AddOsUpgradeWorkItems(const InstallerState& installer_state,
1596 const base::FilePath& setup_path,
1597 const Version& new_version,
1598 const Product& product,
1599 WorkItemList* install_list) {
1600 const HKEY root_key = installer_state.root_key();
1601 base::string16 cmd_key(
1602 GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
1604 if (installer_state.operation() == InstallerState::UNINSTALL) {
1605 install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
1606 ->set_log_message("Removing OS upgrade command");
1607 } else {
1608 // Register with Google Update to have setup.exe --on-os-upgrade called on
1609 // OS upgrade.
1610 CommandLine cmd_line(installer_state
1611 .GetInstallerDirectory(new_version)
1612 .Append(setup_path.BaseName()));
1613 // Add the main option to indicate OS upgrade flow.
1614 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
1615 // Add product-specific options.
1616 product.AppendProductFlags(&cmd_line);
1617 if (installer_state.system_install())
1618 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1619 // Log everything for now.
1620 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1622 AppCommand cmd(cmd_line.GetCommandLineString());
1623 cmd.set_is_auto_run_on_os_upgrade(true);
1624 cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
1628 void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state,
1629 const base::FilePath& setup_path,
1630 const Version& new_version,
1631 const Product& product,
1632 WorkItemList* work_item_list) {
1633 const HKEY root_key = installer_state.root_key();
1634 base::string16 cmd_key(
1635 GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance));
1636 if (installer_state.operation() == InstallerState::UNINSTALL) {
1637 work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key, KEY_WOW64_32KEY)
1638 ->set_log_message("Removing query EULA acceptance command");
1639 } else {
1640 CommandLine cmd_line(installer_state
1641 .GetInstallerDirectory(new_version)
1642 .Append(setup_path.BaseName()));
1643 cmd_line.AppendSwitch(switches::kQueryEULAAcceptance);
1644 if (installer_state.system_install())
1645 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1646 if (installer_state.verbose_logging())
1647 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1648 AppCommand cmd(cmd_line.GetCommandLineString());
1649 cmd.set_is_web_accessible(true);
1650 cmd.set_is_run_as_user(true);
1651 cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
1655 void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
1656 WorkItemList* work_item_list) {
1657 DCHECK(work_item_list);
1659 base::string16 cmd_key(
1660 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1661 BrowserDistribution::CHROME_BINARIES),
1662 kCmdQuickEnableCf));
1664 // Unconditionally remove the legacy Quick Enable command from the binaries.
1665 // Do this even if multi-install Chrome isn't installed to ensure that it is
1666 // not left behind in any case.
1667 work_item_list->AddDeleteRegKeyWorkItem(
1668 installer_state.root_key(), cmd_key, KEY_WOW64_32KEY)
1669 ->set_log_message("removing " + base::UTF16ToASCII(kCmdQuickEnableCf) +
1670 " command");
1673 } // namespace installer