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