Remove old about scheme URL constants.
[chromium-blink-merge.git] / chrome / installer / setup / install_worker.cc
blob1713fe5884a2077c9cf9376eba5bce734372bab2
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/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;
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(
217 installer_state.root_key(), full_cmd_key)->set_log_message(
218 "removing " + WideToASCII(command_key) + " command");
219 } else {
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,
241 work_item_list);
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,
255 work_item_list);
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();
288 } else {
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);
301 return cmd_line;
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,
330 machine_state,
331 setup_path,
332 new_version));
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();
351 ++it) {
352 const Product& p = **it;
353 if (p.is_chrome_app_host()) {
354 AddInstallAppCommandWorkItems(installer_state, original_state,
355 new_version, p, list);
357 if (p.is_chrome()) {
358 AddOsUpgradeWorkItems(installer_state, setup_path, new_version, p,
359 list);
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);
398 } else {
399 PathService::Get(base::DIR_START_MENU, &uninstall_link);
402 if (uninstall_link.empty()) {
403 LOG(ERROR) << "Failed to get location for shortcut.";
404 } else {
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(),
460 temp_path.value(),
461 WorkItem::ALWAYS);
462 } else {
463 install_list->AddCopyTreeWorkItem(
464 src_path.Append(installer::kChromeExe).value(),
465 target_path.Append(installer::kChromeExe).value(),
466 temp_path.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(),
481 temp_path.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
487 // never be.
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(),
493 temp_path.value(),
494 WorkItem::ALWAYS_MOVE);
495 } else {
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),
500 temp_path);
503 // For the component build to work with the installer, we need to also drop
504 // chrome.exe.manifest (other manifests are already contained in the version
505 // directory in the archive so no explicit work is required for them).
506 #if defined(COMPONENT_BUILD)
507 static const base::FilePath::CharType kChromeExeManifest[] =
508 FILE_PATH_LITERAL("chrome.exe.manifest");
509 install_list->AddMoveTreeWorkItem(
510 src_path.Append(kChromeExeManifest).value(),
511 target_path.Append(kChromeExeManifest).value(),
512 temp_path.value(),
513 WorkItem::ALWAYS_MOVE);
514 #endif // defined(COMPONENT_BUILD)
516 // In the past, we copied rather than moved for system level installs so that
517 // the permissions of %ProgramFiles% would be picked up. Now that |temp_path|
518 // is in %ProgramFiles% for system level installs (and in %LOCALAPPDATA%
519 // otherwise), there is no need to do this.
520 // Note that we pass true for check_duplicates to avoid failing on in-use
521 // repair runs if the current_version is the same as the new_version.
522 bool check_for_duplicates = (current_version &&
523 current_version->Equals(new_version));
524 install_list->AddMoveTreeWorkItem(
525 src_path.AppendASCII(new_version.GetString()).value(),
526 target_path.AppendASCII(new_version.GetString()).value(),
527 temp_path.value(),
528 check_for_duplicates ? WorkItem::CHECK_DUPLICATES :
529 WorkItem::ALWAYS_MOVE);
531 // Delete any old_chrome.exe if present (ignore failure if it's in use).
532 install_list->AddDeleteTreeWorkItem(
533 target_path.Append(installer::kChromeOldExe), temp_path)->
534 set_ignore_failure(true);
537 // Probes COM machinery to get an instance of delegate_execute.exe's
538 // CommandExecuteImpl class. This is required so that COM purges its cache of
539 // the path to the binary, which changes on updates. This callback
540 // unconditionally returns true since an install should not be aborted if the
541 // probe fails.
542 bool ProbeCommandExecuteCallback(const base::string16& command_execute_id,
543 const CallbackWorkItem& work_item) {
544 // Noop on rollback.
545 if (work_item.IsRollback())
546 return true;
548 CLSID class_id = {};
550 HRESULT hr = CLSIDFromString(command_execute_id.c_str(), &class_id);
551 if (FAILED(hr)) {
552 LOG(DFATAL) << "Failed converting \"" << command_execute_id << "\" to "
553 "CLSID; hr=0x" << std::hex << hr;
554 } else {
555 base::win::ScopedComPtr<IUnknown> command_execute_impl;
556 hr = command_execute_impl.CreateInstance(class_id, NULL,
557 CLSCTX_LOCAL_SERVER);
558 if (hr != REGDB_E_CLASSNOTREG) {
559 LOG(ERROR) << "Unexpected result creating CommandExecuteImpl; hr=0x"
560 << std::hex << hr;
564 return true;
567 void AddUninstallDelegateExecuteWorkItems(
568 HKEY root,
569 const base::string16& delegate_execute_path,
570 WorkItemList* list) {
571 VLOG(1) << "Adding unregistration items for DelegateExecute verb handler in "
572 << root;
573 list->AddDeleteRegKeyWorkItem(root, delegate_execute_path);
575 // In the past, the ICommandExecuteImpl interface and a TypeLib were both
576 // registered. Remove these since this operation may be updating a machine
577 // that had the old registrations.
578 list->AddDeleteRegKeyWorkItem(root,
579 L"Software\\Classes\\Interface\\"
580 L"{0BA0D4E9-2259-4963-B9AE-A839F7CB7544}");
581 list->AddDeleteRegKeyWorkItem(root,
582 L"Software\\Classes\\TypeLib\\"
583 #if defined(GOOGLE_CHROME_BUILD)
584 L"{4E805ED8-EBA0-4601-9681-12815A56EBFD}"
585 #else
586 L"{7779FB70-B399-454A-AA1A-BAA850032B10}"
587 #endif
591 // Google Chrome Canary, between 20.0.1101.0 (crrev.com/132190) and 20.0.1106.0
592 // (exclusively -- crrev.com/132596), registered a DelegateExecute class by
593 // mistake (with the same GUID as Chrome). The fix stopped registering the bad
594 // value, but didn't delete it. This is a problem for users who had installed
595 // Canary before 20.0.1106.0 and now have a system-level Chrome, as the
596 // left-behind Canary registrations in HKCU mask the HKLM registrations for the
597 // same GUID. Cleanup those registrations if they still exist and belong to this
598 // Canary (i.e., the registered delegate_execute's path is under |target_path|).
599 void CleanupBadCanaryDelegateExecuteRegistration(
600 const base::FilePath& target_path,
601 WorkItemList* list) {
602 base::string16 google_chrome_delegate_execute_path(
603 L"Software\\Classes\\CLSID\\{5C65F4B0-3651-4514-B207-D10CB699B14B}");
604 base::string16 google_chrome_local_server_32(
605 google_chrome_delegate_execute_path + L"\\LocalServer32");
607 RegKey local_server_32_key;
608 base::string16 registered_server;
609 if (local_server_32_key.Open(HKEY_CURRENT_USER,
610 google_chrome_local_server_32.c_str(),
611 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
612 local_server_32_key.ReadValue(L"ServerExecutable",
613 &registered_server) == ERROR_SUCCESS &&
614 target_path.IsParent(base::FilePath(registered_server))) {
615 scoped_ptr<WorkItemList> no_rollback_list(
616 WorkItem::CreateNoRollbackWorkItemList());
617 AddUninstallDelegateExecuteWorkItems(
618 HKEY_CURRENT_USER, google_chrome_delegate_execute_path,
619 no_rollback_list.get());
620 list->AddWorkItem(no_rollback_list.release());
621 VLOG(1) << "Added deletion items for bad Canary registrations.";
625 } // namespace
627 // This method adds work items to create (or update) Chrome uninstall entry in
628 // either the Control Panel->Add/Remove Programs list or in the Omaha client
629 // state key if running under an MSI installer.
630 void AddUninstallShortcutWorkItems(const InstallerState& installer_state,
631 const base::FilePath& setup_path,
632 const Version& new_version,
633 const Product& product,
634 WorkItemList* install_list) {
635 HKEY reg_root = installer_state.root_key();
636 BrowserDistribution* browser_dist = product.distribution();
637 DCHECK(browser_dist);
639 // When we are installed via an MSI, we need to store our uninstall strings
640 // in the Google Update client state key. We do this even for non-MSI
641 // managed installs to avoid breaking the edge case whereby an MSI-managed
642 // install is updated by a non-msi installer (which would confuse the MSI
643 // machinery if these strings were not also updated). The UninstallString
644 // value placed in the client state key is also used by the mini_installer to
645 // locate the setup.exe instance used for binary patching.
646 // Do not quote the command line for the MSI invocation.
647 base::FilePath install_path(installer_state.target_path());
648 base::FilePath installer_path(
649 installer_state.GetInstallerDirectory(new_version));
650 installer_path = installer_path.Append(setup_path.BaseName());
652 CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
653 AppendUninstallCommandLineFlags(installer_state, product,
654 &uninstall_arguments);
656 base::string16 update_state_key(browser_dist->GetStateKey());
657 install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
658 install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
659 installer::kUninstallStringField, installer_path.value(), true);
660 install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
661 installer::kUninstallArgumentsField,
662 uninstall_arguments.GetCommandLineString(), true);
664 // MSI installations will manage their own uninstall shortcuts.
665 if (!installer_state.is_msi() && product.ShouldCreateUninstallEntry()) {
666 // We need to quote the command line for the Add/Remove Programs dialog.
667 CommandLine quoted_uninstall_cmd(installer_path);
668 DCHECK_EQ(quoted_uninstall_cmd.GetCommandLineString()[0], '"');
669 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
671 base::string16 uninstall_reg = browser_dist->GetUninstallRegPath();
672 install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
673 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
674 installer::kUninstallDisplayNameField, browser_dist->GetDisplayName(),
675 true);
676 install_list->AddSetRegValueWorkItem(reg_root,
677 uninstall_reg, installer::kUninstallStringField,
678 quoted_uninstall_cmd.GetCommandLineString(), true);
679 install_list->AddSetRegValueWorkItem(reg_root,
680 uninstall_reg,
681 L"InstallLocation",
682 install_path.value(),
683 true);
685 BrowserDistribution* dist = product.distribution();
686 base::string16 chrome_icon = ShellUtil::FormatIconLocation(
687 install_path.Append(dist->GetIconFilename()).value(),
688 dist->GetIconIndex(BrowserDistribution::SHORTCUT_CHROME));
689 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
690 L"DisplayIcon", chrome_icon, true);
691 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
692 L"NoModify", static_cast<DWORD>(1),
693 true);
694 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
695 L"NoRepair", static_cast<DWORD>(1),
696 true);
698 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
699 L"Publisher",
700 browser_dist->GetPublisherName(),
701 true);
702 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
703 L"Version",
704 ASCIIToWide(new_version.GetString()),
705 true);
706 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
707 L"DisplayVersion",
708 ASCIIToWide(new_version.GetString()),
709 true);
710 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
711 L"InstallDate",
712 InstallUtil::GetCurrentDate(),
713 false);
715 const std::vector<uint16>& version_components = new_version.components();
716 if (version_components.size() == 4) {
717 // Our version should be in major.minor.build.rev.
718 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
719 L"VersionMajor", static_cast<DWORD>(version_components[2]), true);
720 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
721 L"VersionMinor", static_cast<DWORD>(version_components[3]), true);
726 // Create Version key for a product (if not already present) and sets the new
727 // product version as the last step.
728 void AddVersionKeyWorkItems(HKEY root,
729 BrowserDistribution* dist,
730 const Version& new_version,
731 bool add_language_identifier,
732 WorkItemList* list) {
733 // Create Version key for each distribution (if not already present) and set
734 // the new product version as the last step.
735 base::string16 version_key(dist->GetVersionKey());
736 list->AddCreateRegKeyWorkItem(root, version_key);
738 base::string16 product_name(dist->GetDisplayName());
739 list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
740 product_name, true); // overwrite name also
741 list->AddSetRegValueWorkItem(root, version_key,
742 google_update::kRegOopcrashesField,
743 static_cast<DWORD>(1),
744 false); // set during first install
745 if (add_language_identifier) {
746 // Write the language identifier of the current translation. Omaha's set of
747 // languages is a superset of Chrome's set of translations with this one
748 // exception: what Chrome calls "en-us", Omaha calls "en". sigh.
749 base::string16 language(GetCurrentTranslation());
750 if (LowerCaseEqualsASCII(language, "en-us"))
751 language.resize(2);
752 list->AddSetRegValueWorkItem(root, version_key,
753 google_update::kRegLangField, language,
754 false); // do not overwrite language
756 list->AddSetRegValueWorkItem(root, version_key,
757 google_update::kRegVersionField,
758 ASCIIToWide(new_version.GetString()),
759 true); // overwrite version
762 // Mirror oeminstall the first time anything is installed multi. There is no
763 // need to update the value on future install/update runs since this value never
764 // changes. Note that the value is removed by Google Update after EULA
765 // acceptance is processed.
766 void AddOemInstallWorkItems(const InstallationState& original_state,
767 const InstallerState& installer_state,
768 WorkItemList* install_list) {
769 DCHECK(installer_state.is_multi_install());
770 const bool system_install = installer_state.system_install();
771 if (!original_state.GetProductState(system_install,
772 BrowserDistribution::CHROME_BINARIES)) {
773 const HKEY root_key = installer_state.root_key();
774 base::string16 multi_key(
775 installer_state.multi_package_binaries_distribution()->GetStateKey());
777 // Copy the value from Chrome unless Chrome isn't installed or being
778 // installed.
779 BrowserDistribution::Type source_type;
780 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) {
781 source_type = BrowserDistribution::CHROME_BROWSER;
782 } else if (!installer_state.products().empty()) {
783 // Pick a product, any product.
784 source_type = installer_state.products()[0]->distribution()->GetType();
785 } else {
786 // Nothing is being installed? Entirely unexpected, so do no harm.
787 LOG(ERROR) << "No products found in AddOemInstallWorkItems";
788 return;
790 const ProductState* source_product =
791 original_state.GetNonVersionedProductState(system_install, source_type);
793 base::string16 oem_install;
794 if (source_product->GetOemInstall(&oem_install)) {
795 VLOG(1) << "Mirroring oeminstall=\"" << oem_install << "\" from "
796 << BrowserDistribution::GetSpecificDistribution(source_type)->
797 GetDisplayName();
798 install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
799 // Always overwrite an old value.
800 install_list->AddSetRegValueWorkItem(root_key, multi_key,
801 google_update::kRegOemInstallField,
802 oem_install, true);
803 } else {
804 // Clear any old value.
805 install_list->AddDeleteRegValueWorkItem(
806 root_key, multi_key, google_update::kRegOemInstallField);
811 // Mirror eulaaccepted the first time anything is installed multi. There is no
812 // need to update the value on future install/update runs since
813 // GoogleUpdateSettings::SetEULAConsent will modify the value for both the
814 // relevant product and for the binaries.
815 void AddEulaAcceptedWorkItems(const InstallationState& original_state,
816 const InstallerState& installer_state,
817 WorkItemList* install_list) {
818 DCHECK(installer_state.is_multi_install());
819 const bool system_install = installer_state.system_install();
820 if (!original_state.GetProductState(system_install,
821 BrowserDistribution::CHROME_BINARIES)) {
822 const HKEY root_key = installer_state.root_key();
823 base::string16 multi_key(
824 installer_state.multi_package_binaries_distribution()->GetStateKey());
826 // Copy the value from the product with the greatest value.
827 bool have_eula_accepted = false;
828 BrowserDistribution::Type product_type;
829 DWORD eula_accepted;
830 const Products& products = installer_state.products();
831 for (Products::const_iterator it = products.begin(); it < products.end();
832 ++it) {
833 const Product& product = **it;
834 if (product.is_chrome_binaries())
835 continue;
836 DWORD dword_value = 0;
837 BrowserDistribution::Type this_type = product.distribution()->GetType();
838 const ProductState* product_state =
839 original_state.GetNonVersionedProductState(
840 system_install, this_type);
841 if (product_state->GetEulaAccepted(&dword_value) &&
842 (!have_eula_accepted || dword_value > eula_accepted)) {
843 have_eula_accepted = true;
844 eula_accepted = dword_value;
845 product_type = this_type;
849 if (have_eula_accepted) {
850 VLOG(1) << "Mirroring eulaaccepted=" << eula_accepted << " from "
851 << BrowserDistribution::GetSpecificDistribution(product_type)->
852 GetDisplayName();
853 install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
854 install_list->AddSetRegValueWorkItem(
855 root_key, multi_key, google_update::kRegEULAAceptedField,
856 eula_accepted, true);
857 } else {
858 // Clear any old value.
859 install_list->AddDeleteRegValueWorkItem(
860 root_key, multi_key, google_update::kRegEULAAceptedField);
865 // Adds work items that make registry adjustments for Google Update.
866 void AddGoogleUpdateWorkItems(const InstallationState& original_state,
867 const InstallerState& installer_state,
868 WorkItemList* install_list) {
869 // Is a multi-install product being installed or over-installed?
870 if (installer_state.operation() != InstallerState::MULTI_INSTALL &&
871 installer_state.operation() != InstallerState::MULTI_UPDATE) {
872 VLOG(1) << "AddGoogleUpdateWorkItems noop: " << installer_state.operation();
873 return;
876 const bool system_install = installer_state.system_install();
877 const HKEY root_key = installer_state.root_key();
878 base::string16 multi_key(
879 installer_state.multi_package_binaries_distribution()->GetStateKey());
881 // For system-level installs, make sure the ClientStateMedium key for the
882 // binaries exists.
883 if (system_install) {
884 install_list->AddCreateRegKeyWorkItem(
885 root_key,
886 installer_state.multi_package_binaries_distribution()->
887 GetStateMediumKey().c_str());
890 // Creating the ClientState key for binaries, if we're migrating to multi then
891 // copy over Chrome's brand code if it has one.
892 if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
893 const ProductState* chrome_product_state =
894 original_state.GetNonVersionedProductState(
895 system_install, BrowserDistribution::CHROME_BROWSER);
897 const base::string16& brand(chrome_product_state->brand());
898 if (!brand.empty()) {
899 install_list->AddCreateRegKeyWorkItem(root_key, multi_key);
900 // Write Chrome's brand code to the multi key. Never overwrite the value
901 // if one is already present (although this shouldn't happen).
902 install_list->AddSetRegValueWorkItem(root_key,
903 multi_key,
904 google_update::kRegBrandField,
905 brand,
906 false);
910 AddOemInstallWorkItems(original_state, installer_state, install_list);
911 AddEulaAcceptedWorkItems(original_state, installer_state, install_list);
912 AddUsageStatsWorkItems(original_state, installer_state, install_list);
914 // TODO(grt): check for other keys/values we should put in the package's
915 // ClientState and/or Clients key.
918 void AddUsageStatsWorkItems(const InstallationState& original_state,
919 const InstallerState& installer_state,
920 WorkItemList* install_list) {
921 DCHECK(installer_state.operation() == InstallerState::MULTI_INSTALL ||
922 installer_state.operation() == InstallerState::MULTI_UPDATE);
924 HKEY root_key = installer_state.root_key();
925 bool value_found = false;
926 DWORD usagestats = 0;
927 const Products& products = installer_state.products();
929 // Search for an existing usagestats value for any product.
930 for (Products::const_iterator scan = products.begin(), end = products.end();
931 !value_found && scan != end; ++scan) {
932 if ((*scan)->is_chrome_binaries())
933 continue;
934 BrowserDistribution* dist = (*scan)->distribution();
935 const ProductState* product_state =
936 original_state.GetNonVersionedProductState(
937 installer_state.system_install(), dist->GetType());
938 value_found = product_state->GetUsageStats(&usagestats);
941 // If a value was found, write it in the appropriate location for the
942 // binaries and remove all values from the products.
943 if (value_found) {
944 base::string16 state_key(
945 installer_state.multi_package_binaries_distribution()->GetStateKey());
946 install_list->AddCreateRegKeyWorkItem(root_key, state_key);
947 // Overwrite any existing value so that overinstalls (where Omaha writes a
948 // new value into a product's state key) pick up the correct value.
949 install_list->AddSetRegValueWorkItem(root_key, state_key,
950 google_update::kRegUsageStatsField,
951 usagestats, true);
953 for (Products::const_iterator scan = products.begin(), end = products.end();
954 scan != end; ++scan) {
955 if ((*scan)->is_chrome_binaries())
956 continue;
957 BrowserDistribution* dist = (*scan)->distribution();
958 if (installer_state.system_install()) {
959 install_list->AddDeleteRegValueWorkItem(
960 root_key, dist->GetStateMediumKey(),
961 google_update::kRegUsageStatsField);
962 // Previous versions of Chrome also wrote a value in HKCU even for
963 // system-level installs, so clean that up.
964 install_list->AddDeleteRegValueWorkItem(
965 HKEY_CURRENT_USER, dist->GetStateKey(),
966 google_update::kRegUsageStatsField);
968 install_list->AddDeleteRegValueWorkItem(
969 root_key, dist->GetStateKey(), google_update::kRegUsageStatsField);
974 bool AppendPostInstallTasks(const InstallerState& installer_state,
975 const base::FilePath& setup_path,
976 const Version* current_version,
977 const Version& new_version,
978 const base::FilePath& temp_path,
979 WorkItemList* post_install_task_list) {
980 DCHECK(post_install_task_list);
982 HKEY root = installer_state.root_key();
983 const Products& products = installer_state.products();
984 base::FilePath new_chrome_exe(
985 installer_state.target_path().Append(installer::kChromeNewExe));
987 // Append work items that will only be executed if this was an update.
988 // We update the 'opv' value with the current version that is active,
989 // the 'cpv' value with the critical update version (if present), and the
990 // 'cmd' value with the rename command to run.
992 scoped_ptr<WorkItemList> in_use_update_work_items(
993 WorkItem::CreateConditionalWorkItemList(
994 new ConditionRunIfFileExists(new_chrome_exe)));
995 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
997 // |critical_version| will be valid only if this in-use update includes a
998 // version considered critical relative to the version being updated.
999 Version critical_version(installer_state.DetermineCriticalVersion(
1000 current_version, new_version));
1001 base::FilePath installer_path(
1002 installer_state.GetInstallerDirectory(new_version).Append(
1003 setup_path.BaseName()));
1005 CommandLine rename(installer_path);
1006 rename.AppendSwitch(switches::kRenameChromeExe);
1007 if (installer_state.system_install())
1008 rename.AppendSwitch(switches::kSystemLevel);
1010 if (installer_state.verbose_logging())
1011 rename.AppendSwitch(switches::kVerboseLogging);
1013 base::string16 version_key;
1014 for (size_t i = 0; i < products.size(); ++i) {
1015 BrowserDistribution* dist = products[i]->distribution();
1016 version_key = dist->GetVersionKey();
1018 if (current_version) {
1019 in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
1020 google_update::kRegOldVersionField,
1021 ASCIIToWide(current_version->GetString()), true);
1023 if (critical_version.IsValid()) {
1024 in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
1025 google_update::kRegCriticalVersionField,
1026 ASCIIToWide(critical_version.GetString()), true);
1027 } else {
1028 in_use_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1029 google_update::kRegCriticalVersionField);
1032 // Adding this registry entry for all products (but the binaries) is
1033 // overkill. However, as it stands, we don't have a way to know which
1034 // product will check the key and run the command, so we add it for all.
1035 // The first to run it will perform the operation and clean up the other
1036 // values.
1037 if (dist->GetType() != BrowserDistribution::CHROME_BINARIES) {
1038 CommandLine product_rename_cmd(rename);
1039 products[i]->AppendRenameFlags(&product_rename_cmd);
1040 in_use_update_work_items->AddSetRegValueWorkItem(
1041 root, version_key, google_update::kRegRenameCmdField,
1042 product_rename_cmd.GetCommandLineString(), true);
1046 post_install_task_list->AddWorkItem(in_use_update_work_items.release());
1049 // Append work items that will be executed if this was NOT an in-use update.
1051 scoped_ptr<WorkItemList> regular_update_work_items(
1052 WorkItem::CreateConditionalWorkItemList(
1053 new Not(new ConditionRunIfFileExists(new_chrome_exe))));
1054 regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
1056 // Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
1057 for (size_t i = 0; i < products.size(); ++i) {
1058 BrowserDistribution* dist = products[i]->distribution();
1059 base::string16 version_key(dist->GetVersionKey());
1060 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1061 google_update::kRegOldVersionField);
1062 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1063 google_update::kRegCriticalVersionField);
1064 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
1065 google_update::kRegRenameCmdField);
1068 post_install_task_list->AddWorkItem(regular_update_work_items.release());
1071 AddRegisterComDllWorkItemsForPackage(installer_state, current_version,
1072 new_version, post_install_task_list);
1074 // If we're told that we're an MSI install, make sure to set the marker
1075 // in the client state key so that future updates do the right thing.
1076 if (installer_state.is_msi()) {
1077 for (size_t i = 0; i < products.size(); ++i) {
1078 const Product* product = products[i];
1079 AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true,
1080 post_install_task_list);
1082 // We want MSI installs to take over the Add/Remove Programs shortcut.
1083 // Make a best-effort attempt to delete any shortcuts left over from
1084 // previous non-MSI installations for the same type of install (system or
1085 // per user).
1086 if (product->ShouldCreateUninstallEntry()) {
1087 AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product,
1088 temp_path,
1089 post_install_task_list);
1094 return true;
1097 void AddInstallWorkItems(const InstallationState& original_state,
1098 const InstallerState& installer_state,
1099 const base::FilePath& setup_path,
1100 const base::FilePath& archive_path,
1101 const base::FilePath& src_path,
1102 const base::FilePath& temp_path,
1103 const Version* current_version,
1104 const Version& new_version,
1105 WorkItemList* install_list) {
1106 DCHECK(install_list);
1108 const base::FilePath& target_path = installer_state.target_path();
1110 // A temp directory that work items need and the actual install directory.
1111 install_list->AddCreateDirWorkItem(temp_path);
1112 install_list->AddCreateDirWorkItem(target_path);
1114 if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) ||
1115 installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
1116 AddChromeWorkItems(original_state,
1117 installer_state,
1118 setup_path,
1119 archive_path,
1120 src_path,
1121 temp_path,
1122 current_version,
1123 new_version,
1124 install_list);
1127 if (installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1128 install_list->AddCopyTreeWorkItem(
1129 src_path.Append(installer::kChromeAppHostExe).value(),
1130 target_path.Append(installer::kChromeAppHostExe).value(),
1131 temp_path.value(),
1132 WorkItem::ALWAYS,
1133 L"");
1136 // Copy installer in install directory
1137 AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_path,
1138 new_version, install_list);
1140 const HKEY root = installer_state.root_key();
1141 // Only set "lang" for user-level installs since for system-level, the install
1142 // language may not be related to a given user's runtime language.
1143 const bool add_language_identifier = !installer_state.system_install();
1145 const Products& products = installer_state.products();
1146 for (Products::const_iterator it = products.begin(); it < products.end();
1147 ++it) {
1148 const Product& product = **it;
1150 AddUninstallShortcutWorkItems(installer_state, setup_path, new_version,
1151 product, install_list);
1153 AddVersionKeyWorkItems(root, product.distribution(), new_version,
1154 add_language_identifier, install_list);
1156 AddDelegateExecuteWorkItems(installer_state, target_path, new_version,
1157 product, install_list);
1159 AddActiveSetupWorkItems(installer_state, setup_path, new_version, product,
1160 install_list);
1163 // TODO(huangs): Implement actual migration code and remove the hack below.
1164 // If installing Chrome without the legacy stand-alone App Launcher (to be
1165 // handled later), add "shadow" App Launcher registry keys so Google Update
1166 // would recognize the "dr" value in the App Launcher ClientState key.
1167 // Checking .is_multi_install() excludes Chrome Canary and stand-alone Chrome.
1168 if (installer_state.is_multi_install() &&
1169 installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) &&
1170 !installer_state.FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
1171 BrowserDistribution* shadow_app_launcher_dist =
1172 BrowserDistribution::GetSpecificDistribution(
1173 BrowserDistribution::CHROME_APP_HOST);
1174 AddVersionKeyWorkItems(root, shadow_app_launcher_dist, new_version,
1175 add_language_identifier, install_list);
1178 // Add any remaining work items that involve special settings for
1179 // each product.
1180 AddProductSpecificWorkItems(original_state, installer_state, setup_path,
1181 new_version, install_list);
1183 // Copy over brand, usagestats, and other values.
1184 AddGoogleUpdateWorkItems(original_state, installer_state, install_list);
1186 // Append the tasks that run after the installation.
1187 AppendPostInstallTasks(installer_state,
1188 setup_path,
1189 current_version,
1190 new_version,
1191 temp_path,
1192 install_list);
1195 void AddRegisterComDllWorkItems(const base::FilePath& dll_folder,
1196 const std::vector<base::FilePath>& dll_list,
1197 bool system_level,
1198 bool do_register,
1199 bool ignore_failures,
1200 WorkItemList* work_item_list) {
1201 DCHECK(work_item_list);
1202 if (dll_list.empty()) {
1203 VLOG(1) << "No COM DLLs to register";
1204 } else {
1205 std::vector<base::FilePath>::const_iterator dll_iter(dll_list.begin());
1206 for (; dll_iter != dll_list.end(); ++dll_iter) {
1207 base::FilePath dll_path = dll_folder.Append(*dll_iter);
1208 WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
1209 dll_path.value(), do_register, !system_level);
1210 DCHECK(work_item);
1211 work_item->set_ignore_failure(ignore_failures);
1216 void AddSetMsiMarkerWorkItem(const InstallerState& installer_state,
1217 BrowserDistribution* dist,
1218 bool set,
1219 WorkItemList* work_item_list) {
1220 DCHECK(work_item_list);
1221 DWORD msi_value = set ? 1 : 0;
1222 WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
1223 installer_state.root_key(), dist->GetStateKey(),
1224 google_update::kRegMSIField, msi_value, true);
1225 DCHECK(set_msi_work_item);
1226 set_msi_work_item->set_ignore_failure(true);
1227 set_msi_work_item->set_log_message("Could not write MSI marker!");
1230 void AddDelegateExecuteWorkItems(const InstallerState& installer_state,
1231 const base::FilePath& target_path,
1232 const Version& new_version,
1233 const Product& product,
1234 WorkItemList* list) {
1235 base::string16 handler_class_uuid;
1236 BrowserDistribution* dist = product.distribution();
1237 if (!dist->GetCommandExecuteImplClsid(&handler_class_uuid)) {
1238 if (InstallUtil::IsChromeSxSProcess()) {
1239 CleanupBadCanaryDelegateExecuteRegistration(target_path, list);
1240 } else {
1241 VLOG(1) << "No DelegateExecute verb handler processing to do for "
1242 << dist->GetDisplayName();
1244 return;
1247 HKEY root = installer_state.root_key();
1248 base::string16 delegate_execute_path(L"Software\\Classes\\CLSID\\");
1249 delegate_execute_path.append(handler_class_uuid);
1251 // Unconditionally remove registration regardless of whether or not it is
1252 // needed since builds after r132190 included it when it wasn't strictly
1253 // necessary. Do this removal before adding in the new key to ensure that
1254 // the COM probe/flush below does its job.
1255 AddUninstallDelegateExecuteWorkItems(root, delegate_execute_path, list);
1257 // Add work items to register the handler iff it is present.
1258 // See also shell_util.cc's GetProgIdEntries.
1259 if (installer_state.operation() != InstallerState::UNINSTALL) {
1260 VLOG(1) << "Adding registration items for DelegateExecute verb handler.";
1262 // Force COM to flush its cache containing the path to the old handler.
1263 list->AddCallbackWorkItem(base::Bind(&ProbeCommandExecuteCallback,
1264 handler_class_uuid));
1266 // The path to the exe (in the version directory).
1267 base::FilePath delegate_execute(target_path);
1268 if (new_version.IsValid())
1269 delegate_execute = delegate_execute.AppendASCII(new_version.GetString());
1270 delegate_execute = delegate_execute.Append(kDelegateExecuteExe);
1272 // Command-line featuring the quoted path to the exe.
1273 base::string16 command(1, L'"');
1274 command.append(delegate_execute.value()).append(1, L'"');
1276 // Register the CommandExecuteImpl class in Software\Classes\CLSID\...
1277 list->AddCreateRegKeyWorkItem(root, delegate_execute_path);
1278 list->AddSetRegValueWorkItem(root, delegate_execute_path, L"",
1279 L"CommandExecuteImpl Class", true);
1280 base::string16 subkey(delegate_execute_path);
1281 subkey.append(L"\\LocalServer32");
1282 list->AddCreateRegKeyWorkItem(root, subkey);
1283 list->AddSetRegValueWorkItem(root, subkey, L"", command, true);
1284 list->AddSetRegValueWorkItem(root, subkey, L"ServerExecutable",
1285 delegate_execute.value(), true);
1287 subkey.assign(delegate_execute_path).append(L"\\Programmable");
1288 list->AddCreateRegKeyWorkItem(root, subkey);
1292 void AddActiveSetupWorkItems(const InstallerState& installer_state,
1293 const base::FilePath& setup_path,
1294 const Version& new_version,
1295 const Product& product,
1296 WorkItemList* list) {
1297 DCHECK(installer_state.operation() != InstallerState::UNINSTALL);
1298 BrowserDistribution* dist = product.distribution();
1300 if (!product.is_chrome() || !installer_state.system_install()) {
1301 const char* install_level =
1302 installer_state.system_install() ? "system" : "user";
1303 VLOG(1) << "No Active Setup processing to do for " << install_level
1304 << "-level " << dist->GetDisplayName();
1305 return;
1307 DCHECK(installer_state.RequiresActiveSetup());
1309 const HKEY root = HKEY_LOCAL_MACHINE;
1310 const base::string16 active_setup_path(InstallUtil::GetActiveSetupPath(dist));
1312 VLOG(1) << "Adding registration items for Active Setup.";
1313 list->AddCreateRegKeyWorkItem(root, active_setup_path);
1314 list->AddSetRegValueWorkItem(root, active_setup_path, L"",
1315 dist->GetDisplayName(), true);
1317 base::FilePath active_setup_exe(installer_state.GetInstallerDirectory(
1318 new_version).Append(kActiveSetupExe));
1319 CommandLine cmd(active_setup_exe);
1320 cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
1321 cmd.AppendSwitch(installer::switches::kVerboseLogging);
1322 cmd.AppendSwitch(installer::switches::kSystemLevel);
1323 product.AppendProductFlags(&cmd);
1324 list->AddSetRegValueWorkItem(root, active_setup_path, L"StubPath",
1325 cmd.GetCommandLineString(), true);
1327 // TODO(grt): http://crbug.com/75152 Write a reference to a localized
1328 // resource.
1329 list->AddSetRegValueWorkItem(root, active_setup_path, L"Localized Name",
1330 dist->GetDisplayName(), true);
1332 list->AddSetRegValueWorkItem(root, active_setup_path, L"IsInstalled",
1333 static_cast<DWORD>(1U), true);
1335 list->AddSetRegValueWorkItem(root, active_setup_path, L"Version",
1336 kActiveSetupVersion, true);
1339 void AddDeleteOldIELowRightsPolicyWorkItems(
1340 const InstallerState& installer_state,
1341 WorkItemList* install_list) {
1342 DCHECK(install_list);
1344 base::string16 key_path;
1345 GetOldIELowRightsElevationPolicyKeyPath(&key_path);
1346 install_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), key_path);
1349 void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
1350 const Product& product,
1351 CommandLine* uninstall_cmd) {
1352 DCHECK(uninstall_cmd);
1354 uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
1356 // Append the product-specific uninstall flags.
1357 product.AppendProductFlags(uninstall_cmd);
1358 if (installer_state.is_msi())
1359 uninstall_cmd->AppendSwitch(installer::switches::kMsi);
1360 if (installer_state.system_install())
1361 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
1362 if (installer_state.verbose_logging())
1363 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
1366 void RefreshElevationPolicy() {
1367 const wchar_t kIEFrameDll[] = L"ieframe.dll";
1368 const char kIERefreshPolicy[] = "IERefreshElevationPolicy";
1370 HMODULE ieframe = LoadLibrary(kIEFrameDll);
1371 if (ieframe) {
1372 typedef HRESULT (__stdcall *IERefreshPolicy)();
1373 IERefreshPolicy ie_refresh_policy = reinterpret_cast<IERefreshPolicy>(
1374 GetProcAddress(ieframe, kIERefreshPolicy));
1376 if (ie_refresh_policy) {
1377 ie_refresh_policy();
1378 } else {
1379 VLOG(1) << kIERefreshPolicy << " not supported.";
1382 FreeLibrary(ieframe);
1383 } else {
1384 VLOG(1) << "Cannot load " << kIEFrameDll;
1388 void AddOsUpgradeWorkItems(const InstallerState& installer_state,
1389 const base::FilePath& setup_path,
1390 const Version& new_version,
1391 const Product& product,
1392 WorkItemList* install_list) {
1393 const HKEY root_key = installer_state.root_key();
1394 base::string16 cmd_key(
1395 GetRegCommandKey(product.distribution(), kCmdOnOsUpgrade));
1397 if (installer_state.operation() == InstallerState::UNINSTALL) {
1398 install_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)->
1399 set_log_message("Removing OS upgrade command");
1400 } else {
1401 // Register with Google Update to have setup.exe --on-os-upgrade called on
1402 // OS upgrade.
1403 CommandLine cmd_line(installer_state
1404 .GetInstallerDirectory(new_version)
1405 .Append(setup_path.BaseName()));
1406 // Add the main option to indicate OS upgrade flow.
1407 cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
1408 // Add product-specific options.
1409 product.AppendProductFlags(&cmd_line);
1410 if (installer_state.system_install())
1411 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1412 // Log everything for now.
1413 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1415 AppCommand cmd(cmd_line.GetCommandLineString());
1416 cmd.set_is_auto_run_on_os_upgrade(true);
1417 cmd.AddWorkItems(installer_state.root_key(), cmd_key, install_list);
1421 void AddQueryEULAAcceptanceWorkItems(const InstallerState& installer_state,
1422 const base::FilePath& setup_path,
1423 const Version& new_version,
1424 const Product& product,
1425 WorkItemList* work_item_list) {
1426 const HKEY root_key = installer_state.root_key();
1427 base::string16 cmd_key(
1428 GetRegCommandKey(product.distribution(), kCmdQueryEULAAcceptance));
1429 if (installer_state.operation() == InstallerState::UNINSTALL) {
1430 work_item_list->AddDeleteRegKeyWorkItem(root_key, cmd_key)->
1431 set_log_message("Removing query EULA acceptance command");
1432 } else {
1433 CommandLine cmd_line(installer_state
1434 .GetInstallerDirectory(new_version)
1435 .Append(setup_path.BaseName()));
1436 cmd_line.AppendSwitch(switches::kQueryEULAAcceptance);
1437 if (installer_state.system_install())
1438 cmd_line.AppendSwitch(installer::switches::kSystemLevel);
1439 if (installer_state.verbose_logging())
1440 cmd_line.AppendSwitch(installer::switches::kVerboseLogging);
1441 AppCommand cmd(cmd_line.GetCommandLineString());
1442 cmd.set_is_web_accessible(true);
1443 cmd.set_is_run_as_user(true);
1444 cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list);
1448 void AddQuickEnableChromeFrameWorkItems(const InstallerState& installer_state,
1449 WorkItemList* work_item_list) {
1450 DCHECK(work_item_list);
1452 base::string16 cmd_key(
1453 GetRegCommandKey(BrowserDistribution::GetSpecificDistribution(
1454 BrowserDistribution::CHROME_BINARIES),
1455 kCmdQuickEnableCf));
1457 // Unconditionally remove the legacy Quick Enable command from the binaries.
1458 // Do this even if multi-install Chrome isn't installed to ensure that it is
1459 // not left behind in any case.
1460 work_item_list->AddDeleteRegKeyWorkItem(
1461 installer_state.root_key(), cmd_key)->set_log_message(
1462 "removing " + WideToASCII(kCmdQuickEnableCf) + " command");
1466 } // namespace installer