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