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.
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"
16 #include "base/bind.h"
17 #include "base/command_line.h"
18 #include "base/file_util.h"
19 #include "base/files/file_path.h"
20 #include "base/logging.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/path_service.h"
23 #include "base/strings/string16.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/version.h"
27 #include "base/win/registry.h"
28 #include "base/win/scoped_comptr.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/common/chrome_constants.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/installer/setup/install.h"
33 #include "chrome/installer/setup/setup_constants.h"
34 #include "chrome/installer/setup/setup_util.h"
35 #include "chrome/installer/util/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/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
;
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()) {
102 base::FilePath
old_dll_path(installer_state
.target_path().AppendASCII(
103 old_version
->GetString()));
105 installer::AddRegisterComDllWorkItems(old_dll_path
,
107 installer_state
.system_install(),
113 base::FilePath
dll_path(installer_state
.target_path().AppendASCII(
114 new_version
.GetString()));
115 installer::AddRegisterComDllWorkItems(dll_path
,
117 installer_state
.system_install(),
119 false, // Must succeed.
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(),
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
164 if (temp_path
.IsParent(archive_path
)) {
165 install_list
->AddMoveTreeWorkItem(archive_path
.value(),
168 WorkItem::ALWAYS_MOVE
);
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(),
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])
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
,
205 const char* command_with_parameter
,
206 WorkItemList
* work_item_list
) {
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(
217 installer_state
.root_key(), full_cmd_key
)->set_log_message(
218 "removing " + base::UTF16ToASCII(command_key
) + " command");
220 CommandLine
cmd_line(installer_state
.target_path().Append(app
));
221 cmd_line
.AppendSwitchASCII(command_with_parameter
, "%1");
223 AppCommand
cmd(cmd_line
.GetCommandLineString());
224 cmd
.set_sends_pings(true);
225 cmd
.set_is_web_accessible(true);
226 cmd
.set_is_run_as_user(true);
227 cmd
.AddWorkItems(installer_state
.root_key(), full_cmd_key
, work_item_list
);
231 void AddInstallAppCommandWorkItems(const InstallerState
& installer_state
,
232 const InstallationState
& machine_state
,
233 const Version
& new_version
,
234 const Product
& product
,
235 WorkItemList
* work_item_list
) {
236 DCHECK(product
.is_chrome_app_host());
237 AddCommandWithParameterWorkItems(installer_state
, machine_state
, new_version
,
238 product
, kCmdInstallApp
,
239 installer::kChromeAppHostExe
,
240 ::switches::kInstallFromWebstore
,
244 void AddInstallExtensionCommandWorkItem(const InstallerState
& installer_state
,
245 const InstallationState
& machine_state
,
246 const base::FilePath
& setup_path
,
247 const Version
& new_version
,
248 const Product
& product
,
249 WorkItemList
* work_item_list
) {
250 DCHECK(product
.is_chrome());
251 AddCommandWithParameterWorkItems(installer_state
, machine_state
, new_version
,
252 product
, kCmdInstallExtension
,
253 installer::kChromeExe
,
254 ::switches::kLimitedInstallFromWebstore
,
258 // Returns the basic CommandLine to setup.exe for a quick-enable operation on
259 // the binaries. This will unconditionally include --multi-install as well as
260 // --verbose-logging if the current installation was launched with
261 // --verbose-logging. |setup_path| and |new_version| are optional only when
262 // the operation is an uninstall.
263 CommandLine
GetGenericQuickEnableCommand(
264 const InstallerState
& installer_state
,
265 const InstallationState
& machine_state
,
266 const base::FilePath
& setup_path
,
267 const Version
& new_version
) {
268 // Only valid for multi-install operations.
269 DCHECK(installer_state
.is_multi_install());
270 // Only valid when Chrome Binaries aren't being uninstalled.
271 DCHECK(installer_state
.operation() != InstallerState::UNINSTALL
||
272 !installer_state
.FindProduct(BrowserDistribution::CHROME_BINARIES
));
273 // setup_path and new_version are required when not uninstalling.
274 DCHECK(installer_state
.operation() == InstallerState::UNINSTALL
||
275 (!setup_path
.empty() && new_version
.IsValid()));
277 // The path to setup.exe contains the version of the Chrome Binaries, so it
278 // takes a little work to get it right.
279 base::FilePath binaries_setup_path
;
280 if (installer_state
.operation() == InstallerState::UNINSTALL
) {
281 // One or more products are being uninstalled, but not Chrome Binaries.
282 // Use the path to the currently installed Chrome Binaries' setup.exe.
283 const ProductState
* product_state
= machine_state
.GetProductState(
284 installer_state
.system_install(),
285 BrowserDistribution::CHROME_BINARIES
);
286 DCHECK(product_state
);
287 binaries_setup_path
= product_state
->uninstall_command().GetProgram();
289 // Chrome Binaries are being installed, updated, or otherwise operated on.
290 // Use the path to the given |setup_path| in the normal location of
291 // multi-install Chrome Binaries of the given |version|.
292 binaries_setup_path
= installer_state
.GetInstallerDirectory(new_version
)
293 .Append(setup_path
.BaseName());
295 DCHECK(!binaries_setup_path
.empty());
297 CommandLine
cmd_line(binaries_setup_path
);
298 cmd_line
.AppendSwitch(switches::kMultiInstall
);
299 if (installer_state
.verbose_logging())
300 cmd_line
.AppendSwitch(switches::kVerboseLogging
);
304 // Adds work items to add the "quick-enable-application-host" command to the
305 // multi-installer binaries' version key on the basis of the current operation
306 // (represented in |installer_state|) and the pre-existing machine configuration
307 // (represented in |machine_state|).
308 void AddQuickEnableApplicationLauncherWorkItems(
309 const InstallerState
& installer_state
,
310 const InstallationState
& machine_state
,
311 const base::FilePath
& setup_path
,
312 const Version
& new_version
,
313 WorkItemList
* work_item_list
) {
314 DCHECK(work_item_list
);
316 bool will_have_chrome_binaries
=
317 WillProductBePresentAfterSetup(installer_state
, machine_state
,
318 BrowserDistribution::CHROME_BINARIES
);
320 // For system-level binaries there is no way to keep the command state in sync
321 // with the installation/uninstallation of the Application Launcher (which is
322 // always at user-level). So we do not try to remove the command, i.e., it
323 // will always be installed if the Chrome Binaries are installed.
324 if (will_have_chrome_binaries
) {
325 base::string16
cmd_key(
326 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
327 BrowserDistribution::CHROME_BINARIES
),
328 kCmdQuickEnableApplicationHost
));
329 CommandLine
cmd_line(GetGenericQuickEnableCommand(installer_state
,
333 // kMultiInstall and kVerboseLogging were processed above.
334 cmd_line
.AppendSwitch(switches::kChromeAppLauncher
);
335 cmd_line
.AppendSwitch(switches::kEnsureGoogleUpdatePresent
);
336 AppCommand
cmd(cmd_line
.GetCommandLineString());
337 cmd
.set_sends_pings(true);
338 cmd
.set_is_web_accessible(true);
339 cmd
.set_is_run_as_user(true);
340 cmd
.AddWorkItems(installer_state
.root_key(), cmd_key
, work_item_list
);
344 void AddProductSpecificWorkItems(const InstallationState
& original_state
,
345 const InstallerState
& installer_state
,
346 const base::FilePath
& setup_path
,
347 const Version
& new_version
,
348 WorkItemList
* list
) {
349 const Products
& products
= installer_state
.products();
350 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
352 const Product
& p
= **it
;
353 if (p
.is_chrome_app_host()) {
354 AddInstallAppCommandWorkItems(installer_state
, original_state
,
355 new_version
, p
, list
);
358 AddOsUpgradeWorkItems(installer_state
, setup_path
, new_version
, p
,
360 AddInstallExtensionCommandWorkItem(installer_state
, original_state
,
361 setup_path
, new_version
, p
, list
);
363 if (p
.is_chrome_binaries()) {
364 AddQueryEULAAcceptanceWorkItems(
365 installer_state
, setup_path
, new_version
, p
, list
);
366 AddQuickEnableChromeFrameWorkItems(installer_state
, list
);
367 AddQuickEnableApplicationLauncherWorkItems(
368 installer_state
, original_state
, setup_path
, new_version
, list
);
373 // This is called when an MSI installation is run. It may be that a user is
374 // attempting to install the MSI on top of a non-MSI managed installation.
375 // If so, try and remove any existing uninstallation shortcuts, as we want the
376 // uninstall to be managed entirely by the MSI machinery (accessible via the
377 // Add/Remove programs dialog).
378 void AddDeleteUninstallShortcutsForMSIWorkItems(
379 const InstallerState
& installer_state
,
380 const Product
& product
,
381 const base::FilePath
& temp_path
,
382 WorkItemList
* work_item_list
) {
383 DCHECK(installer_state
.is_msi())
384 << "This must only be called for MSI installations!";
386 // First attempt to delete the old installation's ARP dialog entry.
387 HKEY reg_root
= installer_state
.root_key();
388 base::string16
uninstall_reg(product
.distribution()->GetUninstallRegPath());
390 WorkItem
* delete_reg_key
= work_item_list
->AddDeleteRegKeyWorkItem(
391 reg_root
, uninstall_reg
);
392 delete_reg_key
->set_ignore_failure(true);
394 // Then attempt to delete the old installation's start menu shortcut.
395 base::FilePath uninstall_link
;
396 if (installer_state
.system_install()) {
397 PathService::Get(base::DIR_COMMON_START_MENU
, &uninstall_link
);
399 PathService::Get(base::DIR_START_MENU
, &uninstall_link
);
402 if (uninstall_link
.empty()) {
403 LOG(ERROR
) << "Failed to get location for shortcut.";
405 uninstall_link
= uninstall_link
.Append(
406 product
.distribution()->GetStartMenuShortcutSubfolder(
407 BrowserDistribution::SUBFOLDER_CHROME
));
408 uninstall_link
= uninstall_link
.Append(
409 product
.distribution()->GetUninstallLinkName() + installer::kLnkExt
);
410 VLOG(1) << "Deleting old uninstall shortcut (if present): "
411 << uninstall_link
.value();
412 WorkItem
* delete_link
= work_item_list
->AddDeleteTreeWorkItem(
413 uninstall_link
, temp_path
);
414 delete_link
->set_ignore_failure(true);
415 delete_link
->set_log_message(
416 "Failed to delete old uninstall shortcut.");
420 // Adds Chrome specific install work items to |install_list|.
421 // |current_version| can be NULL to indicate no Chrome is currently installed.
422 void AddChromeWorkItems(const InstallationState
& original_state
,
423 const InstallerState
& installer_state
,
424 const base::FilePath
& setup_path
,
425 const base::FilePath
& archive_path
,
426 const base::FilePath
& src_path
,
427 const base::FilePath
& temp_path
,
428 const Version
* current_version
,
429 const Version
& new_version
,
430 WorkItemList
* install_list
) {
431 const base::FilePath
& target_path
= installer_state
.target_path();
433 if (current_version
) {
434 // Delete the archive from an existing install to save some disk space. We
435 // make this an unconditional work item since there's no need to roll this
436 // back; if installation fails we'll be moved to the "-full" channel anyway.
437 base::FilePath
old_installer_dir(
438 installer_state
.GetInstallerDirectory(*current_version
));
439 base::FilePath
old_archive(
440 old_installer_dir
.Append(installer::kChromeArchive
));
441 // Don't delete the archive that we are actually installing from.
442 if (archive_path
!= old_archive
) {
443 install_list
->AddDeleteTreeWorkItem(old_archive
, temp_path
)->
444 set_ignore_failure(true);
448 // Delete any new_chrome.exe if present (we will end up creating a new one
449 // if required) and then copy chrome.exe
450 base::FilePath
new_chrome_exe(target_path
.Append(installer::kChromeNewExe
));
452 install_list
->AddDeleteTreeWorkItem(new_chrome_exe
, temp_path
);
454 // TODO(grt): Remove this check in M35.
455 if (installer_state
.IsChromeFrameRunning(original_state
)) {
456 VLOG(1) << "Chrome Frame in use. Copying to new_chrome.exe";
457 install_list
->AddCopyTreeWorkItem(
458 src_path
.Append(installer::kChromeExe
).value(),
459 new_chrome_exe
.value(),
463 install_list
->AddCopyTreeWorkItem(
464 src_path
.Append(installer::kChromeExe
).value(),
465 target_path
.Append(installer::kChromeExe
).value(),
467 WorkItem::NEW_NAME_IF_IN_USE
,
468 new_chrome_exe
.value());
471 // Extra executable for 64 bit systems.
472 // NOTE: We check for "not disabled" so that if the API call fails, we play it
473 // safe and copy the executable anyway.
474 // NOTE: the file wow_helper.exe is only needed for Vista and below.
475 if (base::win::OSInfo::GetInstance()->wow64_status() !=
476 base::win::OSInfo::WOW64_DISABLED
&&
477 base::win::GetVersion() <= base::win::VERSION_VISTA
) {
478 install_list
->AddMoveTreeWorkItem(
479 src_path
.Append(installer::kWowHelperExe
).value(),
480 target_path
.Append(installer::kWowHelperExe
).value(),
482 WorkItem::ALWAYS_MOVE
);
485 // Install kVisualElementsManifest if it is present in |src_path|. No need to
486 // make this a conditional work item as if the file is not there now, it will
488 if (base::PathExists(
489 src_path
.Append(installer::kVisualElementsManifest
))) {
490 install_list
->AddMoveTreeWorkItem(
491 src_path
.Append(installer::kVisualElementsManifest
).value(),
492 target_path
.Append(installer::kVisualElementsManifest
).value(),
494 WorkItem::ALWAYS_MOVE
);
496 // We do not want to have an old VisualElementsManifest pointing to an old
497 // version directory. Delete it as there wasn't a new one to replace it.
498 install_list
->AddDeleteTreeWorkItem(
499 target_path
.Append(installer::kVisualElementsManifest
),
503 // In the past, we copied rather than moved for system level installs so that
504 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path|
505 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
506 // otherwise), there is no need to do this.
507 // Note that we pass true for check_duplicates to avoid failing on in-use
508 // repair runs if the current_version is the same as the new_version.
509 bool check_for_duplicates
= (current_version
&&
510 current_version
->Equals(new_version
));
511 install_list
->AddMoveTreeWorkItem(
512 src_path
.AppendASCII(new_version
.GetString()).value(),
513 target_path
.AppendASCII(new_version
.GetString()).value(),
515 check_for_duplicates
? WorkItem::CHECK_DUPLICATES
:
516 WorkItem::ALWAYS_MOVE
);
518 // Delete any old_chrome.exe if present (ignore failure if it's in use).
519 install_list
->AddDeleteTreeWorkItem(
520 target_path
.Append(installer::kChromeOldExe
), temp_path
)->
521 set_ignore_failure(true);
524 // Probes COM machinery to get an instance of delegate_execute.exe's
525 // CommandExecuteImpl class. This is required so that COM purges its cache of
526 // the path to the binary, which changes on updates. This callback
527 // unconditionally returns true since an install should not be aborted if the
529 bool ProbeCommandExecuteCallback(const base::string16
& command_execute_id
,
530 const CallbackWorkItem
& work_item
) {
532 if (work_item
.IsRollback())
537 HRESULT hr
= CLSIDFromString(command_execute_id
.c_str(), &class_id
);
539 LOG(DFATAL
) << "Failed converting \"" << command_execute_id
<< "\" to "
540 "CLSID; hr=0x" << std::hex
<< hr
;
542 base::win::ScopedComPtr
<IUnknown
> command_execute_impl
;
543 hr
= command_execute_impl
.CreateInstance(class_id
, NULL
,
544 CLSCTX_LOCAL_SERVER
);
545 if (hr
!= REGDB_E_CLASSNOTREG
) {
546 LOG(ERROR
) << "Unexpected result creating CommandExecuteImpl; hr=0x"
554 void AddUninstallDelegateExecuteWorkItems(
556 const base::string16
& delegate_execute_path
,
557 WorkItemList
* list
) {
558 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
560 list
->AddDeleteRegKeyWorkItem(root
, delegate_execute_path
);
562 // In the past, the ICommandExecuteImpl interface and a TypeLib were both
563 // registered. Remove these since this operation may be updating a machine
564 // that had the old registrations.
565 list
->AddDeleteRegKeyWorkItem(root
,
566 L
"Software\\Classes\\Interface\\"
567 L
"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}");
568 list
->AddDeleteRegKeyWorkItem(root
,
569 L
"Software\\Classes\\TypeLib\\"
570 #if defined(GOOGLE_CHROME_BUILD)
571 L
"{4E805ED8-EBA0-4601-9681-12815A56EBFD}"
573 L
"{7779FB70-B399-454A-AA1A-BAA850032B10}"
578 // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
579 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by
580 // mistake (with the same GUID as Chrome). The fix stopped registering the bad
581 // value, but didn't delete it. This is a problem for users who had installed
582 // Canary before 20.0.1106.0 and now have a system-level Chrome, as the
583 // left-behind Canary registrations in HKCU mask the HKLM registrations for the
584 // same GUID. Cleanup those registrations if they still exist and belong to this
585 // Canary (i.e., the registered delegate_execute's path is under |target_path|).
586 void CleanupBadCanaryDelegateExecuteRegistration(
587 const base::FilePath
& target_path
,
588 WorkItemList
* list
) {
589 base::string16
google_chrome_delegate_execute_path(
590 L
"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
591 base::string16
google_chrome_local_server_32(
592 google_chrome_delegate_execute_path
+ L
"\\LocalServer32");
594 RegKey local_server_32_key
;
595 base::string16 registered_server
;
596 if (local_server_32_key
.Open(HKEY_CURRENT_USER
,
597 google_chrome_local_server_32
.c_str(),
598 KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
599 local_server_32_key
.ReadValue(L
"ServerExecutable",
600 ®istered_server
) == ERROR_SUCCESS
&&
601 target_path
.IsParent(base::FilePath(registered_server
))) {
602 scoped_ptr
<WorkItemList
> no_rollback_list(
603 WorkItem::CreateNoRollbackWorkItemList());
604 AddUninstallDelegateExecuteWorkItems(
605 HKEY_CURRENT_USER
, google_chrome_delegate_execute_path
,
606 no_rollback_list
.get());
607 list
->AddWorkItem(no_rollback_list
.release());
608 VLOG(1) << "Added deletion items for bad Canary registrations.";
614 // This method adds work items to create (or update) Chrome uninstall entry in
615 // either the Control Panel->Add/Remove Programs list or in the Omaha client
616 // state key if running under an MSI installer.
617 void AddUninstallShortcutWorkItems(const InstallerState
& installer_state
,
618 const base::FilePath
& setup_path
,
619 const Version
& new_version
,
620 const Product
& product
,
621 WorkItemList
* install_list
) {
622 HKEY reg_root
= installer_state
.root_key();
623 BrowserDistribution
* browser_dist
= product
.distribution();
624 DCHECK(browser_dist
);
626 // When we are installed via an MSI, we need to store our uninstall strings
627 // in the Google Update client state key. We do this even for non-MSI
628 // managed installs to avoid breaking the edge case whereby an MSI-managed
629 // install is updated by a non-msi installer (which would confuse the MSI
630 // machinery if these strings were not also updated). The UninstallString
631 // value placed in the client state key is also used by the mini_installer to
632 // locate the setup.exe instance used for binary patching.
633 // Do not quote the command line for the MSI invocation.
634 base::FilePath
install_path(installer_state
.target_path());
635 base::FilePath
installer_path(
636 installer_state
.GetInstallerDirectory(new_version
));
637 installer_path
= installer_path
.Append(setup_path
.BaseName());
639 CommandLine
uninstall_arguments(CommandLine::NO_PROGRAM
);
640 AppendUninstallCommandLineFlags(installer_state
, product
,
641 &uninstall_arguments
);
643 base::string16
update_state_key(browser_dist
->GetStateKey());
644 install_list
->AddCreateRegKeyWorkItem(reg_root
, update_state_key
);
645 install_list
->AddSetRegValueWorkItem(reg_root
, update_state_key
,
646 installer::kUninstallStringField
, installer_path
.value(), true);
647 install_list
->AddSetRegValueWorkItem(reg_root
, update_state_key
,
648 installer::kUninstallArgumentsField
,
649 uninstall_arguments
.GetCommandLineString(), true);
651 // MSI installations will manage their own uninstall shortcuts.
652 if (!installer_state
.is_msi() && product
.ShouldCreateUninstallEntry()) {
653 // We need to quote the command line for the Add/Remove Programs dialog.
654 CommandLine
quoted_uninstall_cmd(installer_path
);
655 DCHECK_EQ(quoted_uninstall_cmd
.GetCommandLineString()[0], '"');
656 quoted_uninstall_cmd
.AppendArguments(uninstall_arguments
, false);
658 base::string16 uninstall_reg
= browser_dist
->GetUninstallRegPath();
659 install_list
->AddCreateRegKeyWorkItem(reg_root
, uninstall_reg
);
660 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
661 installer::kUninstallDisplayNameField
, browser_dist
->GetDisplayName(),
663 install_list
->AddSetRegValueWorkItem(reg_root
,
664 uninstall_reg
, installer::kUninstallStringField
,
665 quoted_uninstall_cmd
.GetCommandLineString(), true);
666 install_list
->AddSetRegValueWorkItem(reg_root
,
669 install_path
.value(),
672 BrowserDistribution
* dist
= product
.distribution();
673 base::string16 chrome_icon
= ShellUtil::FormatIconLocation(
674 install_path
.Append(dist
->GetIconFilename()).value(),
675 dist
->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME
));
676 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
677 L
"DisplayIcon", chrome_icon
, true);
678 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
679 L
"NoModify", static_cast<DWORD
>(1),
681 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
682 L
"NoRepair", static_cast<DWORD
>(1),
685 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
687 browser_dist
->GetPublisherName(),
689 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
691 ASCIIToWide(new_version
.GetString()),
693 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
695 ASCIIToWide(new_version
.GetString()),
697 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
699 InstallUtil::GetCurrentDate(),
702 const std::vector
<uint16
>& version_components
= new_version
.components();
703 if (version_components
.size() == 4) {
704 // Our version should be in major.minor.build.rev.
705 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
706 L
"VersionMajor", static_cast<DWORD
>(version_components
[2]), true);
707 install_list
->AddSetRegValueWorkItem(reg_root
, uninstall_reg
,
708 L
"VersionMinor", static_cast<DWORD
>(version_components
[3]), true);
713 // Create Version key for a product (if not already present) and sets the new
714 // product version as the last step.
715 void AddVersionKeyWorkItems(HKEY root
,
716 BrowserDistribution
* dist
,
717 const Version
& new_version
,
718 bool add_language_identifier
,
719 WorkItemList
* list
) {
720 // Create Version key for each distribution (if not already present) and set
721 // the new product version as the last step.
722 base::string16
version_key(dist
->GetVersionKey());
723 list
->AddCreateRegKeyWorkItem(root
, version_key
);
725 base::string16
product_name(dist
->GetDisplayName());
726 list
->AddSetRegValueWorkItem(root
, version_key
, google_update::kRegNameField
,
727 product_name
, true); // overwrite name also
728 list
->AddSetRegValueWorkItem(root
, version_key
,
729 google_update::kRegOopcrashesField
,
730 static_cast<DWORD
>(1),
731 false); // set during first install
732 if (add_language_identifier
) {
733 // Write the language identifier of the current translation. Omaha's set of
734 // languages is a superset of Chrome's set of translations with this one
735 // exception: what Chrome calls "en-us", Omaha calls "en". sigh.
736 base::string16
language(GetCurrentTranslation());
737 if (LowerCaseEqualsASCII(language
, "en-us"))
739 list
->AddSetRegValueWorkItem(root
, version_key
,
740 google_update::kRegLangField
, language
,
741 false); // do not overwrite language
743 list
->AddSetRegValueWorkItem(root
, version_key
,
744 google_update::kRegVersionField
,
745 ASCIIToWide(new_version
.GetString()),
746 true); // overwrite version
749 // Mirror oeminstall the first time anything is installed multi. There is no
750 // need to update the value on future install/update runs since this value never
751 // changes. Note that the value is removed by Google Update after EULA
752 // acceptance is processed.
753 void AddOemInstallWorkItems(const InstallationState
& original_state
,
754 const InstallerState
& installer_state
,
755 WorkItemList
* install_list
) {
756 DCHECK(installer_state
.is_multi_install());
757 const bool system_install
= installer_state
.system_install();
758 if (!original_state
.GetProductState(system_install
,
759 BrowserDistribution::CHROME_BINARIES
)) {
760 const HKEY root_key
= installer_state
.root_key();
761 base::string16
multi_key(
762 installer_state
.multi_package_binaries_distribution()->GetStateKey());
764 // Copy the value from Chrome unless Chrome isn't installed or being
766 BrowserDistribution::Type source_type
;
767 if (installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
)) {
768 source_type
= BrowserDistribution::CHROME_BROWSER
;
769 } else if (!installer_state
.products().empty()) {
770 // Pick a product, any product.
771 source_type
= installer_state
.products()[0]->distribution()->GetType();
773 // Nothing is being installed? Entirely unexpected, so do no harm.
774 LOG(ERROR
) << "No products found in AddOemInstallWorkItems";
777 const ProductState
* source_product
=
778 original_state
.GetNonVersionedProductState(system_install
, source_type
);
780 base::string16 oem_install
;
781 if (source_product
->GetOemInstall(&oem_install
)) {
782 VLOG(1) << "Mirroring oeminstall=\"" << oem_install
<< "\" from "
783 << BrowserDistribution::GetSpecificDistribution(source_type
)->
785 install_list
->AddCreateRegKeyWorkItem(root_key
, multi_key
);
786 // Always overwrite an old value.
787 install_list
->AddSetRegValueWorkItem(root_key
, multi_key
,
788 google_update::kRegOemInstallField
,
791 // Clear any old value.
792 install_list
->AddDeleteRegValueWorkItem(
793 root_key
, multi_key
, google_update::kRegOemInstallField
);
798 // Mirror eulaaccepted the first time anything is installed multi. There is no
799 // need to update the value on future install/update runs since
800 // GoogleUpdateSettings::SetEULAConsent will modify the value for both the
801 // relevant product and for the binaries.
802 void AddEulaAcceptedWorkItems(const InstallationState
& original_state
,
803 const InstallerState
& installer_state
,
804 WorkItemList
* install_list
) {
805 DCHECK(installer_state
.is_multi_install());
806 const bool system_install
= installer_state
.system_install();
807 if (!original_state
.GetProductState(system_install
,
808 BrowserDistribution::CHROME_BINARIES
)) {
809 const HKEY root_key
= installer_state
.root_key();
810 base::string16
multi_key(
811 installer_state
.multi_package_binaries_distribution()->GetStateKey());
813 // Copy the value from the product with the greatest value.
814 bool have_eula_accepted
= false;
815 BrowserDistribution::Type product_type
;
817 const Products
& products
= installer_state
.products();
818 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
820 const Product
& product
= **it
;
821 if (product
.is_chrome_binaries())
823 DWORD dword_value
= 0;
824 BrowserDistribution::Type this_type
= product
.distribution()->GetType();
825 const ProductState
* product_state
=
826 original_state
.GetNonVersionedProductState(
827 system_install
, this_type
);
828 if (product_state
->GetEulaAccepted(&dword_value
) &&
829 (!have_eula_accepted
|| dword_value
> eula_accepted
)) {
830 have_eula_accepted
= true;
831 eula_accepted
= dword_value
;
832 product_type
= this_type
;
836 if (have_eula_accepted
) {
837 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted
<< " from "
838 << BrowserDistribution::GetSpecificDistribution(product_type
)->
840 install_list
->AddCreateRegKeyWorkItem(root_key
, multi_key
);
841 install_list
->AddSetRegValueWorkItem(
842 root_key
, multi_key
, google_update::kRegEULAAceptedField
,
843 eula_accepted
, true);
845 // Clear any old value.
846 install_list
->AddDeleteRegValueWorkItem(
847 root_key
, multi_key
, google_update::kRegEULAAceptedField
);
852 // Adds work items that make registry adjustments for Google Update.
853 void AddGoogleUpdateWorkItems(const InstallationState
& original_state
,
854 const InstallerState
& installer_state
,
855 WorkItemList
* install_list
) {
856 // Is a multi-install product being installed or over-installed?
857 if (installer_state
.operation() != InstallerState::MULTI_INSTALL
&&
858 installer_state
.operation() != InstallerState::MULTI_UPDATE
) {
859 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state
.operation();
863 const bool system_install
= installer_state
.system_install();
864 const HKEY root_key
= installer_state
.root_key();
865 base::string16
multi_key(
866 installer_state
.multi_package_binaries_distribution()->GetStateKey());
868 // For system-level installs, make sure the ClientStateMedium key for the
870 if (system_install
) {
871 install_list
->AddCreateRegKeyWorkItem(
873 installer_state
.multi_package_binaries_distribution()->
874 GetStateMediumKey().c_str());
877 // Creating the ClientState key for binaries, if we're migrating to multi then
878 // copy over Chrome's brand code if it has one.
879 if (installer_state
.state_type() != BrowserDistribution::CHROME_BINARIES
) {
880 const ProductState
* chrome_product_state
=
881 original_state
.GetNonVersionedProductState(
882 system_install
, BrowserDistribution::CHROME_BROWSER
);
884 const base::string16
& brand(chrome_product_state
->brand());
885 if (!brand
.empty()) {
886 install_list
->AddCreateRegKeyWorkItem(root_key
, multi_key
);
887 // Write Chrome's brand code to the multi key. Never overwrite the value
888 // if one is already present (although this shouldn't happen).
889 install_list
->AddSetRegValueWorkItem(root_key
,
891 google_update::kRegBrandField
,
897 AddOemInstallWorkItems(original_state
, installer_state
, install_list
);
898 AddEulaAcceptedWorkItems(original_state
, installer_state
, install_list
);
899 AddUsageStatsWorkItems(original_state
, installer_state
, install_list
);
901 // TODO(grt): check for other keys/values we should put in the package's
902 // ClientState and/or Clients key.
905 void AddUsageStatsWorkItems(const InstallationState
& original_state
,
906 const InstallerState
& installer_state
,
907 WorkItemList
* install_list
) {
908 DCHECK(installer_state
.operation() == InstallerState::MULTI_INSTALL
||
909 installer_state
.operation() == InstallerState::MULTI_UPDATE
);
911 HKEY root_key
= installer_state
.root_key();
912 bool value_found
= false;
913 DWORD usagestats
= 0;
914 const Products
& products
= installer_state
.products();
916 // Search for an existing usagestats value for any product.
917 for (Products::const_iterator scan
= products
.begin(), end
= products
.end();
918 !value_found
&& scan
!= end
; ++scan
) {
919 if ((*scan
)->is_chrome_binaries())
921 BrowserDistribution
* dist
= (*scan
)->distribution();
922 const ProductState
* product_state
=
923 original_state
.GetNonVersionedProductState(
924 installer_state
.system_install(), dist
->GetType());
925 value_found
= product_state
->GetUsageStats(&usagestats
);
928 // If a value was found, write it in the appropriate location for the
929 // binaries and remove all values from the products.
931 base::string16
state_key(
932 installer_state
.multi_package_binaries_distribution()->GetStateKey());
933 install_list
->AddCreateRegKeyWorkItem(root_key
, state_key
);
934 // Overwrite any existing value so that overinstalls (where Omaha writes a
935 // new value into a product's state key) pick up the correct value.
936 install_list
->AddSetRegValueWorkItem(root_key
, state_key
,
937 google_update::kRegUsageStatsField
,
940 for (Products::const_iterator scan
= products
.begin(), end
= products
.end();
941 scan
!= end
; ++scan
) {
942 if ((*scan
)->is_chrome_binaries())
944 BrowserDistribution
* dist
= (*scan
)->distribution();
945 if (installer_state
.system_install()) {
946 install_list
->AddDeleteRegValueWorkItem(
947 root_key
, dist
->GetStateMediumKey(),
948 google_update::kRegUsageStatsField
);
949 // Previous versions of Chrome also wrote a value in HKCU even for
950 // system-level installs, so clean that up.
951 install_list
->AddDeleteRegValueWorkItem(
952 HKEY_CURRENT_USER
, dist
->GetStateKey(),
953 google_update::kRegUsageStatsField
);
955 install_list
->AddDeleteRegValueWorkItem(
956 root_key
, dist
->GetStateKey(), google_update::kRegUsageStatsField
);
961 bool AppendPostInstallTasks(const InstallerState
& installer_state
,
962 const base::FilePath
& setup_path
,
963 const Version
* current_version
,
964 const Version
& new_version
,
965 const base::FilePath
& temp_path
,
966 WorkItemList
* post_install_task_list
) {
967 DCHECK(post_install_task_list
);
969 HKEY root
= installer_state
.root_key();
970 const Products
& products
= installer_state
.products();
971 base::FilePath
new_chrome_exe(
972 installer_state
.target_path().Append(installer::kChromeNewExe
));
974 // Append work items that will only be executed if this was an update.
975 // We update the 'opv' value with the current version that is active,
976 // the 'cpv' value with the critical update version (if present), and the
977 // 'cmd' value with the rename command to run.
979 scoped_ptr
<WorkItemList
> in_use_update_work_items(
980 WorkItem::CreateConditionalWorkItemList(
981 new ConditionRunIfFileExists(new_chrome_exe
)));
982 in_use_update_work_items
->set_log_message("InUseUpdateWorkItemList");
984 // |critical_version| will be valid only if this in-use update includes a
985 // version considered critical relative to the version being updated.
986 Version
critical_version(installer_state
.DetermineCriticalVersion(
987 current_version
, new_version
));
988 base::FilePath
installer_path(
989 installer_state
.GetInstallerDirectory(new_version
).Append(
990 setup_path
.BaseName()));
992 CommandLine
rename(installer_path
);
993 rename
.AppendSwitch(switches::kRenameChromeExe
);
994 if (installer_state
.system_install())
995 rename
.AppendSwitch(switches::kSystemLevel
);
997 if (installer_state
.verbose_logging())
998 rename
.AppendSwitch(switches::kVerboseLogging
);
1000 base::string16 version_key
;
1001 for (size_t i
= 0; i
< products
.size(); ++i
) {
1002 BrowserDistribution
* dist
= products
[i
]->distribution();
1003 version_key
= dist
->GetVersionKey();
1005 if (current_version
) {
1006 in_use_update_work_items
->AddSetRegValueWorkItem(root
, version_key
,
1007 google_update::kRegOldVersionField
,
1008 ASCIIToWide(current_version
->GetString()), true);
1010 if (critical_version
.IsValid()) {
1011 in_use_update_work_items
->AddSetRegValueWorkItem(root
, version_key
,
1012 google_update::kRegCriticalVersionField
,
1013 ASCIIToWide(critical_version
.GetString()), true);
1015 in_use_update_work_items
->AddDeleteRegValueWorkItem(root
, version_key
,
1016 google_update::kRegCriticalVersionField
);
1019 // Adding this registry entry for all products (but the binaries) is
1020 // overkill. However, as it stands, we don't have a way to know which
1021 // product will check the key and run the command, so we add it for all.
1022 // The first to run it will perform the operation and clean up the other
1024 if (dist
->GetType() != BrowserDistribution::CHROME_BINARIES
) {
1025 CommandLine
product_rename_cmd(rename
);
1026 products
[i
]->AppendRenameFlags(&product_rename_cmd
);
1027 in_use_update_work_items
->AddSetRegValueWorkItem(
1028 root
, version_key
, google_update::kRegRenameCmdField
,
1029 product_rename_cmd
.GetCommandLineString(), true);
1033 post_install_task_list
->AddWorkItem(in_use_update_work_items
.release());
1036 // Append work items that will be executed if this was NOT an in-use update.
1038 scoped_ptr
<WorkItemList
> regular_update_work_items(
1039 WorkItem::CreateConditionalWorkItemList(
1040 new Not(new ConditionRunIfFileExists(new_chrome_exe
))));
1041 regular_update_work_items
->set_log_message("RegularUpdateWorkItemList");
1043 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1044 for (size_t i
= 0; i
< products
.size(); ++i
) {
1045 BrowserDistribution
* dist
= products
[i
]->distribution();
1046 base::string16
version_key(dist
->GetVersionKey());
1047 regular_update_work_items
->AddDeleteRegValueWorkItem(root
, version_key
,
1048 google_update::kRegOldVersionField
);
1049 regular_update_work_items
->AddDeleteRegValueWorkItem(root
, version_key
,
1050 google_update::kRegCriticalVersionField
);
1051 regular_update_work_items
->AddDeleteRegValueWorkItem(root
, version_key
,
1052 google_update::kRegRenameCmdField
);
1055 post_install_task_list
->AddWorkItem(regular_update_work_items
.release());
1058 AddRegisterComDllWorkItemsForPackage(installer_state
, current_version
,
1059 new_version
, post_install_task_list
);
1061 // If we're told that we're an MSI install, make sure to set the marker
1062 // in the client state key so that future updates do the right thing.
1063 if (installer_state
.is_msi()) {
1064 for (size_t i
= 0; i
< products
.size(); ++i
) {
1065 const Product
* product
= products
[i
];
1066 AddSetMsiMarkerWorkItem(installer_state
, product
->distribution(), true,
1067 post_install_task_list
);
1069 // We want MSI installs to take over the Add/Remove Programs shortcut.
1070 // Make a best-effort attempt to delete any shortcuts left over from
1071 // previous non-MSI installations for the same type of install (system or
1073 if (product
->ShouldCreateUninstallEntry()) {
1074 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state
, *product
,
1076 post_install_task_list
);
1084 void AddInstallWorkItems(const InstallationState
& original_state
,
1085 const InstallerState
& installer_state
,
1086 const base::FilePath
& setup_path
,
1087 const base::FilePath
& archive_path
,
1088 const base::FilePath
& src_path
,
1089 const base::FilePath
& temp_path
,
1090 const Version
* current_version
,
1091 const Version
& new_version
,
1092 WorkItemList
* install_list
) {
1093 DCHECK(install_list
);
1095 const base::FilePath
& target_path
= installer_state
.target_path();
1097 // A temp directory that work items need and the actual install directory.
1098 install_list
->AddCreateDirWorkItem(temp_path
);
1099 install_list
->AddCreateDirWorkItem(target_path
);
1101 if (installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
) ||
1102 installer_state
.FindProduct(BrowserDistribution::CHROME_BINARIES
)) {
1103 AddChromeWorkItems(original_state
,
1114 if (installer_state
.FindProduct(BrowserDistribution::CHROME_APP_HOST
)) {
1115 install_list
->AddCopyTreeWorkItem(
1116 src_path
.Append(installer::kChromeAppHostExe
).value(),
1117 target_path
.Append(installer::kChromeAppHostExe
).value(),
1123 // Copy installer in install directory
1124 AddInstallerCopyTasks(installer_state
, setup_path
, archive_path
, temp_path
,
1125 new_version
, install_list
);
1127 const HKEY root
= installer_state
.root_key();
1128 // Only set "lang" for user-level installs since for system-level, the install
1129 // language may not be related to a given user's runtime language.
1130 const bool add_language_identifier
= !installer_state
.system_install();
1132 const Products
& products
= installer_state
.products();
1133 for (Products::const_iterator it
= products
.begin(); it
< products
.end();
1135 const Product
& product
= **it
;
1137 AddUninstallShortcutWorkItems(installer_state
, setup_path
, new_version
,
1138 product
, install_list
);
1140 AddVersionKeyWorkItems(root
, product
.distribution(), new_version
,
1141 add_language_identifier
, install_list
);
1143 AddDelegateExecuteWorkItems(installer_state
, target_path
, new_version
,
1144 product
, install_list
);
1146 AddActiveSetupWorkItems(installer_state
, setup_path
, new_version
, product
,
1150 // TODO(huangs): Implement actual migration code and remove the hack below.
1151 // If installing Chrome without the legacy stand-alone App Launcher (to be
1152 // handled later), add "shadow" App Launcher registry keys so Google Update
1153 // would recognize the "dr" value in the App Launcher ClientState key.
1154 // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome.
1155 if (installer_state
.is_multi_install() &&
1156 installer_state
.FindProduct(BrowserDistribution::CHROME_BROWSER
) &&
1157 !installer_state
.FindProduct(BrowserDistribution::CHROME_APP_HOST
)) {
1158 BrowserDistribution
* shadow_app_launcher_dist
=
1159 BrowserDistribution::GetSpecificDistribution(
1160 BrowserDistribution::CHROME_APP_HOST
);
1161 AddVersionKeyWorkItems(root
, shadow_app_launcher_dist
, new_version
,
1162 add_language_identifier
, install_list
);
1165 // Add any remaining work items that involve special settings for
1167 AddProductSpecificWorkItems(original_state
, installer_state
, setup_path
,
1168 new_version
, install_list
);
1170 // Copy over brand, usagestats, and other values.
1171 AddGoogleUpdateWorkItems(original_state
, installer_state
, install_list
);
1173 // Append the tasks that run after the installation.
1174 AppendPostInstallTasks(installer_state
,
1182 void AddRegisterComDllWorkItems(const base::FilePath
& dll_folder
,
1183 const std::vector
<base::FilePath
>& dll_list
,
1186 bool ignore_failures
,
1187 WorkItemList
* work_item_list
) {
1188 DCHECK(work_item_list
);
1189 if (dll_list
.empty()) {
1190 VLOG(1) << "No COM DLLs to register";
1192 std::vector
<base::FilePath
>::const_iterator
dll_iter(dll_list
.begin());
1193 for (; dll_iter
!= dll_list
.end(); ++dll_iter
) {
1194 base::FilePath dll_path
= dll_folder
.Append(*dll_iter
);
1195 WorkItem
* work_item
= work_item_list
->AddSelfRegWorkItem(
1196 dll_path
.value(), do_register
, !system_level
);
1198 work_item
->set_ignore_failure(ignore_failures
);
1203 void AddSetMsiMarkerWorkItem(const InstallerState
& installer_state
,
1204 BrowserDistribution
* dist
,
1206 WorkItemList
* work_item_list
) {
1207 DCHECK(work_item_list
);
1208 DWORD msi_value
= set
? 1 : 0;
1209 WorkItem
* set_msi_work_item
= work_item_list
->AddSetRegValueWorkItem(
1210 installer_state
.root_key(), dist
->GetStateKey(),
1211 google_update::kRegMSIField
, msi_value
, true);
1212 DCHECK(set_msi_work_item
);
1213 set_msi_work_item
->set_ignore_failure(true);
1214 set_msi_work_item
->set_log_message("Could not write MSI marker!");
1217 void AddDelegateExecuteWorkItems(const InstallerState
& installer_state
,
1218 const base::FilePath
& target_path
,
1219 const Version
& new_version
,
1220 const Product
& product
,
1221 WorkItemList
* list
) {
1222 base::string16 handler_class_uuid
;
1223 BrowserDistribution
* dist
= product
.distribution();
1224 if (!dist
->GetCommandExecuteImplClsid(&handler_class_uuid
)) {
1225 if (InstallUtil::IsChromeSxSProcess()) {
1226 CleanupBadCanaryDelegateExecuteRegistration(target_path
, list
);
1228 VLOG(1) << "No DelegateExecute verb handler processing to do for "
1229 << dist
->GetDisplayName();
1234 HKEY root
= installer_state
.root_key();
1235 base::string16
delegate_execute_path(L
"Software\\Classes\\CLSID\\");
1236 delegate_execute_path
.append(handler_class_uuid
);
1238 // Unconditionally remove registration regardless of whether or not it is
1239 // needed since builds after r132190 included it when it wasn't strictly
1240 // necessary. Do this removal before adding in the new key to ensure that
1241 // the COM probe/flush below does its job.
1242 AddUninstallDelegateExecuteWorkItems(root
, delegate_execute_path
, list
);
1244 // Add work items to register the handler iff it is present.
1245 // See also shell_util.cc's GetProgIdEntries.
1246 if (installer_state
.operation() != InstallerState::UNINSTALL
) {
1247 VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1249 // Force COM to flush its cache containing the path to the old handler.
1250 list
->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback
,
1251 handler_class_uuid
));
1253 // The path to the exe (in the version directory).
1254 base::FilePath
delegate_execute(target_path
);
1255 if (new_version
.IsValid())
1256 delegate_execute
= delegate_execute
.AppendASCII(new_version
.GetString());
1257 delegate_execute
= delegate_execute
.Append(kDelegateExecuteExe
);
1259 // Command-line featuring the quoted path to the exe.
1260 base::string16
command(1, L
'"');
1261 command
.append(delegate_execute
.value()).append(1, L
'"');
1263 // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1264 list
->AddCreateRegKeyWorkItem(root
, delegate_execute_path
);
1265 list
->AddSetRegValueWorkItem(root
, delegate_execute_path
, L
"",
1266 L
"CommandExecuteImpl Class", true);
1267 base::string16
subkey(delegate_execute_path
);
1268 subkey
.append(L
"\\LocalServer32");
1269 list
->AddCreateRegKeyWorkItem(root
, subkey
);
1270 list
->AddSetRegValueWorkItem(root
, subkey
, L
"", command
, true);
1271 list
->AddSetRegValueWorkItem(root
, subkey
, L
"ServerExecutable",
1272 delegate_execute
.value(), true);
1274 subkey
.assign(delegate_execute_path
).append(L
"\\Programmable");
1275 list
->AddCreateRegKeyWorkItem(root
, subkey
);
1279 void AddActiveSetupWorkItems(const InstallerState
& installer_state
,
1280 const base::FilePath
& setup_path
,
1281 const Version
& new_version
,
1282 const Product
& product
,
1283 WorkItemList
* list
) {
1284 DCHECK(installer_state
.operation() != InstallerState::UNINSTALL
);
1285 BrowserDistribution
* dist
= product
.distribution();
1287 if (!product
.is_chrome() || !installer_state
.system_install()) {
1288 const char* install_level
=
1289 installer_state
.system_install() ? "system" : "user";
1290 VLOG(1) << "No Active Setup processing to do for " << install_level
1291 << "-level " << dist
->GetDisplayName();
1294 DCHECK(installer_state
.RequiresActiveSetup());
1296 const HKEY root
= HKEY_LOCAL_MACHINE
;
1297 const base::string16
active_setup_path(InstallUtil::GetActiveSetupPath(dist
));
1299 VLOG(1) << "Adding registration items for Active Setup.";
1300 list
->AddCreateRegKeyWorkItem(root
, active_setup_path
);
1301 list
->AddSetRegValueWorkItem(root
, active_setup_path
, L
"",
1302 dist
->GetDisplayName(), true);
1304 base::FilePath
active_setup_exe(installer_state
.GetInstallerDirectory(
1305 new_version
).Append(kActiveSetupExe
));
1306 CommandLine
cmd(active_setup_exe
);
1307 cmd
.AppendSwitch(installer::switches::kConfigureUserSettings
);
1308 cmd
.AppendSwitch(installer::switches::kVerboseLogging
);
1309 cmd
.AppendSwitch(installer::switches::kSystemLevel
);
1310 product
.AppendProductFlags(&cmd
);
1311 list
->AddSetRegValueWorkItem(root
, active_setup_path
, L
"StubPath",
1312 cmd
.GetCommandLineString(), true);
1314 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1316 list
->AddSetRegValueWorkItem(root
, active_setup_path
, L
"Localized Name",
1317 dist
->GetDisplayName(), true);
1319 list
->AddSetRegValueWorkItem(root
, active_setup_path
, L
"IsInstalled",
1320 static_cast<DWORD
>(1U), true);
1322 list
->AddSetRegValueWorkItem(root
, active_setup_path
, L
"Version",
1323 kActiveSetupVersion
, true);
1326 void AddDeleteOldIELowRightsPolicyWorkItems(
1327 const InstallerState
& installer_state
,
1328 WorkItemList
* install_list
) {
1329 DCHECK(install_list
);
1331 base::string16 key_path
;
1332 GetOldIELowRightsElevationPolicyKeyPath(&key_path
);
1333 install_list
->AddDeleteRegKeyWorkItem(installer_state
.root_key(), key_path
);
1336 void AppendUninstallCommandLineFlags(const InstallerState
& installer_state
,
1337 const Product
& product
,
1338 CommandLine
* uninstall_cmd
) {
1339 DCHECK(uninstall_cmd
);
1341 uninstall_cmd
->AppendSwitch(installer::switches::kUninstall
);
1343 // Append the product-specific uninstall flags.
1344 product
.AppendProductFlags(uninstall_cmd
);
1345 if (installer_state
.is_msi())
1346 uninstall_cmd
->AppendSwitch(installer::switches::kMsi
);
1347 if (installer_state
.system_install())
1348 uninstall_cmd
->AppendSwitch(installer::switches::kSystemLevel
);
1349 if (installer_state
.verbose_logging())
1350 uninstall_cmd
->AppendSwitch(installer::switches::kVerboseLogging
);
1353 void RefreshElevationPolicy() {
1354 const wchar_t kIEFrameDll
[] = L
"ieframe.dll";
1355 const char kIERefreshPolicy
[] = "IERefreshElevationPolicy";
1357 HMODULE ieframe
= LoadLibrary(kIEFrameDll
);
1359 typedef HRESULT (__stdcall
*IERefreshPolicy
)();
1360 IERefreshPolicy ie_refresh_policy
= reinterpret_cast<IERefreshPolicy
>(
1361 GetProcAddress(ieframe
, kIERefreshPolicy
));
1363 if (ie_refresh_policy
) {
1364 ie_refresh_policy();
1366 VLOG(1) << kIERefreshPolicy
<< " not supported.";
1369 FreeLibrary(ieframe
);
1371 VLOG(1) << "Cannot load " << kIEFrameDll
;
1375 void AddOsUpgradeWorkItems(const InstallerState
& installer_state
,
1376 const base::FilePath
& setup_path
,
1377 const Version
& new_version
,
1378 const Product
& product
,
1379 WorkItemList
* install_list
) {
1380 const HKEY root_key
= installer_state
.root_key();
1381 base::string16
cmd_key(
1382 GetRegCommandKey(product
.distribution(), kCmdOnOsUpgrade
));
1384 if (installer_state
.operation() == InstallerState::UNINSTALL
) {
1385 install_list
->AddDeleteRegKeyWorkItem(root_key
, cmd_key
)->
1386 set_log_message("Removing OS upgrade command");
1388 // Register with Google Update to have setup.exe --on-os-upgrade called on
1390 CommandLine
cmd_line(installer_state
1391 .GetInstallerDirectory(new_version
)
1392 .Append(setup_path
.BaseName()));
1393 // Add the main option to indicate OS upgrade flow.
1394 cmd_line
.AppendSwitch(installer::switches::kOnOsUpgrade
);
1395 // Add product-specific options.
1396 product
.AppendProductFlags(&cmd_line
);
1397 if (installer_state
.system_install())
1398 cmd_line
.AppendSwitch(installer::switches::kSystemLevel
);
1399 // Log everything for now.
1400 cmd_line
.AppendSwitch(installer::switches::kVerboseLogging
);
1402 AppCommand
cmd(cmd_line
.GetCommandLineString());
1403 cmd
.set_is_auto_run_on_os_upgrade(true);
1404 cmd
.AddWorkItems(installer_state
.root_key(), cmd_key
, install_list
);
1408 void AddQueryEULAAcceptanceWorkItems(const InstallerState
& installer_state
,
1409 const base::FilePath
& setup_path
,
1410 const Version
& new_version
,
1411 const Product
& product
,
1412 WorkItemList
* work_item_list
) {
1413 const HKEY root_key
= installer_state
.root_key();
1414 base::string16
cmd_key(
1415 GetRegCommandKey(product
.distribution(), kCmdQueryEULAAcceptance
));
1416 if (installer_state
.operation() == InstallerState::UNINSTALL
) {
1417 work_item_list
->AddDeleteRegKeyWorkItem(root_key
, cmd_key
)->
1418 set_log_message("Removing query EULA acceptance command");
1420 CommandLine
cmd_line(installer_state
1421 .GetInstallerDirectory(new_version
)
1422 .Append(setup_path
.BaseName()));
1423 cmd_line
.AppendSwitch(switches::kQueryEULAAcceptance
);
1424 if (installer_state
.system_install())
1425 cmd_line
.AppendSwitch(installer::switches::kSystemLevel
);
1426 if (installer_state
.verbose_logging())
1427 cmd_line
.AppendSwitch(installer::switches::kVerboseLogging
);
1428 AppCommand
cmd(cmd_line
.GetCommandLineString());
1429 cmd
.set_is_web_accessible(true);
1430 cmd
.set_is_run_as_user(true);
1431 cmd
.AddWorkItems(installer_state
.root_key(), cmd_key
, work_item_list
);
1435 void AddQuickEnableChromeFrameWorkItems(const InstallerState
& installer_state
,
1436 WorkItemList
* work_item_list
) {
1437 DCHECK(work_item_list
);
1439 base::string16
cmd_key(
1440 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1441 BrowserDistribution::CHROME_BINARIES
),
1442 kCmdQuickEnableCf
));
1444 // Unconditionally remove the legacy Quick Enable command from the binaries.
1445 // Do this even if multi-install Chrome isn't installed to ensure that it is
1446 // not left behind in any case.
1447 work_item_list
->AddDeleteRegKeyWorkItem(
1448 installer_state
.root_key(), cmd_key
)->set_log_message(
1449 "removing " + base::UTF16ToASCII(kCmdQuickEnableCf
) + " command");
1453 } // namespace installer