diff options
43 files changed, 2414 insertions, 1277 deletions
diff --git a/chrome/browser/browser_main_win.cc b/chrome/browser/browser_main_win.cc index e4b52da..273a113 100644 --- a/chrome/browser/browser_main_win.cc +++ b/chrome/browser/browser_main_win.cc @@ -29,6 +29,7 @@ #include "chrome/common/env_vars.h" #include "chrome/common/main_function_params.h" #include "chrome/common/result_codes.h" +#include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/shell_util.h" @@ -99,9 +100,12 @@ int DoUninstallTasks(bool chrome_still_running) { VLOG(1) << "Failed to delete sentinel file."; // We want to remove user level shortcuts and we only care about the ones // created by us and not by the installer so |alternate| is false. - if (!ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER, false)) + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + if (!ShellUtil::RemoveChromeDesktopShortcut(dist, ShellUtil::CURRENT_USER, + false)) VLOG(1) << "Failed to delete desktop shortcut."; - if (!ShellUtil::RemoveChromeQuickLaunchShortcut(ShellUtil::CURRENT_USER)) + if (!ShellUtil::RemoveChromeQuickLaunchShortcut(dist, + ShellUtil::CURRENT_USER)) VLOG(1) << "Failed to delete quick launch shortcut."; } return ret; @@ -176,22 +180,23 @@ int HandleIconsCommands(const CommandLine &parsed_command_line) { // allow the user level Chrome to run. So we notify the user and uninstall // user level Chrome. bool CheckMachineLevelInstall() { - scoped_ptr<installer::Version> version(InstallUtil::GetChromeVersion(true)); + // TODO(tommi): Check if using the default distribution is always the right + // thing to do. + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + scoped_ptr<installer::Version> version(InstallUtil::GetChromeVersion(dist, + true)); if (version.get()) { FilePath exe_path; PathService::Get(base::DIR_EXE, &exe_path); std::wstring exe = exe_path.value(); - std::transform(exe.begin(), exe.end(), exe.begin(), tolower); - std::wstring user_exe_path = installer::GetChromeInstallPath(false); - std::transform(user_exe_path.begin(), user_exe_path.end(), - user_exe_path.begin(), tolower); - if (exe == user_exe_path) { + FilePath user_exe_path(installer::GetChromeInstallPath(false, dist)); + if (FilePath::CompareEqualIgnoreCase(exe, user_exe_path.value())) { const std::wstring text = l10n_util::GetString(IDS_MACHINE_LEVEL_INSTALL_CONFLICT); const std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME); const UINT flags = MB_OK | MB_ICONERROR | MB_TOPMOST; win_util::MessageBox(NULL, text, caption, flags); - FilePath uninstall_path(InstallUtil::GetChromeUninstallCmd(false)); + FilePath uninstall_path(InstallUtil::GetChromeUninstallCmd(false, dist)); CommandLine uninstall_cmd(uninstall_path); if (!uninstall_cmd.GetProgram().value().empty()) { uninstall_cmd.AppendSwitch(installer_util::switches::kForceUninstall); diff --git a/chrome/browser/first_run/first_run_win.cc b/chrome/browser/first_run/first_run_win.cc index bffded1..55016c4 100644 --- a/chrome/browser/first_run/first_run_win.cc +++ b/chrome/browser/first_run/first_run_win.cc @@ -224,7 +224,7 @@ bool FirstRun::CreateChromeDesktopShortcut() { BrowserDistribution *dist = BrowserDistribution::GetDistribution(); if (!dist) return false; - return ShellUtil::CreateChromeDesktopShortcut(chrome_exe.value(), + return ShellUtil::CreateChromeDesktopShortcut(dist, chrome_exe.value(), dist->GetAppDescription(), ShellUtil::CURRENT_USER, false, true); // create if doesn't exist. } @@ -233,7 +233,8 @@ bool FirstRun::CreateChromeQuickLaunchShortcut() { FilePath chrome_exe; if (!PathService::Get(base::FILE_EXE, &chrome_exe)) return false; - return ShellUtil::CreateChromeQuickLaunchShortcut(chrome_exe.value(), + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + return ShellUtil::CreateChromeQuickLaunchShortcut(dist, chrome_exe.value(), ShellUtil::CURRENT_USER, // create only for current user. true); // create if doesn't exist. } diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc index 044a787..ad1f653 100644 --- a/chrome/browser/shell_integration_win.cc +++ b/chrome/browser/shell_integration_win.cc @@ -272,7 +272,8 @@ bool ShellIntegration::SetAsDefaultBrowser() { } // From UI currently we only allow setting default browser for current user. - if (!ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + if (!ShellUtil::MakeChromeDefault(dist, ShellUtil::CURRENT_USER, chrome_exe.value(), true)) { LOG(ERROR) << "Chrome could not be set as default browser."; return false; @@ -315,7 +316,7 @@ ShellIntegration::DefaultBrowserState ShellIntegration::IsDefaultBrowser() { // app name being default. If not, then default browser is just called // Google Chrome or Chromium so we do not append suffix to app name. std::wstring suffix; - if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(&suffix)) + if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) app_name += suffix; for (int i = 0; i < _countof(kChromeProtocols); i++) { diff --git a/chrome/browser/ui/views/about_chrome_view.cc b/chrome/browser/ui/views/about_chrome_view.cc index 2808d98..11cede9 100644 --- a/chrome/browser/ui/views/about_chrome_view.cc +++ b/chrome/browser/ui/views/about_chrome_view.cc @@ -26,6 +26,7 @@ #include "chrome/common/chrome_version_info.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" +#include "chrome/installer/util/browser_distribution.h" #include "gfx/canvas.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -739,8 +740,11 @@ void AboutChromeView::UpdateStatus(GoogleUpdateUpgradeResult result, // Google Update reported that Chrome is up-to-date. Now make sure that we // are running the latest version and if not, notify the user by falling // into the next case of UPGRADE_SUCCESSFUL. + // TODO(tommi): Check if using the default distribution is always the + // right thing to do. + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); scoped_ptr<installer::Version> installed_version( - InstallUtil::GetChromeVersion(false)); + InstallUtil::GetChromeVersion(dist, false)); scoped_ptr<installer::Version> running_version( installer::Version::GetVersionFromString(current_version_)); if (!installed_version.get() || diff --git a/chrome/browser/ui/views/uninstall_view.cc b/chrome/browser/ui/views/uninstall_view.cc index a7535b0..ecd2ac7 100644 --- a/chrome/browser/ui/views/uninstall_view.cc +++ b/chrome/browser/ui/views/uninstall_view.cc @@ -65,10 +65,11 @@ void UninstallView::SetupControls() { layout->AddView(delete_profile_); // Set default browser combo box - if (BrowserDistribution::GetDistribution()->CanSetAsDefault() && + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + if (dist->CanSetAsDefault() && ShellIntegration::IsDefaultBrowser()) { browsers_.reset(new BrowsersMap()); - ShellUtil::GetRegisteredBrowsers(browsers_.get()); + ShellUtil::GetRegisteredBrowsers(dist, browsers_.get()); if (!browsers_->empty()) { layout->AddPaddingRow(0, kRelatedControlVerticalSpacing); diff --git a/chrome/browser/upgrade_detector.cc b/chrome/browser/upgrade_detector.cc index 6a65c7a..56886bf 100644 --- a/chrome/browser/upgrade_detector.cc +++ b/chrome/browser/upgrade_detector.cc @@ -86,10 +86,13 @@ class DetectUpgradeTask : public Task { // Get the version of the currently *installed* instance of Chrome, // which might be newer than the *running* instance if we have been // upgraded in the background. - installed_version.reset(InstallUtil::GetChromeVersion(false)); + // TODO(tommi): Check if using the default distribution is always the right + // thing to do. + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + installed_version.reset(InstallUtil::GetChromeVersion(dist, false)); if (!installed_version.get()) { // User level Chrome is not installed, check system level. - installed_version.reset(InstallUtil::GetChromeVersion(true)); + installed_version.reset(InstallUtil::GetChromeVersion(dist, true)); } #elif defined(OS_MACOSX) installed_version.reset( diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi index c431fac..468c95d 100644 --- a/chrome/chrome_installer.gypi +++ b/chrome/chrome_installer.gypi @@ -92,6 +92,9 @@ 'installer/util/lzma_util_unittest.cc', 'installer/util/master_preferences_unittest.cc', 'installer/util/move_tree_work_item_unittest.cc', + 'installer/util/package_unittest.cc', + 'installer/util/product_unittest.h', + 'installer/util/product_unittest.cc', 'installer/util/run_all_unittests.cc', 'installer/util/set_reg_value_work_item_unittest.cc', 'installer/util/shell_util_unittest.cc', diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi index 02603bf..2c922cd 100644 --- a/chrome/chrome_installer_util.gypi +++ b/chrome/chrome_installer_util.gypi @@ -101,6 +101,10 @@ 'installer/util/lzma_util.h', 'installer/util/master_preferences.cc', 'installer/util/master_preferences.h', + 'installer/util/package.h', + 'installer/util/package.cc', + 'installer/util/product.h', + 'installer/util/product.cc', 'installer/util/shell_util.cc', 'installer/util/shell_util.h', ], diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc index 8aefcf3..3817aa1 100644 --- a/chrome/installer/setup/install.cc +++ b/chrome/installer/setup/install.cc @@ -22,6 +22,8 @@ #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/master_preferences_constants.h" +#include "chrome/installer/util/package.h" +#include "chrome/installer/util/product.h" #include "chrome/installer/util/set_reg_value_work_item.h" #include "chrome/installer/util/shell_util.h" #include "chrome/installer/util/util_constants.h" @@ -33,12 +35,10 @@ namespace { -std::wstring AppendPath(const std::wstring& parent_path, - const std::wstring& path) { - std::wstring new_path(parent_path); - file_util::AppendToPath(&new_path, path); - return new_path; -} +using installer::Products; +using installer::Product; +using installer::Package; +using installer::Version; void AddChromeToMediaPlayerList() { std::wstring reg_path(installer::kMediaPlayerRegPath); @@ -53,87 +53,95 @@ void AddChromeToMediaPlayerList() { LOG(ERROR) << "Could not add Chrome to media player inclusion list."; } -void AddInstallerCopyTasks(const std::wstring& exe_path, - const std::wstring& archive_path, - const std::wstring& temp_path, - const std::wstring& install_path, - const std::wstring& new_version, +void AddInstallerCopyTasks(const FilePath& setup_path, + const FilePath& archive_path, + const FilePath& temp_path, + const Version& new_version, WorkItemList* install_list, - bool system_level) { - std::wstring installer_dir(installer::GetInstallerPathUnderChrome( - install_path, new_version)); - install_list->AddCreateDirWorkItem( - FilePath::FromWStringHack(installer_dir)); - - std::wstring exe_dst(installer_dir); - std::wstring archive_dst(installer_dir); - file_util::AppendToPath(&exe_dst, - file_util::GetFilenameFromPath(exe_path)); - file_util::AppendToPath(&archive_dst, - file_util::GetFilenameFromPath(archive_path)); - - install_list->AddCopyTreeWorkItem(exe_path, exe_dst, temp_path, - WorkItem::ALWAYS); - if (system_level) { - install_list->AddCopyTreeWorkItem(archive_path, archive_dst, temp_path, - WorkItem::ALWAYS); + const Package& package) { + DCHECK(install_list); + FilePath installer_dir(package.GetInstallerDirectory(new_version)); + install_list->AddCreateDirWorkItem(installer_dir); + + FilePath exe_dst(installer_dir.Append(setup_path.BaseName())); + FilePath archive_dst(installer_dir.Append(archive_path.BaseName())); + + install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(), + temp_path.value(), WorkItem::ALWAYS); + if (package.system_level()) { + install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(), + temp_path.value(), WorkItem::ALWAYS); } else { - install_list->AddMoveTreeWorkItem(archive_path, archive_dst, temp_path); + install_list->AddMoveTreeWorkItem(archive_path.value(), archive_dst.value(), + temp_path.value()); } } void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd, - bool is_system) { + const Product& product) { DCHECK(uninstall_cmd); + uninstall_cmd->AppendSwitch(installer_util::switches::kUninstall); - // TODO(tommi): In case of multiple installations, we need to create multiple - // uninstall entries, and not one magic one for all. const installer_util::MasterPreferences& prefs = InstallUtil::GetMasterPreferencesForCurrentProcess(); - DCHECK(!prefs.is_multi_install()); - if (prefs.install_chrome_frame()) { + bool cf_switch_added = false; + + if (prefs.is_multi_install()) { + uninstall_cmd->AppendSwitch(installer_util::switches::kMultiInstall); + switch (product.distribution()->GetType()) { + case BrowserDistribution::CHROME_BROWSER: + uninstall_cmd->AppendSwitch(installer_util::switches::kChrome); + break; + case BrowserDistribution::CHROME_FRAME: + uninstall_cmd->AppendSwitch(installer_util::switches::kChromeFrame); + cf_switch_added = true; + break; + case BrowserDistribution::CEEE: + uninstall_cmd->AppendSwitch(installer_util::switches::kCeee); + break; + default: + NOTREACHED(); + break; + } + } + + if (product.distribution()->GetType() == BrowserDistribution::CHROME_FRAME) { + DCHECK(prefs.install_chrome_frame()); uninstall_cmd->AppendSwitch(installer_util::switches::kDeleteProfile); - uninstall_cmd->AppendSwitch(installer_util::switches::kChromeFrame); + if (!cf_switch_added) { + uninstall_cmd->AppendSwitch(installer_util::switches::kChromeFrame); + } } - if (InstallUtil::IsChromeSxSProcess()) { + if (InstallUtil::IsChromeSxSProcess()) uninstall_cmd->AppendSwitch(installer_util::switches::kChromeSxS); - } - if (InstallUtil::IsMSIProcess(is_system)) { + if (product.IsMsi()) uninstall_cmd->AppendSwitch(installer_util::switches::kMsi); - } // Propagate the verbose logging switch to uninstalls too. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(installer_util::switches::kVerboseLogging)) { + bool value = false; + if (prefs.GetBool(installer_util::master_preferences::kVerboseLogging, + &value) && value) uninstall_cmd->AppendSwitch(installer_util::switches::kVerboseLogging); - } - if (is_system) { + if (product.system_level()) uninstall_cmd->AppendSwitch(installer_util::switches::kSystemLevel); - } } // This method adds work items to create (or update) Chrome uninstall entry in // either the Control Panel->Add/Remove Programs list or in the Omaha client // state key if running under an MSI installer. -void AddUninstallShortcutWorkItems(HKEY reg_root, - const std::wstring& exe_path, - const std::wstring& install_path, - const std::wstring& product_name, - const std::wstring& new_version, - WorkItemList* install_list) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - - // TODO(tommi): Support this for multi-install. We need to add work items - // for each product being installed. - const installer_util::MasterPreferences& prefs = - InstallUtil::GetMasterPreferencesForCurrentProcess(); - DCHECK(!prefs.is_multi_install()) << "TODO"; +void AddUninstallShortcutWorkItems(const FilePath& setup_path, + const Version& new_version, + WorkItemList* install_list, + const Product& product) { + HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; + BrowserDistribution* browser_dist = product.distribution(); + DCHECK(browser_dist); // When we are installed via an MSI, we need to store our uninstall strings // in the Google Update client state key. We do this even for non-MSI @@ -141,16 +149,15 @@ void AddUninstallShortcutWorkItems(HKEY reg_root, // install is updated by a non-msi installer (which would confuse the MSI // machinery if these strings were not also updated). // Do not quote the command line for the MSI invocation. - FilePath installer_path(installer::GetInstallerPathUnderChrome(install_path, - new_version)); - installer_path = installer_path.Append( - file_util::GetFilenameFromPath(exe_path)); + FilePath install_path(product.package().path()); + FilePath installer_path( + product.package().GetInstallerDirectory(new_version)); + installer_path = installer_path.Append(setup_path.BaseName()); CommandLine uninstall_arguments(CommandLine::NO_PROGRAM); - AppendUninstallCommandLineFlags(&uninstall_arguments, - reg_root == HKEY_LOCAL_MACHINE); + AppendUninstallCommandLineFlags(&uninstall_arguments, product); - std::wstring update_state_key = dist->GetStateKey(); + std::wstring update_state_key(browser_dist->GetStateKey()); install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key); install_list->AddSetRegValueWorkItem(reg_root, update_state_key, installer_util::kUninstallStringField, installer_path.value(), true); @@ -159,31 +166,32 @@ void AddUninstallShortcutWorkItems(HKEY reg_root, uninstall_arguments.command_line_string(), true); // MSI installations will manage their own uninstall shortcuts. - if (!InstallUtil::IsMSIProcess(reg_root == HKEY_LOCAL_MACHINE)) { + if (!product.IsMsi()) { // We need to quote the command line for the Add/Remove Programs dialog. CommandLine quoted_uninstall_cmd(installer_path); DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"'); quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false); - std::wstring uninstall_reg = dist->GetUninstallRegPath(); + std::wstring uninstall_reg = browser_dist->GetUninstallRegPath(); install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, - installer_util::kUninstallDisplayNameField, product_name, true); + installer_util::kUninstallDisplayNameField, + browser_dist->GetAppShortCutName(), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, installer_util::kUninstallStringField, quoted_uninstall_cmd.command_line_string(), true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, L"InstallLocation", - install_path, + install_path.value(), true); // DisplayIcon, NoModify and NoRepair - std::wstring chrome_icon = AppendPath(install_path, - installer_util::kChromeExe); - ShellUtil::GetChromeIcon(chrome_icon); + FilePath chrome_icon(install_path.Append(installer_util::kChromeExe)); + ShellUtil::GetChromeIcon(product.distribution(), chrome_icon.value()); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, - L"DisplayIcon", chrome_icon, true); + L"DisplayIcon", chrome_icon.value(), + true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, L"NoModify", 1, true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, @@ -191,12 +199,14 @@ void AddUninstallShortcutWorkItems(HKEY reg_root, install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, L"Publisher", - dist->GetPublisherName(), true); + browser_dist->GetPublisherName(), + true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, - L"Version", new_version.c_str(), true); + L"Version", new_version.GetString(), + true); install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg, L"DisplayVersion", - new_version.c_str(), true); + new_version.GetString(), true); time_t rawtime = time(NULL); struct tm timeinfo = {0}; localtime_s(&timeinfo, &rawtime); @@ -214,31 +224,31 @@ void AddUninstallShortcutWorkItems(HKEY reg_root, // If so, try and remove any existing uninstallation shortcuts, as we want the // uninstall to be managed entirely by the MSI machinery (accessible via the // Add/Remove programs dialog). -void DeleteUninstallShortcutsForMSI(bool is_system_install) { - DCHECK(InstallUtil::IsMSIProcess(is_system_install)) - << "This must only be called for MSI installations!"; +void DeleteUninstallShortcutsForMSI(const Product& product) { + DCHECK(product.IsMsi()) << "This must only be called for MSI installations!"; // First attempt to delete the old installation's ARP dialog entry. - HKEY reg_root = is_system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - std::wstring uninstall_reg = dist->GetUninstallRegPath(); + std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath()); InstallUtil::DeleteRegistryKey(root_key, uninstall_reg); // Then attempt to delete the old installation's start menu shortcut. FilePath uninstall_link; - if (is_system_install) { + if (product.system_level()) { PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link); } else { PathService::Get(base::DIR_START_MENU, &uninstall_link); } + if (uninstall_link.empty()) { LOG(ERROR) << "Failed to get location for shortcut."; } else { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - uninstall_link = uninstall_link.Append(dist->GetAppShortCutName()); uninstall_link = uninstall_link.Append( - dist->GetUninstallLinkName() + L".lnk"); + product.distribution()->GetAppShortCutName()); + uninstall_link = uninstall_link.Append( + product.distribution()->GetUninstallLinkName() + L".lnk"); VLOG(1) << "Deleting old uninstall shortcut (if present): " << uninstall_link.value(); if (!file_util::Delete(uninstall_link, true)) @@ -249,15 +259,14 @@ void DeleteUninstallShortcutsForMSI(bool is_system_install) { // Copy master preferences file provided to installer, in the same folder // as chrome.exe so Chrome first run can find it. This function will be called // only on the first install of Chrome. -void CopyPreferenceFileForFirstRun(bool system_level, - const std::wstring& prefs_source_path) { - FilePath prefs_dest_path = FilePath::FromWStringHack( - installer::GetChromeInstallPath(system_level)); - prefs_dest_path = prefs_dest_path.AppendASCII( - installer_util::kDefaultMasterPrefs); - if (!file_util::CopyFile(FilePath::FromWStringHack(prefs_source_path), - prefs_dest_path)) - VLOG(1) << "Failed to copy master preferences."; +void CopyPreferenceFileForFirstRun(const Package& package, + const FilePath& prefs_source_path) { + FilePath prefs_dest_path(package.path().AppendASCII( + installer_util::kDefaultMasterPrefs)); + if (!file_util::CopyFile(prefs_source_path, prefs_dest_path)) { + VLOG(1) << "Failed to copy master preferences from:" + << prefs_source_path.value() << " gle: " << ::GetLastError(); + } } // This method creates Chrome shortcuts in Start->Programs for all users or @@ -274,25 +283,32 @@ void CopyPreferenceFileForFirstRun(bool system_level, // // If the shortcuts do not exist, the function does not recreate them during // update. -bool CreateOrUpdateChromeShortcuts(const std::wstring& exe_path, - const std::wstring& install_path, - const std::wstring& new_version, +bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, + const Version& new_version, installer_util::InstallStatus install_status, - bool system_install, + const Product& product, bool create_all_shortcut, bool alt_shortcut) { + // TODO(tommi): Change this function to use WorkItemList. +#ifndef NDEBUG + const installer_util::MasterPreferences& prefs = + InstallUtil::GetMasterPreferencesForCurrentProcess(); + DCHECK(prefs.install_chrome()); +#endif + FilePath shortcut_path; - int dir_enum = (system_install) ? base::DIR_COMMON_START_MENU : - base::DIR_START_MENU; + int dir_enum = product.system_level() ? base::DIR_COMMON_START_MENU : + base::DIR_START_MENU; if (!PathService::Get(dir_enum, &shortcut_path)) { LOG(ERROR) << "Failed to get location for shortcut."; return false; } + BrowserDistribution* browser_dist = product.distribution(); + // The location of Start->Programs->Google Chrome folder - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - const std::wstring& product_name = dist->GetAppShortCutName(); - const std::wstring& product_desc = dist->GetAppDescription(); + const std::wstring product_name(browser_dist->GetAppShortCutName()); + const std::wstring product_desc(browser_dist->GetAppDescription()); shortcut_path = shortcut_path.Append(product_name); // Create/update Chrome link (points to chrome.exe) & Uninstall Chrome link @@ -305,151 +321,191 @@ bool CreateOrUpdateChromeShortcuts(const std::wstring& exe_path, bool ret = true; FilePath chrome_link(shortcut_path); // Chrome link (launches Chrome) chrome_link = chrome_link.Append(product_name + L".lnk"); - std::wstring chrome_exe(install_path); // Chrome link target - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + // Chrome link target + FilePath chrome_exe( + product.package().path().Append(installer_util::kChromeExe)); if ((install_status == installer_util::FIRST_INSTALL_SUCCESS) || (install_status == installer_util::INSTALL_REPAIRED)) { if (!file_util::PathExists(shortcut_path)) file_util::CreateDirectoryW(shortcut_path); - VLOG(1) << "Creating shortcut to " << chrome_exe << " at " + VLOG(1) << "Creating shortcut to " << chrome_exe.value() << " at " << chrome_link.value(); - ret = ret && ShellUtil::UpdateChromeShortcut(chrome_exe, - chrome_link.value(), - product_desc, true); + ret = ShellUtil::UpdateChromeShortcut(browser_dist, chrome_exe.value(), + chrome_link.value(), product_desc, true); } else if (file_util::PathExists(chrome_link)) { VLOG(1) << "Updating shortcut at " << chrome_link.value() - << " to point to " << chrome_exe; - ret = ret && ShellUtil::UpdateChromeShortcut(chrome_exe, - chrome_link.value(), - product_desc, false); + << " to point to " << chrome_exe.value(); + ret = ShellUtil::UpdateChromeShortcut(browser_dist, chrome_exe.value(), + chrome_link.value(), product_desc, false); + } else { + VLOG(1) + << "not first or repaired install, link file doesn't exist. status: " + << install_status; } // Create/update uninstall link if we are not an MSI install. MSI // installations are, for the time being, managed only through the // Add/Remove Programs dialog. // TODO(robertshield): We could add a shortcut to msiexec /X {GUID} here. - if (!InstallUtil::IsMSIProcess(system_install)) { + if (ret && !product.IsMsi()) { FilePath uninstall_link(shortcut_path); // Uninstall Chrome link uninstall_link = uninstall_link.Append( - dist->GetUninstallLinkName() + L".lnk"); + browser_dist->GetUninstallLinkName() + L".lnk"); if ((install_status == installer_util::FIRST_INSTALL_SUCCESS) || (install_status == installer_util::INSTALL_REPAIRED) || (file_util::PathExists(uninstall_link))) { if (!file_util::PathExists(shortcut_path)) - file_util::CreateDirectoryW(shortcut_path); - std::wstring setup_exe(installer::GetInstallerPathUnderChrome( - install_path, new_version)); - file_util::AppendToPath(&setup_exe, - file_util::GetFilenameFromPath(exe_path)); - - CommandLine arguments(CommandLine::NO_PROGRAM); - AppendUninstallCommandLineFlags(&arguments, system_install); - VLOG(1) << "Creating/updating uninstall link at " - << uninstall_link.value(); - ret = ret && file_util::CreateShortcutLink(setup_exe.c_str(), - uninstall_link.value().c_str(), - NULL, - arguments.command_line_string().c_str(), - NULL, - setup_exe.c_str(), - 0, - NULL); + file_util::CreateDirectory(shortcut_path); + + FilePath setup_exe( + product.package().GetInstallerDirectory(new_version) + .Append(setup_path.BaseName())); + + CommandLine arguments(CommandLine::NO_PROGRAM); + AppendUninstallCommandLineFlags(&arguments, product); + VLOG(1) << "Creating/updating uninstall link at " + << uninstall_link.value(); + ret = file_util::CreateShortcutLink(setup_exe.value().c_str(), + uninstall_link.value().c_str(), + NULL, + arguments.command_line_string().c_str(), + NULL, + setup_exe.value().c_str(), + 0, + NULL); } } // Update Desktop and Quick Launch shortcuts. If --create-new-shortcuts // is specified we want to create them, otherwise we update them only if // they exist. - if (system_install) { - ret = ret && ShellUtil::CreateChromeDesktopShortcut(chrome_exe, - product_desc, ShellUtil::SYSTEM_LEVEL, alt_shortcut, - create_all_shortcut); - ret = ret && ShellUtil::CreateChromeQuickLaunchShortcut(chrome_exe, - ShellUtil::CURRENT_USER | ShellUtil::SYSTEM_LEVEL, create_all_shortcut); - } else { - ret = ret && ShellUtil::CreateChromeDesktopShortcut(chrome_exe, - product_desc, ShellUtil::CURRENT_USER, alt_shortcut, - create_all_shortcut); - ret = ret && ShellUtil::CreateChromeQuickLaunchShortcut(chrome_exe, - ShellUtil::CURRENT_USER, create_all_shortcut); + if (ret) { + if (product.system_level()) { + ret = ShellUtil::CreateChromeDesktopShortcut(product.distribution(), + chrome_exe.value(), product_desc, ShellUtil::SYSTEM_LEVEL, + alt_shortcut, create_all_shortcut); + if (ret) { + ret = ShellUtil::CreateChromeQuickLaunchShortcut( + product.distribution(), chrome_exe.value(), + ShellUtil::CURRENT_USER | ShellUtil::SYSTEM_LEVEL, + create_all_shortcut); + } + } else { + ret = ShellUtil::CreateChromeDesktopShortcut(product.distribution(), + chrome_exe.value(), product_desc, ShellUtil::CURRENT_USER, + alt_shortcut, create_all_shortcut); + if (ret) { + ret = ShellUtil::CreateChromeQuickLaunchShortcut( + product.distribution(), chrome_exe.value(), ShellUtil::CURRENT_USER, + create_all_shortcut); + } + } } return ret; } +bool RegisterComDlls(const Package& install, + const Version* current_version, + const Version& new_version) { + // TODO(tommi): setup.exe should always have at least one DLL to register. + // Currently we rely on scan_server_dlls.py to populate the array for us, + // but we might as well use an explicit static array of required components. + if (kNumDllsToRegister <= 0) { + NOTREACHED() << "no dlls to register"; + return false; + } + + // Unregister DLLs that were left from the old version that is being upgraded. + if (current_version) { + FilePath old_dll_path(install.path().Append(current_version->GetString())); + // Ignore failures to unregister old DLLs. + installer::RegisterComDllList(old_dll_path, install.system_level(), false, + false); + } + + FilePath dll_path(install.path().Append(new_version.GetString())); + return installer::RegisterComDllList(dll_path, install.system_level(), true, + true); +} + // After a successful copying of all the files, this function is called to // do a few post install tasks: -// - Handle the case of in-use-update by updating "opv" key or deleting it if -// not required. +// - Handle the case of in-use-update by updating "opv" (old version) key or +// deleting it if not required. // - Register any new dlls and unregister old dlls. // - If this is an MSI install, ensures that the MSI marker is set, and sets // it if not. // If these operations are successful, the function returns true, otherwise // false. -bool DoPostInstallTasks(HKEY reg_root, - const std::wstring& exe_path, - const std::wstring& install_path, - const std::wstring& new_chrome_exe, - const std::wstring& current_version, - const installer::Version& new_version) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - std::wstring version_key = dist->GetVersionKey(); - - bool is_system_install = (reg_root == HKEY_LOCAL_MACHINE); - const installer_util::MasterPreferences& prefs = - InstallUtil::GetMasterPreferencesForCurrentProcess(); - - if (file_util::PathExists(FilePath::FromWStringHack(new_chrome_exe))) { +bool DoPostInstallTasks(const FilePath& setup_path, + const FilePath& new_chrome_exe, + const Version* current_version, + const Version& new_version, + const Package& package) { + HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; + const Products& products = package.products(); + + if (file_util::PathExists(new_chrome_exe)) { // Looks like this was in use update. So make sure we update the 'opv' key // with the current version that is active and 'cmd' key with the rename // command to run. - if (current_version.empty()) { - LOG(ERROR) << "New chrome.exe exists but current version is empty!"; + if (!current_version) { + LOG(ERROR) << "New chrome.exe exists but current version is NULL!"; return false; } - scoped_ptr<WorkItemList> inuse_list(WorkItem::CreateWorkItemList()); - inuse_list->AddSetRegValueWorkItem(reg_root, - version_key, - google_update::kRegOldVersionField, - current_version.c_str(), - true); - FilePath installer_path(installer::GetInstallerPathUnderChrome(install_path, - new_version.GetString())); - installer_path = installer_path.Append( - file_util::GetFilenameFromPath(exe_path)); - CommandLine rename_cmd(installer_path); - rename_cmd.AppendSwitch(installer_util::switches::kRenameChromeExe); - if (is_system_install) - rename_cmd.AppendSwitch(installer_util::switches::kSystemLevel); + scoped_ptr<WorkItemList> inuse_list(WorkItem::CreateWorkItemList()); + FilePath installer_path(package.GetInstallerDirectory(new_version) + .Append(setup_path.BaseName())); - if (prefs.install_chrome_frame()) - rename_cmd.AppendSwitch(installer_util::switches::kChromeFrame); + CommandLine rename(installer_path); + rename.AppendSwitch(installer_util::switches::kRenameChromeExe); + if (package.system_level()) + rename.AppendSwitch(installer_util::switches::kSystemLevel); if (InstallUtil::IsChromeSxSProcess()) - rename_cmd.AppendSwitch(installer_util::switches::kChromeSxS); + rename.AppendSwitch(installer_util::switches::kChromeSxS); + + for (size_t i = 0; i < products.size(); ++i) { + BrowserDistribution* dist = products[i]->distribution(); + std::wstring version_key(dist->GetVersionKey()); + inuse_list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegOldVersionField, + current_version->GetString(), true); + + // Adding this registry entry for all products is overkill. + // However, as it stands, we don't have a way to know which distribution + // will check the key and run the command, so we add it for all. + // After the first run, the subsequent runs should just be noops. + // (see Upgrade::SwapNewChromeExeIfPresent). + inuse_list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegRenameCmdField, + rename.command_line_string(), true); + } - inuse_list->AddSetRegValueWorkItem(reg_root, - version_key, - google_update::kRegRenameCmdField, - rename_cmd.command_line_string(), - true); if (!inuse_list->Do()) { LOG(ERROR) << "Couldn't write opv/cmd values to registry."; inuse_list->Rollback(); return false; } } else { - // Since this was not in-use-update, delete 'opv' and 'cmd' keys. + // Since this was not an in-use-update, delete 'opv' and 'cmd' keys. scoped_ptr<WorkItemList> inuse_list(WorkItem::CreateWorkItemList()); - inuse_list->AddDeleteRegValueWorkItem(reg_root, version_key, - google_update::kRegOldVersionField, - true); - inuse_list->AddDeleteRegValueWorkItem(reg_root, version_key, - google_update::kRegRenameCmdField, - true); + for (size_t i = 0; i < products.size(); ++i) { + BrowserDistribution* dist = products[i]->distribution(); + std::wstring version_key(dist->GetVersionKey()); + inuse_list->AddDeleteRegValueWorkItem(root, version_key, + google_update::kRegOldVersionField, + true); + inuse_list->AddDeleteRegValueWorkItem(root, version_key, + google_update::kRegRenameCmdField, + true); + } + if (!inuse_list->Do()) { LOG(ERROR) << "Couldn't delete opv/cmd values from registry."; inuse_list->Rollback(); @@ -457,55 +513,27 @@ bool DoPostInstallTasks(HKEY reg_root, } } - if (prefs.install_chrome_frame()) { - // TODO(tommi): setup.exe should always have at least one DLL to - // register. Currently we rely on scan_server_dlls.py to populate - // the array for us, but we might as well use an explicit static - // array of required components. - if (kNumDllsToRegister <= 0) { - NOTREACHED() << "no dlls to register"; - return false; - } - - // any that were left from the old version that is being upgraded: - if (!current_version.empty()) { - std::wstring old_dll_path(install_path); - file_util::AppendToPath(&old_dll_path, current_version); - scoped_ptr<WorkItemList> old_dll_list(WorkItem::CreateWorkItemList()); - if (InstallUtil::BuildDLLRegistrationList(old_dll_path, kDllsToRegister, - kNumDllsToRegister, false, - !is_system_install, - old_dll_list.get())) { - // Don't abort the install as a result of a failure to unregister old - // DLLs. - old_dll_list->Do(); - } - } - - std::wstring dll_path(install_path); - file_util::AppendToPath(&dll_path, new_version.GetString()); - scoped_ptr<WorkItemList> dll_list(WorkItem::CreateWorkItemList()); - if (InstallUtil::BuildDLLRegistrationList(dll_path, kDllsToRegister, - kNumDllsToRegister, true, - !is_system_install, - dll_list.get())) { - if (!dll_list->Do()) { - dll_list->Rollback(); - return false; - } - } + if (FindProduct(products, BrowserDistribution::CHROME_FRAME) || + FindProduct(products, BrowserDistribution::CEEE)) { + // TODO(robershield): move the "which DLLs should be registered" policy + // into the installer. + RegisterComDlls(package, current_version, new_version); } // If we're told that we're an MSI install, make sure to set the marker // in the client state key so that future updates do the right thing. - if (InstallUtil::IsMSIProcess(is_system_install)) { - if (!InstallUtil::SetMSIMarker(is_system_install, true)) - return false; + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + if (product->IsMsi()) { + if (!product->SetMsiMarker(true)) + return false; - // We want MSI installs to take over the Add/Remove Programs shortcut. Make - // a best-effort attempt to delete any shortcuts left over from previous - // non-MSI installations for the same type of install (system or per user). - DeleteUninstallShortcutsForMSI(is_system_install); + // We want MSI installs to take over the Add/Remove Programs shortcut. + // Make a best-effort attempt to delete any shortcuts left over from + // previous non-MSI installations for the same type of install (system or + // per user). + DeleteUninstallShortcutsForMSI(*product); + } } return true; @@ -529,9 +557,11 @@ bool Is64bit() { return false; } -void RegisterChromeOnMachine(const std::wstring& install_path, - bool system_level, +void RegisterChromeOnMachine(const Product& product, bool make_chrome_default) { + DCHECK_EQ(product.distribution()->GetType(), + BrowserDistribution::CHROME_BROWSER); + // Try to add Chrome to Media Player shim inclusion list. We don't do any // error checking here because this operation will fail if user doesn't // have admin rights and we want to ignore the error. @@ -539,246 +569,229 @@ void RegisterChromeOnMachine(const std::wstring& install_path, // Is --make-chrome-default option is given we make Chrome default browser // otherwise we only register it on the machine as a valid browser. - std::wstring chrome_exe(install_path); - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); - VLOG(1) << "Registering Chrome as browser"; + FilePath chrome_exe( + product.package().path().Append(installer_util::kChromeExe)); + VLOG(1) << "Registering Chrome as browser: " << chrome_exe.value(); if (make_chrome_default) { int level = ShellUtil::CURRENT_USER; - if (system_level) + if (product.system_level()) level = level | ShellUtil::SYSTEM_LEVEL; - ShellUtil::MakeChromeDefault(level, chrome_exe, true); + ShellUtil::MakeChromeDefault(product.distribution(), level, + chrome_exe.value(), true); } else { - ShellUtil::RegisterChromeBrowser(chrome_exe, L"", false); + ShellUtil::RegisterChromeBrowser(product.distribution(), chrome_exe.value(), + L"", false); } } // This function installs a new version of Chrome to the specified location. // -// exe_path: Path to the executable (setup.exe) as it will be copied +// setup_path: Path to the executable (setup.exe) as it will be copied // to Chrome install folder after install is complete // archive_path: Path to the archive (chrome.7z) as it will be copied // to Chrome install folder after install is complete // src_path: the path that contains a complete and unpacked Chrome package // to be installed. -// install_path: the destination path for Chrome to be installed to. This -// path does not need to exist. // temp_dir: the path of working directory used during installation. This path // does not need to exist. -// reg_root: the root of registry where the function applies settings for the -// new Chrome version. It should be either HKLM or HKCU. // new_version: new Chrome version that needs to be installed -// current_version: returns the current active version (if any) +// oldest_installed_version: returns the oldest active version (if any) // // This function makes best effort to do installation in a transactional // manner. If failed it tries to rollback all changes on the file system -// and registry. For example, if install_path exists before calling the +// and registry. For example, if package exists before calling the // function, it rolls back all new file and directory changes under -// install_path. If install_path does not exist before calling the function -// (typical new install), the function creates install_path during install +// package. If package does not exist before calling the function +// (typical new install), the function creates package during install // and removes the whole directory during rollback. installer_util::InstallStatus InstallNewVersion( - const std::wstring& exe_path, - const std::wstring& archive_path, - const std::wstring& src_path, - const std::wstring& install_path, - const std::wstring& temp_dir, - const HKEY reg_root, - const installer::Version& new_version, - std::wstring* current_version) { - if (reg_root != HKEY_LOCAL_MACHINE && reg_root != HKEY_CURRENT_USER) - return installer_util::INSTALL_FAILED; - - if (InstallUtil::IsChromeFrameProcess()) { + const FilePath& setup_path, + const FilePath& archive_path, + const FilePath& src_path, + const FilePath& temp_dir, + const Version& new_version, + scoped_ptr<Version>* current_version, + const Package& package) { + DCHECK(current_version); + + const Products& products = package.products(); + DCHECK(products.size()); + + if (FindProduct(products, BrowserDistribution::CHROME_FRAME)) { // Make sure that we don't end up deleting installed files on next reboot. - if (!RemoveFromMovesPendingReboot(install_path.c_str())) { + if (!RemoveFromMovesPendingReboot(package.path().value().c_str())) { LOG(ERROR) << "Error accessing pending moves value."; } } + // TODO(tommi): See if we can't get rid of this parameter. + current_version->reset(package.GetCurrentVersion()); + scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); // A temp directory that work items need and the actual install directory. - install_list->AddCreateDirWorkItem(FilePath::FromWStringHack(temp_dir)); - install_list->AddCreateDirWorkItem(FilePath::FromWStringHack(install_path)); + install_list->AddCreateDirWorkItem(temp_dir); + install_list->AddCreateDirWorkItem(package.path()); // Delete any new_chrome.exe if present (we will end up creating a new one // if required) and then copy chrome.exe - std::wstring new_chrome_exe = AppendPath(install_path, - installer_util::kChromeNewExe); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - base::win::RegKey chrome_key(reg_root, dist->GetVersionKey().c_str(), - KEY_READ); - if (file_util::PathExists(FilePath::FromWStringHack(new_chrome_exe))) - chrome_key.ReadValue(google_update::kRegOldVersionField, current_version); - - if (current_version->empty()) - chrome_key.ReadValue(google_update::kRegVersionField, current_version); - - chrome_key.Close(); + FilePath new_chrome_exe( + package.path().Append(installer_util::kChromeNewExe)); - install_list->AddDeleteTreeWorkItem(new_chrome_exe, std::wstring()); + install_list->AddDeleteTreeWorkItem(new_chrome_exe.value(), std::wstring()); install_list->AddCopyTreeWorkItem( - AppendPath(src_path, installer_util::kChromeExe), - AppendPath(install_path, installer_util::kChromeExe), - temp_dir, WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe); + src_path.Append(installer_util::kChromeExe).value(), + package.path().Append(installer_util::kChromeExe).value(), + temp_dir.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value()); // Extra executable for 64 bit systems. if (Is64bit()) { install_list->AddCopyTreeWorkItem( - AppendPath(src_path, installer::kWowHelperExe), - AppendPath(install_path, installer::kWowHelperExe), - temp_dir, WorkItem::ALWAYS); + src_path.Append(installer::kWowHelperExe).value(), + package.path().Append(installer::kWowHelperExe).value(), + temp_dir.value(), WorkItem::ALWAYS); } // If it is system level install copy the version folder (since we want to // take the permissions of %ProgramFiles% folder) otherwise just move it. - if (reg_root == HKEY_LOCAL_MACHINE) { + if (package.system_level()) { install_list->AddCopyTreeWorkItem( - AppendPath(src_path, new_version.GetString()), - AppendPath(install_path, new_version.GetString()), - temp_dir, WorkItem::ALWAYS); + src_path.Append(new_version.GetString()).value(), + package.path().Append(new_version.GetString()).value(), + temp_dir.value(), WorkItem::ALWAYS); } else { install_list->AddMoveTreeWorkItem( - AppendPath(src_path, new_version.GetString()), - AppendPath(install_path, new_version.GetString()), - temp_dir); + src_path.Append(new_version.GetString()).value(), + package.path().Append(new_version.GetString()).value(), + temp_dir.value()); } - // Copy the default Dictionaries only if the folder doesnt exist already + // Copy the default Dictionaries only if the folder doesn't exist already. install_list->AddCopyTreeWorkItem( - AppendPath(src_path, installer::kDictionaries), - AppendPath(install_path, installer::kDictionaries), - temp_dir, WorkItem::IF_NOT_PRESENT); - - // Copy installer in install directory and - // add shortcut in Control Panel->Add/Remove Programs. - AddInstallerCopyTasks(exe_path, archive_path, temp_dir, install_path, - new_version.GetString(), install_list.get(), - (reg_root == HKEY_LOCAL_MACHINE)); - std::wstring product_name = dist->GetAppShortCutName(); - - AddUninstallShortcutWorkItems(reg_root, exe_path, install_path, - product_name, new_version.GetString(), install_list.get()); + src_path.Append(installer::kDictionaries).value(), + package.path().Append(installer::kDictionaries).value(), + temp_dir.value(), WorkItem::IF_NOT_PRESENT); // Delete any old_chrome.exe if present. install_list->AddDeleteTreeWorkItem( - AppendPath(install_path, installer_util::kChromeOldExe), std::wstring()); - - // Create Version key (if not already present) and set the new Chrome - // version as last step. - std::wstring version_key = dist->GetVersionKey(); - install_list->AddCreateRegKeyWorkItem(reg_root, version_key); - install_list->AddSetRegValueWorkItem(reg_root, version_key, - google_update::kRegNameField, - product_name, - true); // overwrite name also - install_list->AddSetRegValueWorkItem(reg_root, version_key, - google_update::kRegOopcrashesField, - 1, - false); // set during first install - install_list->AddSetRegValueWorkItem(reg_root, version_key, - google_update::kRegVersionField, - new_version.GetString(), - true); // overwrite version + package.path().Append(installer_util::kChromeOldExe).value(), + std::wstring()); + + // Copy installer in install directory and + // add shortcut in Control Panel->Add/Remove Programs. + AddInstallerCopyTasks(setup_path, archive_path, temp_dir, new_version, + install_list.get(), package); + + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + + AddUninstallShortcutWorkItems(setup_path, new_version, install_list.get(), + *product); + + // Create Version key for each distribution (if not already present) and set + // the new product version as the last step. + HKEY root = product->system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; + std::wstring version_key(product->distribution()->GetVersionKey()); + install_list->AddCreateRegKeyWorkItem(root, version_key); + + std::wstring product_name(product->distribution()->GetAppShortCutName()); + install_list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegNameField, + product_name, + true); // overwrite name also + install_list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegOopcrashesField, + 1, + false); // set during first install + install_list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegVersionField, + new_version.GetString(), + true); // overwrite version + } if (!install_list->Do() || - !DoPostInstallTasks(reg_root, exe_path, install_path, - new_chrome_exe, *current_version, new_version)) { + !DoPostInstallTasks(setup_path, new_chrome_exe, current_version->get(), + new_version, package)) { installer_util::InstallStatus result = - (file_util::PathExists(FilePath::FromWStringHack(new_chrome_exe)) && - !current_version->empty() && - (new_version.GetString() == *current_version)) ? + file_util::PathExists(new_chrome_exe) && current_version->get() && + new_version.IsEqual(*current_version->get()) ? installer_util::SAME_VERSION_REPAIR_FAILED : installer_util::INSTALL_FAILED; - LOG(ERROR) << "Install failed, rolling back... "; + LOG(ERROR) << "Install failed, rolling back... result: " << result; install_list->Rollback(); LOG(ERROR) << "Rollback complete. "; return result; } - scoped_ptr<installer::Version> installed_version; - if (!current_version->empty()) - installed_version.reset( - installer::Version::GetVersionFromString(*current_version)); - if (!installed_version.get()) { + + if (!current_version->get()) { VLOG(1) << "First install of version " << new_version.GetString(); return installer_util::FIRST_INSTALL_SUCCESS; } - if (new_version.GetString() == installed_version->GetString()) { + + if (new_version.IsEqual(*current_version->get())) { VLOG(1) << "Install repaired of version " << new_version.GetString(); return installer_util::INSTALL_REPAIRED; } - if (new_version.IsHigherThan(installed_version.get())) { - if (file_util::PathExists(FilePath::FromWStringHack(new_chrome_exe))) { + + if (new_version.IsHigherThan(current_version->get())) { + if (file_util::PathExists(new_chrome_exe)) { VLOG(1) << "Version updated to " << new_version.GetString() - << " while running " << installed_version->GetString(); + << " while running " << (*current_version)->GetString(); return installer_util::IN_USE_UPDATED; } VLOG(1) << "Version updated to " << new_version.GetString(); return installer_util::NEW_VERSION_UPDATED; } + LOG(ERROR) << "Not sure how we got here while updating" << ", new version: " << new_version.GetString() - << ", old version: " << installed_version->GetString(); + << ", old version: " << (*current_version)->GetString(); + return installer_util::INSTALL_FAILED; } -} // namespace +} // end namespace -std::wstring installer::GetInstallerPathUnderChrome( - const std::wstring& install_path, const std::wstring& new_version) { - std::wstring installer_path(install_path); - file_util::AppendToPath(&installer_path, new_version); - file_util::AppendToPath(&installer_path, installer_util::kInstallerDir); - return installer_path; -} +namespace installer { -installer_util::InstallStatus installer::InstallOrUpdateChrome( - const std::wstring& exe_path, const std::wstring& archive_path, - const std::wstring& install_temp_path, const std::wstring& prefs_path, +installer_util::InstallStatus InstallOrUpdateChrome( + const FilePath& setup_path, const FilePath& archive_path, + const FilePath& install_temp_path, const FilePath& prefs_path, const installer_util::MasterPreferences& prefs, const Version& new_version, - const Version* installed_version) { - bool system_install = false; - prefs.GetBool(installer_util::master_preferences::kSystemLevel, - &system_install); - std::wstring install_path(GetChromeInstallPath(system_install)); - if (install_path.empty()) { - LOG(ERROR) << "Could not get installation destination path."; - return installer_util::INSTALL_FAILED; - } - VLOG(1) << "install destination path: " << install_path; + const Package& install) { + bool system_install = install.system_level(); - std::wstring src_path(install_temp_path); - file_util::AppendToPath(&src_path, std::wstring(kInstallSourceDir)); - file_util::AppendToPath(&src_path, std::wstring(kInstallSourceChromeDir)); + FilePath src_path(install_temp_path); + src_path = src_path.Append(kInstallSourceDir).Append(kInstallSourceChromeDir); - HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - std::wstring current_version; - installer_util::InstallStatus result = InstallNewVersion(exe_path, - archive_path, src_path, install_path, install_temp_path, reg_root, - new_version, ¤t_version); + scoped_ptr<Version> existing_version; + installer_util::InstallStatus result = InstallNewVersion(setup_path, + archive_path, src_path, install_temp_path, new_version, + &existing_version, install); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - if (!dist->GetInstallReturnCode(result)) { + if (!BrowserDistribution::GetInstallReturnCode(result)) { if (result == installer_util::FIRST_INSTALL_SUCCESS) - CopyPreferenceFileForFirstRun(system_install, prefs_path); - - bool value = false; - // TODO(tommi): Currently this only creates shortcuts for Chrome, but - // for other products we might want to create shortcuts. - if (prefs.install_chrome() && - (!prefs.GetBool( - installer_util::master_preferences::kDoNotCreateShortcuts, - &value) || !value)) { + CopyPreferenceFileForFirstRun(install, prefs_path); + + bool do_not_create_shortcuts = false; + prefs.GetBool(installer_util::master_preferences::kDoNotCreateShortcuts, + &do_not_create_shortcuts); + + // Currently this only creates shortcuts for Chrome, but for other products + // we might want to create shortcuts. + const Product* chrome_install = + FindProduct(install.products(), BrowserDistribution::CHROME_BROWSER); + if (chrome_install && !do_not_create_shortcuts) { bool create_all_shortcut = false; prefs.GetBool(installer_util::master_preferences::kCreateAllShortcuts, &create_all_shortcut); bool alt_shortcut = false; prefs.GetBool(installer_util::master_preferences::kAltShortcutText, &alt_shortcut); - if (!CreateOrUpdateChromeShortcuts(exe_path, install_path, - new_version.GetString(), result, - system_install, create_all_shortcut, + if (!CreateOrUpdateChromeShortcuts(setup_path, new_version, result, + *chrome_install, create_all_shortcut, alt_shortcut)) { - LOG(WARNING) << "Failed to create/update start menu shortcut."; + PLOG(WARNING) << "Failed to create/update start menu shortcut."; } bool make_chrome_default = false; @@ -797,15 +810,43 @@ installer_util::InstallStatus installer::InstallOrUpdateChrome( &force_chrome_default_for_user); } - RegisterChromeOnMachine(install_path, system_install, + RegisterChromeOnMachine(*chrome_install, make_chrome_default || force_chrome_default_for_user); } - std::wstring latest_version_to_keep(new_version.GetString()); - if (!current_version.empty()) - latest_version_to_keep.assign(current_version); - RemoveOldVersionDirs(install_path, latest_version_to_keep); + install.RemoveOldVersionDirectories(existing_version.get() ? + *existing_version.get() : new_version); } return result; } + +bool RegisterComDllList(const FilePath& dll_folder, bool system_level, + bool do_register, bool rollback_on_failure) { + bool success = false; + scoped_ptr<WorkItemList> work_item_list; + if (rollback_on_failure) { + work_item_list.reset(WorkItem::CreateWorkItemList()); + } else { + work_item_list.reset(WorkItem::CreateNoRollbackWorkItemList()); + } + + // TODO(robertshield): What if the list of old dlls and new ones isn't + // the same? I (elmo) think we should start storing the list of DLLs + // somewhere. + if (InstallUtil::BuildDLLRegistrationList(dll_folder.value(), kDllsToRegister, + kNumDllsToRegister, do_register, + !system_level, + work_item_list.get())) { + // Ignore failures to unregister old DLLs. + success = work_item_list->Do(); + if (!success && rollback_on_failure) { + work_item_list->Rollback(); + } + } + + return success; +} + +} // namespace installer + diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h index 1c8ccc4..bccb7e2 100644 --- a/chrome/installer/setup/install.h +++ b/chrome/installer/setup/install.h @@ -9,7 +9,9 @@ #pragma once #include <string> +#include <vector> +#include "chrome/installer/util/product.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/version.h" @@ -17,15 +19,13 @@ class DictionaryValue; namespace installer { -// Get path to the installer under Chrome version folder -// (for example <path>\Google\Chrome\<Version>\installer) -std::wstring GetInstallerPathUnderChrome(const std::wstring& install_path, - const std::wstring& new_version); + +class Package; // This function installs or updates a new version of Chrome. It returns // install status (failed, new_install, updated etc). // -// exe_path: Path to the executable (setup.exe) as it will be copied +// setup_path: Path to the executable (setup.exe) as it will be copied // to Chrome install folder after install is complete // archive_path: Path to the archive (chrome.7z) as it will be copied // to Chrome install folder after install is complete @@ -34,16 +34,25 @@ std::wstring GetInstallerPathUnderChrome(const std::wstring& install_path, // and unpacked Chrome package. // prefs: master preferences. See chrome/installer/util/master_preferences.h. // new_version: new Chrome version that needs to be installed -// installed_version: currently installed version of Chrome, if any, or -// NULL otherwise +// package: Represents the target installation folder and all distributions +// to be installed in that folder. // // Note: since caller unpacks Chrome to install_temp_path\source, the caller // is responsible for cleaning up install_temp_path. installer_util::InstallStatus InstallOrUpdateChrome( - const std::wstring& exe_path, const std::wstring& archive_path, - const std::wstring& install_temp_path, const std::wstring& prefs_path, + const FilePath& setup_path, const FilePath& archive_path, + const FilePath& install_temp_path, const FilePath& prefs_path, const installer_util::MasterPreferences& prefs, const Version& new_version, - const Version* installed_version); -} + const Package& package); + +// Registers or unregisters COM DLLs in a specific folder as declared in +// kDllsToRegister. +// TODO(robertshield): What if the list of old dlls and new ones isn't +// the same? I think we should start storing the list of DLLs somewhere as +// part of the installation data. +bool RegisterComDllList(const FilePath& dll_folder, bool system_level, + bool do_register, bool rollback_on_failure); + +} // namespace installer #endif // CHROME_INSTALLER_SETUP_INSTALL_H_ diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 7d0725c..d9ace56 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -44,6 +44,14 @@ #include "installer_util_strings.h" +using installer::Product; +using installer::ProductPackageMapping; +using installer::Products; +using installer::Package; +using installer::Packages; +using installer::Version; +using installer_util::MasterPreferences; + namespace { // This method unpacks and uncompresses the given archive file. For Chrome @@ -55,39 +63,41 @@ namespace { // (chrome.7z) or uncompressed archive patch file (chrome_patch.diff). If it // is patch file, it is applied to the old archive file that should be // present on the system already. As the final step the new archive file -// is unpacked in the path specified by parameter "path". -DWORD UnPackArchive(const std::wstring& archive, bool system_install, - const installer::Version* installed_version, - const std::wstring& temp_path, const std::wstring& path, +// is unpacked in the path specified by parameter "output_directory". +DWORD UnPackArchive(const FilePath& archive, + const Package& installation, + const FilePath& temp_path, + const FilePath& output_directory, bool& incremental_install) { // First uncompress the payload. This could be a differential // update (patch.7z) or full archive (chrome.7z). If this uncompress fails // return with error. std::wstring unpacked_file; - int32 ret = LzmaUtil::UnPackArchive(archive, temp_path, &unpacked_file); + int32 ret = LzmaUtil::UnPackArchive(archive.value(), temp_path.value(), + &unpacked_file); if (ret != NO_ERROR) return ret; - std::wstring uncompressed_archive(temp_path); - file_util::AppendToPath(&uncompressed_archive, installer::kChromeArchive); + FilePath uncompressed_archive(temp_path.Append(installer::kChromeArchive)); + scoped_ptr<Version> archive_version( + setup_util::GetVersionFromArchiveDir(installation.path())); // Check if this is differential update and if it is, patch it to the // installer archive that should already be on the machine. We assume // it is a differential installer if chrome.7z is not found. - if (!file_util::PathExists(FilePath::FromWStringHack(uncompressed_archive))) { + if (!file_util::PathExists(uncompressed_archive)) { incremental_install = true; VLOG(1) << "Differential patch found. Applying to existing archive."; - if (!installed_version) { + if (!archive_version.get()) { LOG(ERROR) << "Can not use differential update when Chrome is not " << "installed on the system."; return installer_util::CHROME_NOT_INSTALLED; } - std::wstring existing_archive = - installer::GetChromeInstallPath(system_install); - file_util::AppendToPath(&existing_archive, - installed_version->GetString()); - file_util::AppendToPath(&existing_archive, installer_util::kInstallerDir); - file_util::AppendToPath(&existing_archive, installer::kChromeArchive); + + FilePath existing_archive( + installation.path().Append(archive_version->GetString())); + existing_archive = existing_archive.Append(installer_util::kInstallerDir); + existing_archive = existing_archive.Append(installer::kChromeArchive); if (int i = setup_util::ApplyDiffPatch(FilePath(existing_archive), FilePath(unpacked_file), FilePath(uncompressed_archive))) { @@ -97,7 +107,8 @@ DWORD UnPackArchive(const std::wstring& archive, bool system_install, } // Unpack the uncompressed archive. - return LzmaUtil::UnPackArchive(uncompressed_archive, path, &unpacked_file); + return LzmaUtil::UnPackArchive(uncompressed_archive.value(), + output_directory.value(), &unpacked_file); } @@ -106,39 +117,45 @@ DWORD UnPackArchive(const std::wstring& archive, bool system_install, // for Chrome so there should be a file called new_chrome.exe on the file // system and a key called 'opv' in the registry. This function will move // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation. -installer_util::InstallStatus RenameChromeExecutables(bool system_install) { - std::wstring chrome_path(installer::GetChromeInstallPath(system_install)); - - std::wstring chrome_exe(chrome_path); - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); - std::wstring chrome_old_exe(chrome_path); - file_util::AppendToPath(&chrome_old_exe, installer_util::kChromeOldExe); - std::wstring chrome_new_exe(chrome_path); - file_util::AppendToPath(&chrome_new_exe, installer_util::kChromeNewExe); +installer_util::InstallStatus RenameChromeExecutables( + const Package& installation) { + FilePath chrome_exe(installation.path().Append(installer_util::kChromeExe)); + FilePath chrome_old_exe(installation.path().Append( + installer_util::kChromeOldExe)); + FilePath chrome_new_exe(installation.path().Append( + installer_util::kChromeNewExe)); scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); - install_list->AddDeleteTreeWorkItem(chrome_old_exe, std::wstring()); + install_list->AddDeleteTreeWorkItem(chrome_old_exe.value(), std::wstring()); FilePath temp_path; if (!file_util::CreateNewTempDirectory(L"chrome_", &temp_path)) { LOG(ERROR) << "Failed to create Temp directory " << temp_path.value(); return installer_util::RENAME_FAILED; } - install_list->AddCopyTreeWorkItem(chrome_new_exe, - chrome_exe, + + install_list->AddCopyTreeWorkItem(chrome_new_exe.value(), + chrome_exe.value(), temp_path.ToWStringHack(), WorkItem::IF_DIFFERENT, std::wstring()); - HKEY reg_root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - BrowserDistribution *dist = BrowserDistribution::GetDistribution(); - install_list->AddDeleteRegValueWorkItem(reg_root, - dist->GetVersionKey(), - google_update::kRegOldVersionField, - true); - install_list->AddDeleteTreeWorkItem(chrome_new_exe, std::wstring()); - install_list->AddDeleteRegValueWorkItem(reg_root, - dist->GetVersionKey(), - google_update::kRegRenameCmdField, - true); + install_list->AddDeleteTreeWorkItem(chrome_new_exe.value(), std::wstring()); + + HKEY reg_root = installation.system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; + const Products& products = installation.products(); + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + BrowserDistribution* browser_dist = product->distribution(); + std::wstring version_key(browser_dist->GetVersionKey()); + install_list->AddDeleteRegValueWorkItem(reg_root, + version_key, + google_update::kRegOldVersionField, + true); + install_list->AddDeleteRegValueWorkItem(reg_root, + version_key, + google_update::kRegRenameCmdField, + true); + } installer_util::InstallStatus ret = installer_util::RENAME_SUCCESSFUL; if (!install_list->Do()) { LOG(ERROR) << "Renaming of executables failed. Rolling back any changes."; @@ -149,67 +166,82 @@ installer_util::InstallStatus RenameChromeExecutables(bool system_install) { return ret; } -bool CheckPreInstallConditions(const installer::Version* installed_version, - bool system_install, +bool CheckPreInstallConditions(const Package& installation, installer_util::InstallStatus& status) { - bool is_first_install = (NULL == installed_version); - // Check to avoid simultaneous per-user and per-machine installs. - scoped_ptr<installer::Version> - chrome_version(InstallUtil::GetChromeVersion(!system_install)); - if (chrome_version.get()) { - LOG(ERROR) << "Already installed version " << chrome_version->GetString() - << " conflicts with the current install mode."; - if (!system_install && is_first_install) { - // This is user-level install and there is a system-level chrome - // installation. Instruct omaha to launch the existing one. There - // should be no error dialog. - std::wstring chrome_exe(installer::GetChromeInstallPath(!system_install)); - if (chrome_exe.empty()) { - // If we failed to construct install path. Give up. - status = installer_util::OS_ERROR; - InstallUtil::WriteInstallerResult(system_install, status, - IDS_INSTALL_OS_ERROR_BASE, NULL); - return false; - } else { - status = installer_util::EXISTING_VERSION_LAUNCHED; - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); - chrome_exe = L"\"" + chrome_exe + L"\" --" - + ASCIIToWide(switches::kFirstRun); - InstallUtil::WriteInstallerResult(system_install, status, - 0, NULL); - VLOG(1) << "Launching existing system-level chrome instead."; - base::LaunchApp(chrome_exe, false, false, NULL); + const Products& products = installation.products(); + DCHECK(products.size()); + + bool is_first_install = true; + + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + BrowserDistribution* browser_dist = product->distribution(); + scoped_ptr<Version> installed_version(product->GetInstalledVersion()); + // TODO(tommi): If the current install is for a different distribution than + // that might already be installed, then this check is incorrect. + if (installed_version.get()) + is_first_install = false; + + // Check to avoid simultaneous per-user and per-machine installs. + scoped_ptr<Version> chrome_version( + InstallUtil::GetChromeVersion(browser_dist, !product->system_level())); + + if (chrome_version.get()) { + LOG(ERROR) << "Already installed version " << chrome_version->GetString() + << " conflicts with the current install mode."; + if (!product->system_level() && is_first_install) { + // This is user-level install and there is a system-level chrome + // installation. Instruct Omaha to launch the existing one. There + // should be no error dialog. + FilePath chrome_exe(installer::GetChromeInstallPath( + !product->system_level(), browser_dist)); + if (chrome_exe.empty()) { + // If we failed to construct install path. Give up. + status = installer_util::OS_ERROR; + product->WriteInstallerResult(status, IDS_INSTALL_OS_ERROR_BASE, + NULL); + } else { + status = installer_util::EXISTING_VERSION_LAUNCHED; + chrome_exe = chrome_exe.Append(installer_util::kChromeExe); + CommandLine cmd(chrome_exe); + cmd.AppendSwitch(switches::kFirstRun); + product->WriteInstallerResult(status, 0, NULL); + VLOG(1) << "Launching existing system-level chrome instead."; + base::LaunchApp(cmd, false, false, NULL); + } return false; } + + // If the following compile assert fires it means that the InstallStatus + // enumeration changed which will break the contract between the old + // chrome installed and the new setup.exe that is trying to upgrade. + COMPILE_ASSERT(installer_util::SXS_OPTION_NOT_SUPPORTED == 33, + dont_change_enum); + + // This is an update, not an install. Omaha should know the difference + // and not show a dialog. + status = product->system_level() ? + installer_util::USER_LEVEL_INSTALL_EXISTS : + installer_util::SYSTEM_LEVEL_INSTALL_EXISTS; + int str_id = product->system_level() ? + IDS_INSTALL_USER_LEVEL_EXISTS_BASE : + IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE; + product->WriteInstallerResult(status, str_id, NULL); + return false; } - // If the following compile assert fires it means that the InstallStatus - // enumeration changed which will break the contract between the old chrome - // installed and the new setup.exe that is trying to upgrade. - COMPILE_ASSERT(installer_util::SXS_OPTION_NOT_SUPPORTED == 33, - dont_change_enum); - - // This is an update, not an install. Omaha should know the difference - // and not show a dialog. - status = system_install ? installer_util::USER_LEVEL_INSTALL_EXISTS : - installer_util::SYSTEM_LEVEL_INSTALL_EXISTS; - int str_id = system_install ? IDS_INSTALL_USER_LEVEL_EXISTS_BASE : - IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE; - InstallUtil::WriteInstallerResult(system_install, status, str_id, NULL); - return false; } + // If no previous installation of Chrome, make sure installation directory // either does not exist or can be deleted (i.e. is not locked by some other // process). if (is_first_install) { - FilePath install_path = FilePath::FromWStringHack( - installer::GetChromeInstallPath(system_install)); - if (file_util::PathExists(install_path) && - !file_util::Delete(install_path, true)) { - LOG(ERROR) << "Installation directory " << install_path.value() + if (file_util::PathExists(installation.path()) && + !file_util::Delete(installation.path(), true)) { + LOG(ERROR) << "Installation directory " << installation.path().value() << " exists and can not be deleted."; status = installer_util::INSTALL_DIR_IN_USE; int str_id = IDS_INSTALL_DIR_IN_USE_BASE; - InstallUtil::WriteInstallerResult(system_install, status, str_id, NULL); + WriteInstallerResult(products, status, str_id, NULL); return false; } } @@ -218,21 +250,15 @@ bool CheckPreInstallConditions(const installer::Version* installed_version, } installer_util::InstallStatus InstallChrome(const CommandLine& cmd_line, - const installer::Version* installed_version, - const installer_util::MasterPreferences& prefs) { - bool system_level = false; - prefs.GetBool(installer_util::master_preferences::kSystemLevel, - &system_level); + const Package& installation, const MasterPreferences& prefs) { installer_util::InstallStatus install_status = installer_util::UNKNOWN_STATUS; - if (!CheckPreInstallConditions(installed_version, - system_level, install_status)) + if (!CheckPreInstallConditions(installation, install_status)) return install_status; // For install the default location for chrome.packed.7z is in current // folder, so get that value first. - FilePath archive = - cmd_line.GetProgram().DirName().Append( - installer::kChromeCompressedArchive); + FilePath archive(cmd_line.GetProgram().DirName().Append( + installer::kChromeCompressedArchive)); // If --install-archive is given, get the user specified value if (cmd_line.HasSwitch(installer_util::switches::kInstallArchive)) { @@ -240,120 +266,133 @@ installer_util::InstallStatus InstallChrome(const CommandLine& cmd_line, installer_util::switches::kInstallArchive); } VLOG(1) << "Archive found to install Chrome " << archive.value(); + bool system_level = installation.system_level(); + const Products& products = installation.products(); // Create a temp folder where we will unpack Chrome archive. If it fails, // then we are doomed, so return immediately and no cleanup is required. FilePath temp_path; if (!file_util::CreateNewTempDirectory(L"chrome_", &temp_path)) { LOG(ERROR) << "Could not create temporary path."; - InstallUtil::WriteInstallerResult(system_level, - installer_util::TEMP_DIR_FAILED, - IDS_INSTALL_TEMP_DIR_FAILED_BASE, - NULL); + WriteInstallerResult(products, installer_util::TEMP_DIR_FAILED, + IDS_INSTALL_TEMP_DIR_FAILED_BASE, NULL); return installer_util::TEMP_DIR_FAILED; } VLOG(1) << "created path " << temp_path.value(); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - std::wstring unpack_path(temp_path.ToWStringHack()); - file_util::AppendToPath(&unpack_path, - std::wstring(installer::kInstallSourceDir)); + FilePath unpack_path(temp_path.Append(installer::kInstallSourceDir)); bool incremental_install = false; - if (UnPackArchive(archive.value(), system_level, installed_version, - temp_path.ToWStringHack(), unpack_path, + if (UnPackArchive(archive, installation,temp_path, unpack_path, incremental_install)) { install_status = installer_util::UNCOMPRESSION_FAILED; - InstallUtil::WriteInstallerResult(system_level, install_status, - IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, - NULL); + WriteInstallerResult(products, install_status, + IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, NULL); } else { - VLOG(1) << "unpacked to " << unpack_path; - std::wstring src_path(unpack_path); - file_util::AppendToPath(&src_path, - std::wstring(installer::kInstallSourceChromeDir)); - scoped_ptr<installer::Version> - installer_version(setup_util::GetVersionFromDir(FilePath(src_path))); + VLOG(1) << "unpacked to " << unpack_path.value(); + FilePath src_path(unpack_path.Append(installer::kInstallSourceChromeDir)); + scoped_ptr<Version> + installer_version(setup_util::GetVersionFromArchiveDir(src_path)); if (!installer_version.get()) { LOG(ERROR) << "Did not find any valid version in installer."; install_status = installer_util::INVALID_ARCHIVE; - InstallUtil::WriteInstallerResult(system_level, install_status, - IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); + WriteInstallerResult(products, install_status, + IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); } else { + // TODO(tommi): Move towards having only a single version that is common + // to all products. Only the package should have a version since it + // represents all the binaries. When a single product is upgraded, all + // currently installed product for the shared binary installation, should + // (or rather must) be upgraded. VLOG(1) << "version to install: " << installer_version->GetString(); - if (installed_version && - installed_version->IsHigherThan(installer_version.get())) { - LOG(ERROR) << "Higher version is already installed."; - install_status = installer_util::HIGHER_VERSION_EXISTS; - - int result_resource_id = IDS_INSTALL_HIGHER_VERSION_BASE; - if (InstallUtil::IsChromeFrameProcess()) { - result_resource_id = IDS_INSTALL_HIGHER_VERSION_CF_BASE; + bool higher_version_installed = false; + for (size_t i = 0; i < installation.products().size(); ++i) { + const Product* product = installation.products()[i]; + scoped_ptr<Version> v(product->GetInstalledVersion()); + if (v.get() && v->IsHigherThan(installer_version.get())) { + LOG(ERROR) << "Higher version is already installed."; + higher_version_installed = true; + install_status = installer_util::HIGHER_VERSION_EXISTS; + + if (product->distribution()->GetType() != + BrowserDistribution::CHROME_BROWSER) { + // TODO(robertshield): We should take the installer result text + // strings from the Product. + product->WriteInstallerResult(install_status, + IDS_INSTALL_HIGHER_VERSION_BASE, NULL); + } else { + product->WriteInstallerResult(install_status, + IDS_INSTALL_HIGHER_VERSION_CF_BASE, NULL); + } } - InstallUtil::WriteInstallerResult(system_level, - install_status, - result_resource_id, - NULL); - } else { + } + + if (!higher_version_installed) { // We want to keep uncompressed archive (chrome.7z) that we get after // uncompressing and binary patching. Get the location for this file. - FilePath archive_to_copy = temp_path.Append(installer::kChromeArchive); - std::wstring prefs_source_path = cmd_line.GetSwitchValueNative( - installer_util::switches::kInstallerData); - install_status = installer::InstallOrUpdateChrome( - cmd_line.GetProgram().value(), archive_to_copy.value(), - temp_path.ToWStringHack(), - prefs_source_path, prefs, *installer_version, installed_version); + FilePath archive_to_copy(temp_path.Append(installer::kChromeArchive)); + FilePath prefs_source_path(cmd_line.GetSwitchValueNative( + installer_util::switches::kInstallerData)); + install_status = installer::InstallOrUpdateChrome(cmd_line.GetProgram(), + archive_to_copy, temp_path, prefs_source_path, prefs, + *installer_version, installation); int install_msg_base = IDS_INSTALL_FAILED_BASE; std::wstring chrome_exe; if (install_status == installer_util::SAME_VERSION_REPAIR_FAILED) { - if (InstallUtil::IsChromeFrameProcess()) { + if (FindProduct(products, BrowserDistribution::CHROME_FRAME)) { install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_CF_BASE; } else { install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE; } } else if (install_status != installer_util::INSTALL_FAILED) { - chrome_exe = installer::GetChromeInstallPath(system_level); - if (chrome_exe.empty()) { + if (installation.path().empty()) { // If we failed to construct install path, it means the OS call to // get %ProgramFiles% or %AppData% failed. Report this as failure. install_msg_base = IDS_INSTALL_OS_ERROR_BASE; install_status = installer_util::OS_ERROR; } else { - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + chrome_exe = installation.path() + .Append(installer_util::kChromeExe).value(); chrome_exe = L"\"" + chrome_exe + L"\""; install_msg_base = 0; } } + const Product* chrome_install = + FindProduct(products, BrowserDistribution::CHROME_BROWSER); + bool value = false; - if (prefs.install_chrome()) { + if (chrome_install) { prefs.GetBool( installer_util::master_preferences::kDoNotRegisterForUpdateLaunch, &value); } else { value = true; // Never register. } + bool write_chrome_launch_string = (!value) && (install_status != installer_util::IN_USE_UPDATED); - InstallUtil::WriteInstallerResult(system_level, install_status, + WriteInstallerResult(products, install_status, install_msg_base, write_chrome_launch_string ? &chrome_exe : NULL); if (install_status == installer_util::FIRST_INSTALL_SUCCESS) { VLOG(1) << "First install successful."; - if (prefs.install_chrome()) { + if (chrome_install) { // We never want to launch Chrome in system level install mode. bool do_not_launch_chrome = false; prefs.GetBool( installer_util::master_preferences::kDoNotLaunchChrome, &do_not_launch_chrome); - if (!system_level && !do_not_launch_chrome) - installer::LaunchChrome(system_level); + if (!chrome_install->system_level() && !do_not_launch_chrome) + chrome_install->LaunchChrome(); } } else if ((install_status == installer_util::NEW_VERSION_UPDATED) || (install_status == installer_util::IN_USE_UPDATED)) { - installer_setup::RemoveLegacyRegistryKeys(); + for (size_t i = 0; i < products.size(); ++i) { + installer::RemoveLegacyRegistryKeys( + products[i]->distribution()); + } } } } @@ -364,8 +403,11 @@ installer_util::InstallStatus InstallChrome(const CommandLine& cmd_line, // // There is another way to reach this same function if this is a system // level install. See HandleNonInstallCmdLineOptions(). - dist->LaunchUserExperiment(install_status, *installer_version, - system_level); + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + product->distribution()->LaunchUserExperiment(install_status, + *installer_version, *product, product->system_level()); + } } // Delete temporary files. These include install temporary directory @@ -395,31 +437,36 @@ installer_util::InstallStatus InstallChrome(const CommandLine& cmd_line, } } - dist->UpdateDiffInstallStatus(system_level, incremental_install, - install_status); + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + product->distribution()->UpdateDiffInstallStatus(system_level, + incremental_install, install_status); + } + return install_status; } installer_util::InstallStatus UninstallChrome(const CommandLine& cmd_line, - const installer::Version* version, - bool system_install) { + const Product& product) { VLOG(1) << "Uninstalling Chome"; + + scoped_ptr<Version> installed_version(product.GetInstalledVersion()); + if (installed_version.get()) + VLOG(1) << "version on the system: " << installed_version->GetString(); + bool force = cmd_line.HasSwitch(installer_util::switches::kForceUninstall); - if (!version && !force) { + if (!installed_version.get() && !force) { LOG(ERROR) << "No Chrome installation found for uninstall."; - InstallUtil::WriteInstallerResult(system_install, - installer_util::CHROME_NOT_INSTALLED, - IDS_UNINSTALL_FAILED_BASE, NULL); + product.WriteInstallerResult(installer_util::CHROME_NOT_INSTALLED, + IDS_UNINSTALL_FAILED_BASE, NULL); return installer_util::CHROME_NOT_INSTALLED; } bool remove_all = !cmd_line.HasSwitch( installer_util::switches::kDoNotRemoveSharedItems); - return installer_setup::UninstallChrome(cmd_line.GetProgram().value(), - system_install, - remove_all, force, - cmd_line); + return installer::UninstallChrome(cmd_line.GetProgram(), product, remove_all, + force, cmd_line); } installer_util::InstallStatus ShowEULADialog(const std::wstring& inner_frame) { @@ -454,9 +501,9 @@ installer_util::InstallStatus ShowEULADialog(const std::wstring& inner_frame) { // among others). This function returns true if any such command line option // has been found and processed (so setup.exe should exit at that point). bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, - bool system_install, - int& exit_code) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + int& exit_code, + const ProductPackageMapping& installs) { + DCHECK(installs.products().size()); if (cmd_line.HasSwitch(installer_util::switches::kUpdateSetupExe)) { installer_util::InstallStatus status = installer_util::SETUP_PATCH_FAILED; // If --update-setup-exe command line option is given, we apply the given @@ -483,11 +530,11 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, } } - exit_code = dist->GetInstallReturnCode(status); + exit_code = BrowserDistribution::GetInstallReturnCode(status); if (exit_code) { LOG(WARNING) << "setup.exe patching failed."; - InstallUtil::WriteInstallerResult(system_install, status, - IDS_SETUP_PATCH_FAILED_BASE, NULL); + WriteInstallerResult(installs.products(), status, + IDS_SETUP_PATCH_FAILED_BASE, NULL); } file_util::Delete(temp_path, true); return true; @@ -502,25 +549,37 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, return true; } else if (cmd_line.HasSwitch( installer_util::switches::kRegisterChromeBrowser)) { - // If --register-chrome-browser option is specified, register all - // Chrome protocol/file associations as well as register it as a valid - // browser for Start Menu->Internet shortcut. This option should only - // be used when setup.exe is launched with admin rights. We do not - // make any user specific changes in this option. - std::wstring chrome_exe(cmd_line.GetSwitchValueNative( - installer_util::switches::kRegisterChromeBrowser)); - std::wstring suffix; - if (cmd_line.HasSwitch( - installer_util::switches::kRegisterChromeBrowserSuffix)) { - suffix = cmd_line.GetSwitchValueNative( - installer_util::switches::kRegisterChromeBrowserSuffix); + const Product* chrome_install = + FindProduct(installs.products(), BrowserDistribution::CHROME_BROWSER); + DCHECK(chrome_install); + if (chrome_install) { + // If --register-chrome-browser option is specified, register all + // Chrome protocol/file associations as well as register it as a valid + // browser for Start Menu->Internet shortcut. This option should only + // be used when setup.exe is launched with admin rights. We do not + // make any user specific changes in this option. + std::wstring chrome_exe(cmd_line.GetSwitchValueNative( + installer_util::switches::kRegisterChromeBrowser)); + std::wstring suffix; + if (cmd_line.HasSwitch( + installer_util::switches::kRegisterChromeBrowserSuffix)) { + suffix = cmd_line.GetSwitchValueNative( + installer_util::switches::kRegisterChromeBrowserSuffix); + } + exit_code = ShellUtil::RegisterChromeBrowser( + chrome_install->distribution(), chrome_exe, suffix, false); + } else { + LOG(ERROR) << "Can't register browser - Chrome distribution not found"; + exit_code = installer_util::UNKNOWN_STATUS; } - exit_code = ShellUtil::RegisterChromeBrowser(chrome_exe, suffix, false); return true; } else if (cmd_line.HasSwitch(installer_util::switches::kRenameChromeExe)) { // If --rename-chrome-exe is specified, we want to rename the executables // and exit. - exit_code = RenameChromeExecutables(system_install); + const Packages& packages = installs.packages(); + DCHECK_EQ(1U, packages.size()); + for (size_t i = 0; i < packages.size(); ++i) + exit_code = RenameChromeExecutables(*packages[i].get()); return true; } else if (cmd_line.HasSwitch( installer_util::switches::kRemoveChromeRegistration)) { @@ -535,26 +594,44 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, installer_util::switches::kRegisterChromeBrowserSuffix); } installer_util::InstallStatus tmp = installer_util::UNKNOWN_STATUS; - installer_setup::DeleteChromeRegistrationKeys(HKEY_LOCAL_MACHINE, - suffix, tmp); + const Product* chrome_install = + FindProduct(installs.products(), BrowserDistribution::CHROME_BROWSER); + DCHECK(chrome_install); + if (chrome_install) { + installer::DeleteChromeRegistrationKeys(chrome_install->distribution(), + HKEY_LOCAL_MACHINE, suffix, tmp); + } exit_code = tmp; return true; } else if (cmd_line.HasSwitch(installer_util::switches::kInactiveUserToast)) { // Launch the inactive user toast experiment. - std::string flavor = cmd_line.GetSwitchValueASCII( - installer_util::switches::kInactiveUserToast); - int flavor_int; - base::StringToInt(flavor, &flavor_int); - dist->InactiveUserToastExperiment(flavor_int, - cmd_line.HasSwitch(installer_util::switches::kSystemLevelToast)); + int flavor = -1; + base::StringToInt(cmd_line.GetSwitchValueNative( + installer_util::switches::kInactiveUserToast), &flavor); + DCHECK_NE(-1, flavor); + if (flavor == -1) { + exit_code = installer_util::UNKNOWN_STATUS; + } else { + const Products& products = installs.products(); + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + BrowserDistribution* browser_dist = product->distribution(); + browser_dist->InactiveUserToastExperiment(flavor, *product); + } + } return true; } else if (cmd_line.HasSwitch(installer_util::switches::kSystemLevelToast)) { - // We started as system-level and have been re-launched as user level - // to continue with the toast experiment. - scoped_ptr<installer::Version> - installed_version(InstallUtil::GetChromeVersion(system_install)); - dist->LaunchUserExperiment(installer_util::REENTRY_SYS_UPDATE, - *installed_version, true); + const Products& products = installs.products(); + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + BrowserDistribution* browser_dist = product->distribution(); + // We started as system-level and have been re-launched as user level + // to continue with the toast experiment. + scoped_ptr<Version> installed_version( + InstallUtil::GetChromeVersion(browser_dist, installs.system_level())); + browser_dist->LaunchUserExperiment(installer_util::REENTRY_SYS_UPDATE, + *installed_version, *product, true); + } return true; } return false; @@ -604,9 +681,6 @@ class AutoCom { bool Init(bool system_install) { if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) != S_OK) { LOG(ERROR) << "COM initialization failed."; - InstallUtil::WriteInstallerResult(system_install, - installer_util::OS_ERROR, - IDS_INSTALL_OS_ERROR_BASE, NULL); return false; } initialized_ = true; @@ -617,6 +691,25 @@ class AutoCom { bool initialized_; }; +void PopulateInstallations(const MasterPreferences& prefs, + ProductPackageMapping* installations) { + DCHECK(installations); + if (prefs.install_chrome()) { + VLOG(1) << "Install distribution: Chrome"; + installations->AddDistribution(BrowserDistribution::CHROME_BROWSER); + } + + if (prefs.install_chrome_frame()) { + VLOG(1) << "Install distribution: Chrome Frame"; + installations->AddDistribution(BrowserDistribution::CHROME_FRAME); + } + + if (prefs.install_ceee()) { + VLOG(1) << "Install distribution: CEEE"; + installations->AddDistribution(BrowserDistribution::CHROME_FRAME); + } +} + } // namespace int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, @@ -625,63 +718,62 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, base::AtExitManager exit_manager; CommandLine::Init(0, NULL); - const installer_util::MasterPreferences& prefs = + const MasterPreferences& prefs = InstallUtil::GetMasterPreferencesForCurrentProcess(); installer::InitInstallerLogging(prefs); - const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); - VLOG(1) << "Command Line: " << parsed_command_line.command_line_string(); + const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); + VLOG(1) << "Command Line: " << cmd_line.command_line_string(); bool system_install = false; prefs.GetBool(installer_util::master_preferences::kSystemLevel, &system_install); VLOG(1) << "system install is " << system_install; + ProductPackageMapping installations(system_install); + PopulateInstallations(prefs, &installations); + // Check to make sure current system is WinXP or later. If not, log // error message and get out. if (!InstallUtil::IsOSSupported()) { LOG(ERROR) << "Chrome only supports Windows XP or later."; - InstallUtil::WriteInstallerResult(system_install, - installer_util::OS_NOT_SUPPORTED, - IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL); + WriteInstallerResult(installations.products(), + installer_util::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, + NULL); return installer_util::OS_NOT_SUPPORTED; } // Initialize COM for use later. AutoCom auto_com; if (!auto_com.Init(system_install)) { + WriteInstallerResult(installations.products(), + installer_util::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL); return installer_util::OS_ERROR; } // Some command line options don't work with SxS install/uninstall if (InstallUtil::IsChromeSxSProcess()) { if (system_install || - parsed_command_line.HasSwitch( - installer_util::switches::kForceUninstall) || - parsed_command_line.HasSwitch( - installer_util::switches::kMakeChromeDefault) || - parsed_command_line.HasSwitch( - installer_util::switches::kRegisterChromeBrowser) || - parsed_command_line.HasSwitch( + cmd_line.HasSwitch(installer_util::switches::kForceUninstall) || + cmd_line.HasSwitch(installer_util::switches::kMakeChromeDefault) || + cmd_line.HasSwitch(installer_util::switches::kRegisterChromeBrowser) || + cmd_line.HasSwitch( installer_util::switches::kRemoveChromeRegistration) || - parsed_command_line.HasSwitch( - installer_util::switches::kInactiveUserToast) || - parsed_command_line.HasSwitch( - installer_util::switches::kSystemLevelToast)) { + cmd_line.HasSwitch(installer_util::switches::kInactiveUserToast) || + cmd_line.HasSwitch(installer_util::switches::kSystemLevelToast)) { return installer_util::SXS_OPTION_NOT_SUPPORTED; } } int exit_code = 0; - if (HandleNonInstallCmdLineOptions(parsed_command_line, system_install, - exit_code)) + if (HandleNonInstallCmdLineOptions(cmd_line, exit_code, installations)) return exit_code; if (system_install && !IsUserAnAdmin()) { if (base::win::GetVersion() >= base::win::VERSION_VISTA && - !parsed_command_line.HasSwitch(installer_util::switches::kRunAsAdmin)) { + !cmd_line.HasSwitch(installer_util::switches::kRunAsAdmin)) { CommandLine new_cmd(CommandLine::NO_PROGRAM); - new_cmd.AppendArguments(parsed_command_line, true); + new_cmd.AppendArguments(cmd_line, true); // Append --run-as-admin flag to let the new instance of setup.exe know // that we already tried to launch ourselves as admin. new_cmd.AppendSwitch(installer_util::switches::kRunAsAdmin); @@ -690,45 +782,46 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, return exit_code; } else { LOG(ERROR) << "Non admin user can not install system level Chrome."; - InstallUtil::WriteInstallerResult(system_install, - installer_util::INSUFFICIENT_RIGHTS, - IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, - NULL); + WriteInstallerResult(installations.products(), + installer_util::INSUFFICIENT_RIGHTS, + IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL); return installer_util::INSUFFICIENT_RIGHTS; } } - // Check the existing version installed. - scoped_ptr<installer::Version> - installed_version(InstallUtil::GetChromeVersion(system_install)); - if (installed_version.get()) - VLOG(1) << "version on the system: " << installed_version->GetString(); + bool is_uninstall = cmd_line.HasSwitch(installer_util::switches::kUninstall); installer_util::InstallStatus install_status = installer_util::UNKNOWN_STATUS; // If --uninstall option is given, uninstall chrome - if (parsed_command_line.HasSwitch(installer_util::switches::kUninstall)) { - install_status = UninstallChrome(parsed_command_line, - installed_version.get(), - system_install); - // If --uninstall option is not specified, we assume it is install case. + if (is_uninstall) { + DCHECK_EQ(1U, installations.products().size()) << + "We currently really only support uninstalling one distribution " + "at a time"; + for (size_t i = 0; i < installations.products().size(); ++i) { + install_status = UninstallChrome(cmd_line, + *installations.products()[i]); + } } else { - install_status = InstallChrome(parsed_command_line, installed_version.get(), - prefs); + // If --uninstall option is not specified, we assume it is install case. + const Packages& packages = installations.packages(); + VLOG(1) << "Installing to " << packages.size() << " target paths"; + for (size_t i = 0; i < packages.size(); ++i) { + install_status = InstallChrome(cmd_line, *packages[i].get(), prefs); + } } - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + const Product* cf_install = + FindProduct(installations.products(), BrowserDistribution::CHROME_FRAME); - if (InstallUtil::IsChromeFrameProcess() && - !parsed_command_line.HasSwitch( - installer_util::switches::kForceUninstall)) { + if (cf_install && + !cmd_line.HasSwitch(installer_util::switches::kForceUninstall)) { if (install_status == installer_util::UNINSTALL_REQUIRES_REBOOT) { ShowRebootDialog(); - } else if (parsed_command_line.HasSwitch( - installer_util::switches::kUninstall)) { + } else if (is_uninstall) { ::MessageBoxW(NULL, installer_util::GetLocalizedString( IDS_UNINSTALL_COMPLETE_BASE).c_str(), - dist->GetApplicationName().c_str(), + cf_install->distribution()->GetApplicationName().c_str(), MB_OK); } } @@ -737,14 +830,19 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will // rollback the action. If we're uninstalling we want to avoid this, so always // report success, squashing any more informative return codes. - if (!(InstallUtil::IsMSIProcess(system_install) && - parsed_command_line.HasSwitch(installer_util::switches::kUninstall))) { - // Note that we allow the status installer_util::UNINSTALL_REQUIRES_REBOOT - // to pass through, since this is only returned on uninstall which is never - // invoked directly by Google Update. - return_code = dist->GetInstallReturnCode(install_status); + // TODO(tommi): Fix this loop when IsMsi has been moved out of the Product + // class. + for (size_t i = 0; i < installations.products().size(); ++i) { + const Product* product = installations.products()[i]; + if (!(product->IsMsi() && is_uninstall)) { + // Note that we allow the status installer_util::UNINSTALL_REQUIRES_REBOOT + // to pass through, since this is only returned on uninstall which is + // never invoked directly by Google Update. + return_code = BrowserDistribution::GetInstallReturnCode(install_status); + } } VLOG(1) << "Installation complete, returning: " << return_code; + return return_code; } diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc index d4fbed1..08ea157 100644 --- a/chrome/installer/setup/setup_util.cc +++ b/chrome/installer/setup/setup_util.cc @@ -33,20 +33,26 @@ int setup_util::ApplyDiffPatch(const FilePath& src, dest.value().c_str()); } -installer::Version* setup_util::GetVersionFromDir( +installer::Version* setup_util::GetVersionFromArchiveDir( const FilePath& chrome_path) { VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value(); FilePath root_path = chrome_path.Append(L"*"); - WIN32_FIND_DATA find_data; + // TODO(tommi): The version directory really should match the version of + // setup.exe. To begin with, we should at least DCHECK that that's true. + + // TODO(tommi): use file_util::FileEnumerator. + WIN32_FIND_DATA find_data = {0}; HANDLE file_handle = FindFirstFile(root_path.value().c_str(), &find_data); BOOL ret = TRUE; - installer::Version *version = NULL; + installer::Version* version = NULL; // Here we are assuming that the installer we have is really valid so there // can not be two version directories. We exit as soon as we find a valid // version directory. while (ret) { - if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + lstrcmpW(find_data.cFileName, L"..") != 0 && + lstrcmpW(find_data.cFileName, L".") != 0) { VLOG(1) << "directory found: " << find_data.cFileName; version = installer::Version::GetVersionFromString(find_data.cFileName); if (version) diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h index 61aed71..0858070 100644 --- a/chrome/installer/setup/setup_util.h +++ b/chrome/installer/setup/setup_util.h @@ -23,7 +23,7 @@ namespace setup_util { // Find the version of Chrome from an install source directory. // Chrome_path should contain a version folder. // Returns the first version found or NULL if no version is found. - installer::Version* GetVersionFromDir(const FilePath& chrome_path); + installer::Version* GetVersionFromArchiveDir(const FilePath& chrome_path); } // namespace setup_util #endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_ diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc index 0e74de9..8161173 100644 --- a/chrome/installer/setup/setup_util_unittest.cc +++ b/chrome/installer/setup/setup_util_unittest.cc @@ -57,27 +57,27 @@ TEST_F(SetupUtilTest, ApplyDiffPatchTest) { } // Test that we are parsing Chrome version correctly. -TEST_F(SetupUtilTest, GetVersionFromDirTest) { +TEST_F(SetupUtilTest, GetVersionFromArchiveDirTest) { // Create a version dir FilePath chrome_dir = test_dir_.path().AppendASCII("1.0.0.0"); file_util::CreateDirectory(chrome_dir); ASSERT_TRUE(file_util::PathExists(chrome_dir)); scoped_ptr<installer::Version> version( - setup_util::GetVersionFromDir(test_dir_.path())); + setup_util::GetVersionFromArchiveDir(test_dir_.path())); ASSERT_TRUE(version->GetString() == L"1.0.0.0"); file_util::Delete(chrome_dir, true); ASSERT_FALSE(file_util::PathExists(chrome_dir)); - ASSERT_TRUE(setup_util::GetVersionFromDir(test_dir_.path()) == NULL); + ASSERT_TRUE(setup_util::GetVersionFromArchiveDir(test_dir_.path()) == NULL); chrome_dir = test_dir_.path().AppendASCII("ABC"); file_util::CreateDirectory(chrome_dir); ASSERT_TRUE(file_util::PathExists(chrome_dir)); - ASSERT_TRUE(setup_util::GetVersionFromDir(test_dir_.path()) == NULL); + ASSERT_TRUE(setup_util::GetVersionFromArchiveDir(test_dir_.path()) == NULL); chrome_dir = test_dir_.path().AppendASCII("2.3.4.5"); file_util::CreateDirectory(chrome_dir); ASSERT_TRUE(file_util::PathExists(chrome_dir)); - version.reset(setup_util::GetVersionFromDir(test_dir_.path())); + version.reset(setup_util::GetVersionFromArchiveDir(test_dir_.path())); ASSERT_TRUE(version->GetString() == L"2.3.4.5"); } diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 4b3ddee..1dee62d 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -32,8 +32,9 @@ #include "registered_dlls.h" // NOLINT using base::win::RegKey; +using installer_util::InstallStatus; -namespace { +namespace installer { // This functions checks for any Chrome instances that are // running and first asks them to close politely by sending a Windows message. @@ -90,7 +91,7 @@ void CloseChromeFrameHelperProcess() { DWORD wait = ::WaitForSingleObject(process, kWaitMs); if (wait != WAIT_OBJECT_0) { LOG(WARNING) << "Wait for " << installer_util::kChromeFrameHelperExe - << " to exit failed or timed out."; + << " to exit failed or timed out."; } else { kill = false; VLOG(1) << installer_util::kChromeFrameHelperExe << " exited normally."; @@ -108,19 +109,18 @@ void CloseChromeFrameHelperProcess() { // It returns true iff: // - Software\Clients\StartMenuInternet\Chromium\"" key has a valid value. // - The value is same as chrome.exe path for the current installation. -bool CurrentUserHasDefaultBrowser(bool system_uninstall) { +bool CurrentUserHasDefaultBrowser(const Product& product) { std::wstring reg_key(ShellUtil::kRegStartMenuInternet); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - reg_key.append(L"\\" + dist->GetApplicationName() + ShellUtil::kRegShellOpen); + reg_key.append(L"\\" + product.distribution()->GetApplicationName() + + ShellUtil::kRegShellOpen); RegKey key(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_READ); std::wstring reg_exe; if (key.ReadValue(L"", ®_exe) && reg_exe.length() > 2) { - std::wstring chrome_exe = installer::GetChromeInstallPath(system_uninstall); - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); + FilePath chrome_exe(product.package().path() + .Append(installer_util::kChromeExe)); + // The path in the registry will always have quotes. reg_exe = reg_exe.substr(1, reg_exe.length() - 2); - if ((reg_exe.size() == chrome_exe.size()) && - (std::equal(chrome_exe.begin(), chrome_exe.end(), - reg_exe.begin(), base::CaseInsensitiveCompare<wchar_t>()))) + if (FilePath::CompareEqualIgnoreCase(reg_exe, chrome_exe.value())) return true; } @@ -133,29 +133,40 @@ bool CurrentUserHasDefaultBrowser(bool system_uninstall) { // We try to remove the standard desktop shortcut but if that fails we try // to remove the alternate desktop shortcut. Only one of them should be // present in a given install but at this point we don't know which one. -void DeleteChromeShortcuts(bool system_uninstall) { +void DeleteChromeShortcuts(const Product& product) { + if (product.distribution()->GetType() != + BrowserDistribution::CHROME_BROWSER) { + VLOG(1) << __FUNCTION__ " called for non-CHROME distribution"; + return; + } + FilePath shortcut_path; - if (system_uninstall) { + if (product.system_level()) { PathService::Get(base::DIR_COMMON_START_MENU, &shortcut_path); - if (!ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER | - ShellUtil::SYSTEM_LEVEL, false)) - ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER | - ShellUtil::SYSTEM_LEVEL, true); + if (!ShellUtil::RemoveChromeDesktopShortcut(product.distribution(), + ShellUtil::CURRENT_USER | ShellUtil::SYSTEM_LEVEL, false)) { + ShellUtil::RemoveChromeDesktopShortcut(product.distribution(), + ShellUtil::CURRENT_USER | ShellUtil::SYSTEM_LEVEL, true); + } - ShellUtil::RemoveChromeQuickLaunchShortcut(ShellUtil::CURRENT_USER | - ShellUtil::SYSTEM_LEVEL); + ShellUtil::RemoveChromeQuickLaunchShortcut(product.distribution(), + ShellUtil::CURRENT_USER | ShellUtil::SYSTEM_LEVEL); } else { PathService::Get(base::DIR_START_MENU, &shortcut_path); - if (!ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER, false)) - ShellUtil::RemoveChromeDesktopShortcut(ShellUtil::CURRENT_USER, true); + if (!ShellUtil::RemoveChromeDesktopShortcut(product.distribution(), + ShellUtil::CURRENT_USER, false)) { + ShellUtil::RemoveChromeDesktopShortcut(product.distribution(), + ShellUtil::CURRENT_USER, true); + } - ShellUtil::RemoveChromeQuickLaunchShortcut(ShellUtil::CURRENT_USER); + ShellUtil::RemoveChromeQuickLaunchShortcut(product.distribution(), + ShellUtil::CURRENT_USER); } if (shortcut_path.empty()) { LOG(ERROR) << "Failed to get location for shortcut."; } else { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - shortcut_path = shortcut_path.Append(dist->GetAppShortCutName()); + shortcut_path = shortcut_path.Append( + product.distribution()->GetAppShortCutName()); VLOG(1) << "Deleting shortcut " << shortcut_path.value(); if (!file_util::Delete(shortcut_path, true)) LOG(ERROR) << "Failed to delete folder: " << shortcut_path.value(); @@ -206,124 +217,134 @@ enum DeleteResult { DELETE_REQUIRES_REBOOT }; -// Deletes all installed files of Chromium and Folders or schedules them for -// deletion on reboot if they are in use. Before deleting it -// needs to move setup.exe in a temp folder because the current process -// is using that file. -// Returns DELETE_SUCCEEDED if all files were successfully delete. -// Returns DELETE_FAILED if it could not get the path to the install dir. -// Returns DELETE_REQUIRES_REBOOT if the files were in use and so were -// scheduled for deletion on next reboot. -DeleteResult DeleteFilesAndFolders(const std::wstring& exe_path, - bool system_uninstall, const installer::Version& installed_version, - std::wstring* local_state_path, bool delete_profile) { - std::wstring install_path(installer::GetChromeInstallPath(system_uninstall)); - if (install_path.empty()) { - LOG(ERROR) << "Could not get installation destination path."; - return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. +FilePath GetLocalStateFolder(const Product& product) { + // chrome_frame will be true for CHROME_FRAME and CEEE. + bool chrome_frame = (product.distribution()->GetType() != + BrowserDistribution::CHROME_BROWSER); + // Obtain the location of the user profile data. Chrome Frame needs to + // build this path manually since it doesn't use the Chrome default dir. + FilePath local_state_folder; + if (chrome_frame) { + chrome::GetChromeFrameUserDataDirectory(&local_state_folder); + } else { + chrome::GetDefaultUserDataDirectory(&local_state_folder); } - VLOG(1) << "install destination path: " << install_path; - // Move setup.exe to the temp path. - std::wstring setup_exe(installer::GetInstallerPathUnderChrome( - install_path, installed_version.GetString())); - file_util::AppendToPath(&setup_exe, file_util::GetFilenameFromPath(exe_path)); + LOG_IF(ERROR, local_state_folder.empty()) + << "Could not retrieve user's profile directory."; - FilePath temp_file; - if (!file_util::CreateTemporaryFile(&temp_file)) { - LOG(ERROR) << "Failed to create temporary file for setup.exe."; - } else { - FilePath setup_exe_path = FilePath::FromWStringHack(setup_exe); - file_util::Move(setup_exe_path, temp_file); - } + return local_state_folder; +} - // Obtain the location of the user profile data. Chrome Frame needs to - // build this path manually since it doesn't use the Chrome default dir. - FilePath user_local_state; - bool got_local_state = false; - if (InstallUtil::IsChromeFrameProcess()) { - got_local_state = - chrome::GetChromeFrameUserDataDirectory(&user_local_state); +// Creates a copy of the local state file and returns a path to the copy. +FilePath BackupLocalStateFile(const FilePath& local_state_folder) { + FilePath backup; + FilePath state_file(local_state_folder.Append(chrome::kLocalStateFilename)); + if (!file_util::CreateTemporaryFile(&backup)) { + LOG(ERROR) << "Failed to create temporary file for Local State."; } else { - got_local_state = chrome::GetDefaultUserDataDirectory(&user_local_state); + file_util::CopyFile(state_file, backup); } + return backup; +} - // Move the browser's persisted local state - if (got_local_state) { - FilePath user_local_file( - user_local_state.Append(chrome::kLocalStateFilename)); - FilePath path; - if (!file_util::CreateTemporaryFile(&path)) { - LOG(ERROR) << "Failed to create temporary file for Local State."; +// Copies the local state to the temp folder and then deletes it. +// The path to the copy is returned via the local_state_copy parameter. +DeleteResult DeleteLocalState(const Product& product) { + FilePath user_local_state(GetLocalStateFolder(product)); + if (user_local_state.empty()) + return DELETE_SUCCEEDED; + + DeleteResult result = DELETE_SUCCEEDED; + VLOG(1) << "Deleting user profile" << user_local_state.value(); + if (!file_util::Delete(user_local_state, true)) { + LOG(ERROR) << "Failed to delete user profile dir: " + << user_local_state.value(); + if (product.distribution()->GetType() == + BrowserDistribution::CHROME_FRAME) { + ScheduleDirectoryForDeletion(user_local_state.value().c_str()); + result = DELETE_REQUIRES_REBOOT; } else { - *local_state_path = path.value(); - file_util::CopyFile(user_local_file, path); + result = DELETE_FAILED; } + } + + if (result == DELETE_REQUIRES_REBOOT) { + ScheduleParentAndGrandparentForDeletion(user_local_state); } else { - LOG(ERROR) << "Could not retrieve user's profile directory."; + DeleteEmptyParentDir(user_local_state); + } + + return result; +} + +bool MoveSetupOutOfInstallFolder(const Package& package, + const FilePath& setup_path, + const Version& installed_version) { + bool ret = false; + FilePath setup_exe(package.GetInstallerDirectory(installed_version) + .Append(setup_path.BaseName())); + FilePath temp_file; + if (!file_util::CreateTemporaryFile(&temp_file)) { + LOG(ERROR) << "Failed to create temporary file for setup.exe."; + } else { + ret = file_util::Move(setup_exe, temp_file); + } + return ret; +} + +DeleteResult DeleteFilesAndFolders(const Package& package, + const installer::Version& installed_version) { + VLOG(1) << "DeleteFilesAndFolders: " << package.path().value(); + if (package.path().empty()) { + LOG(ERROR) << "Could not get installation destination path."; + return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. } DeleteResult result = DELETE_SUCCEEDED; - VLOG(1) << "Deleting install path " << install_path; - if (!file_util::Delete(install_path, true)) { - LOG(ERROR) << "Failed to delete folder (1st try): " << install_path; - if (InstallUtil::IsChromeFrameProcess()) { + VLOG(1) << "Deleting install path " << package.path().value(); + if (!file_util::Delete(package.path(), true)) { + LOG(ERROR) << "Failed to delete folder (1st try): " + << package.path().value(); + if (FindProduct(package.products(), + BrowserDistribution::CHROME_FRAME)) { // We don't try killing Chrome processes for Chrome Frame builds since // that is unlikely to help. Instead, schedule files for deletion and // return a value that will trigger a reboot prompt. - ScheduleDirectoryForDeletion(install_path.c_str()); + ScheduleDirectoryForDeletion(package.path().value().c_str()); result = DELETE_REQUIRES_REBOOT; } else { // Try closing any running chrome processes and deleting files once again. CloseAllChromeProcesses(); - if (!file_util::Delete(install_path, true)) { - LOG(ERROR) << "Failed to delete folder (2nd try): " << install_path; - result = DELETE_FAILED; - } - } - } - - if (delete_profile && got_local_state) { - VLOG(1) << "Deleting user profile" << user_local_state.value(); - if (!file_util::Delete(user_local_state, true)) { - LOG(ERROR) << "Failed to delete user profile dir: " - << user_local_state.value(); - if (InstallUtil::IsChromeFrameProcess()) { - ScheduleDirectoryForDeletion(user_local_state.value().c_str()); - result = DELETE_REQUIRES_REBOOT; - } else { + if (!file_util::Delete(package.path(), true)) { + LOG(ERROR) << "Failed to delete folder (2nd try): " + << package.path().value(); result = DELETE_FAILED; } } - if (result == DELETE_REQUIRES_REBOOT) { - ScheduleParentAndGrandparentForDeletion(user_local_state); - } else { - DeleteEmptyParentDir(user_local_state); - } } if (result == DELETE_REQUIRES_REBOOT) { // If we need a reboot to continue, schedule the parent directories for // deletion unconditionally. If they are not empty, the session manager // will not delete them on reboot. - ScheduleParentAndGrandparentForDeletion(FilePath(install_path)); + ScheduleParentAndGrandparentForDeletion(package.path()); } else { // Now check and delete if the parent directories are empty // For example Google\Chrome or Chromium - DeleteEmptyParentDir(FilePath(install_path)); + DeleteEmptyParentDir(package.path()); } return result; } - // This method checks if Chrome is currently running or if the user has // cancelled the uninstall operation by clicking Cancel on the confirmation // box that Chrome pops up. -installer_util::InstallStatus IsChromeActiveOrUserCancelled( - bool system_uninstall) { - static const std::wstring kCmdLineOptions(L" --uninstall"); +InstallStatus IsChromeActiveOrUserCancelled(const Product& product) { int32 exit_code = ResultCodes::NORMAL_EXIT; + CommandLine options(CommandLine::NO_PROGRAM); + options.AppendSwitch(installer_util::switches::kUninstall); // Here we want to save user from frustration (in case of Chrome crashes) // and continue with the uninstallation as long as chrome.exe process exit @@ -334,35 +355,33 @@ installer_util::InstallStatus IsChromeActiveOrUserCancelled( // give this method some brains and not kill chrome.exe launched // by us, we will not uninstall if we get this return code). VLOG(1) << "Launching Chrome to do uninstall tasks."; - if (installer::LaunchChromeAndWaitForResult(system_uninstall, - kCmdLineOptions, - &exit_code)) { + if (product.LaunchChromeAndWait(options, &exit_code)) { VLOG(1) << "chrome.exe launched for uninstall confirmation returned: " << exit_code; if ((exit_code == ResultCodes::UNINSTALL_CHROME_ALIVE) || (exit_code == ResultCodes::UNINSTALL_USER_CANCEL) || (exit_code == ResultCodes::HUNG)) return installer_util::UNINSTALL_CANCELLED; + if (exit_code == ResultCodes::UNINSTALL_DELETE_PROFILE) return installer_util::UNINSTALL_DELETE_PROFILE; } else { - LOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation."; + PLOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation."; } return installer_util::UNINSTALL_CONFIRMED; } -bool ShouldDeleteProfile(const CommandLine& cmd_line, - installer_util::InstallStatus status, - bool system_uninstall) { +bool ShouldDeleteProfile(const CommandLine& cmd_line, InstallStatus status, + const Product& product) { bool should_delete = false; // Chrome Frame uninstallations always want to delete the profile (we have no // UI to prompt otherwise and the profile stores no useful data anyway) // unless they are managed by MSI. MSI uninstalls will explicitly include // the --delete-profile flag to distinguish them from MSI upgrades. - if (InstallUtil::IsChromeFrameProcess() && - !InstallUtil::IsMSIProcess(system_uninstall)) { + if (product.distribution()->GetType() != + BrowserDistribution::CHROME_BROWSER && !product.IsMsi()) { should_delete = true; } else { should_delete = @@ -373,18 +392,18 @@ bool ShouldDeleteProfile(const CommandLine& cmd_line, return should_delete; } -} // namespace - - -bool installer_setup::DeleteChromeRegistrationKeys(HKEY root, - const std::wstring& browser_entry_suffix, - installer_util::InstallStatus& exit_code) { - if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) { +bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, + const std::wstring& browser_entry_suffix, + InstallStatus& exit_code) { + if (!dist->CanSetAsDefault()) { // We should have never set those keys. return true; } RegKey key(root, L"", KEY_ALL_ACCESS); + if (!key.Valid()) { + PLOG(ERROR) << "DeleteChromeRegistrationKeys: failed to open root key"; + } // Delete Software\Classes\ChromeHTML, std::wstring html_prog_id(ShellUtil::kRegClasses); @@ -393,7 +412,6 @@ bool installer_setup::DeleteChromeRegistrationKeys(HKEY root, InstallUtil::DeleteRegistryKey(key, html_prog_id); // Delete Software\Clients\StartMenuInternet\Chromium - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); std::wstring set_access_key(ShellUtil::kRegStartMenuInternet); file_util::AppendToPath(&set_access_key, dist->GetApplicationName()); set_access_key.append(browser_entry_suffix); @@ -435,7 +453,7 @@ bool installer_setup::DeleteChromeRegistrationKeys(HKEY root, return true; } -void installer_setup::RemoveLegacyRegistryKeys() { +void RemoveLegacyRegistryKeys(BrowserDistribution* dist) { // We used to register Chrome to handle crx files, but this turned out // to be not worth the hassle. Remove these old registry entries if // they exist. See: http://codereview.chromium.org/210007 @@ -452,7 +470,7 @@ const wchar_t kChromeExtProgId[] = L"ChromiumExt"; std::wstring suffix; if (roots[i] == HKEY_LOCAL_MACHINE && - !ShellUtil::GetUserSpecificDefaultBrowserSuffix(&suffix)) + !ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) suffix = L""; // Delete Software\Classes\ChromeExt, @@ -469,23 +487,32 @@ const wchar_t kChromeExtProgId[] = L"ChromiumExt"; } } -installer_util::InstallStatus installer_setup::UninstallChrome( - const std::wstring& exe_path, bool system_uninstall, - bool remove_all, bool force_uninstall, - const CommandLine& cmd_line) { - installer_util::InstallStatus status = installer_util::UNINSTALL_CONFIRMED; +InstallStatus UninstallChrome(const FilePath& setup_path, + const Product& product, + bool remove_all, + bool force_uninstall, + const CommandLine& cmd_line) { + InstallStatus status = installer_util::UNINSTALL_CONFIRMED; std::wstring suffix; - if (!ShellUtil::GetUserSpecificDefaultBrowserSuffix(&suffix)) + if (!ShellUtil::GetUserSpecificDefaultBrowserSuffix(product.distribution(), + &suffix)) suffix = L""; + BrowserDistribution* browser_dist = product.distribution(); + bool is_chrome = (browser_dist->GetType() == + BrowserDistribution::CHROME_BROWSER); + + VLOG(1) << "UninstallChrome: " << browser_dist->GetApplicationName(); + if (force_uninstall) { // Since --force-uninstall command line option is used, we are going to // do silent uninstall. Try to close all running Chrome instances. - if (!InstallUtil::IsChromeFrameProcess()) + // NOTE: We don't do this for Chrome Frame or CEEE. + if (is_chrome) CloseAllChromeProcesses(); - } else if (!InstallUtil::IsChromeFrameProcess()) { + } else if (is_chrome) { // no --force-uninstall so lets show some UI dialog boxes. - status = IsChromeActiveOrUserCancelled(system_uninstall); + status = IsChromeActiveOrUserCancelled(product); if (status != installer_util::UNINSTALL_CONFIRMED && status != installer_util::UNINSTALL_DELETE_PROFILE) return status; @@ -494,9 +521,9 @@ installer_util::InstallStatus installer_setup::UninstallChrome( // another uninstaller (silent) in elevated mode to do HKLM cleanup. // And continue uninstalling in the current process also to do HKCU cleanup. if (remove_all && - (!suffix.empty() || CurrentUserHasDefaultBrowser(system_uninstall)) && + (!suffix.empty() || CurrentUserHasDefaultBrowser(product)) && !::IsUserAnAdmin() && - (base::win::GetVersion() >= base::win::VERSION_VISTA) && + base::win::GetVersion() >= base::win::VERSION_VISTA && !cmd_line.HasSwitch(installer_util::switches::kRunAsAdmin)) { CommandLine new_cmd(CommandLine::NO_PROGRAM); new_cmd.AppendArguments(cmd_line, true); @@ -516,49 +543,51 @@ installer_util::InstallStatus installer_setup::UninstallChrome( // Get the version of installed Chrome (if any) scoped_ptr<installer::Version> - installed_version(InstallUtil::GetChromeVersion(system_uninstall)); + installed_version(InstallUtil::GetChromeVersion(browser_dist, + product.system_level())); // Chrome is not in use so lets uninstall Chrome by deleting various files // and registry entries. Here we will just make best effort and keep going // in case of errors. // First delete shortcuts from Start->Programs, Desktop & Quick Launch. - DeleteChromeShortcuts(system_uninstall); + DeleteChromeShortcuts(product); // Delete the registry keys (Uninstall key and Version key). - HKEY reg_root = system_uninstall ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; RegKey key(reg_root, L"", KEY_ALL_ACCESS); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); // Note that we must retrieve the distribution-specific data before deleting - // dist->GetVersionKey(). - std::wstring distribution_data(dist->GetDistributionData(&key)); + // product.GetVersionKey(). + std::wstring distribution_data(browser_dist->GetDistributionData(reg_root)); // Remove Control Panel uninstall link and Omaha product key. - InstallUtil::DeleteRegistryKey(key, dist->GetUninstallRegPath()); - InstallUtil::DeleteRegistryKey(key, dist->GetVersionKey()); + InstallUtil::DeleteRegistryKey(key, browser_dist->GetUninstallRegPath()); + InstallUtil::DeleteRegistryKey(key, browser_dist->GetVersionKey()); // Also try to delete the MSI value in the ClientState key (it might not be // there). This is due to a Google Update behaviour where an uninstall and a // rapid reinstall might result in stale values from the old ClientState key // being picked up on reinstall. - InstallUtil::SetMSIMarker(system_uninstall, false); + product.SetMsiMarker(false); // Remove all Chrome registration keys. - installer_util::InstallStatus ret = installer_util::UNKNOWN_STATUS; - DeleteChromeRegistrationKeys(reg_root, suffix, ret); + InstallStatus ret = installer_util::UNKNOWN_STATUS; + DeleteChromeRegistrationKeys(product.distribution(), reg_root, suffix, ret); // For user level install also we end up creating some keys in HKLM if user // sets Chrome as default browser. So delete those as well (needs admin). - if (remove_all && !system_uninstall && - (!suffix.empty() || CurrentUserHasDefaultBrowser(system_uninstall))) - DeleteChromeRegistrationKeys(HKEY_LOCAL_MACHINE, suffix, ret); + if (remove_all && !product.system_level() && + (!suffix.empty() || CurrentUserHasDefaultBrowser(product))) { + DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE, + suffix, ret); + } // Delete shared registry keys as well (these require admin rights) if // remove_all option is specified. if (remove_all) { - if (!InstallUtil::IsChromeSxSProcess() && - !InstallUtil::IsChromeFrameProcess()) { + if (!InstallUtil::IsChromeSxSProcess() && is_chrome) { // Delete media player registry key that exists only in HKLM. // We don't delete this key in SxS uninstall or Chrome Frame uninstall // as we never set the key for those products. @@ -570,18 +599,14 @@ installer_util::InstallStatus installer_setup::UninstallChrome( } // Unregister any dll servers that we may have registered for Chrome Frame - // builds only. - if (installed_version.get() && InstallUtil::IsChromeFrameProcess()) { - std::wstring dll_path(installer::GetChromeInstallPath(system_uninstall)); - file_util::AppendToPath(&dll_path, installed_version->GetString()); - - scoped_ptr<WorkItemList> dll_list(WorkItem::CreateWorkItemList()); - if (InstallUtil::BuildDLLRegistrationList(dll_path, kDllsToRegister, - kNumDllsToRegister, false, - !system_uninstall, - dll_list.get())) { - dll_list->Do(); - } + // and CEEE builds only. + // TODO(tommi): We should only do this when the folder itself is + // being removed and we know that the DLLs were previously registered. + // Simplest would be to always register them. + if (installed_version.get() && !is_chrome) { + RegisterComDllList(product.package().path().Append( + installed_version->GetString()), + product.system_level(), false, false); } } @@ -596,12 +621,26 @@ installer_util::InstallStatus installer_setup::UninstallChrome( // Finally delete all the files from Chrome folder after moving setup.exe // and the user's Local State to a temp location. - bool delete_profile = ShouldDeleteProfile(cmd_line, status, system_uninstall); - std::wstring local_state_path; + bool delete_profile = ShouldDeleteProfile(cmd_line, status, product); ret = installer_util::UNINSTALL_SUCCESSFUL; - DeleteResult delete_result = DeleteFilesAndFolders(exe_path, - system_uninstall, *installed_version, &local_state_path, delete_profile); + // In order to be able to remove the folder in which we're running, we + // need to move setup.exe out of the install folder. + // TODO(tommi): What if the temp folder is on a different volume? + MoveSetupOutOfInstallFolder(product.package(), setup_path, + *installed_version); + + FilePath backup_state_file(BackupLocalStateFile( + GetLocalStateFolder(product))); + + // TODO(tommi): We should only do this when the last distribution is being + // uninstalled. + DeleteResult delete_result = DeleteFilesAndFolders(product.package(), + *installed_version); + + if (delete_profile) + DeleteLocalState(product); + if (delete_result == DELETE_FAILED) { ret = installer_util::UNINSTALL_FAILED; } else if (delete_result == DELETE_REQUIRES_REBOOT) { @@ -610,14 +649,17 @@ installer_util::InstallStatus installer_setup::UninstallChrome( if (!force_uninstall) { VLOG(1) << "Uninstallation complete. Launching Uninstall survey."; - dist->DoPostUninstallOperations(*installed_version, local_state_path, - distribution_data); + browser_dist->DoPostUninstallOperations(*installed_version, + backup_state_file, distribution_data); } // Try and delete the preserved local state once the post-install // operations are complete. - if (!local_state_path.empty()) - file_util::Delete(local_state_path, false); + if (!backup_state_file.empty()) + file_util::Delete(backup_state_file, false); return ret; } + +} // namespace installer + diff --git a/chrome/installer/setup/uninstall.h b/chrome/installer/setup/uninstall.h index d0dc8c3..9165091 100644 --- a/chrome/installer/setup/uninstall.h +++ b/chrome/installer/setup/uninstall.h @@ -13,39 +13,37 @@ #include <shlobj.h> #include "base/command_line.h" +#include "chrome/installer/util/product.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/version.h" -namespace installer_setup { +namespace installer { // This function removes all Chrome registration related keys. It returns true // if successful, otherwise false. The error code is set in |exit_code|. // |root| is the registry root (HKLM|HKCU) and |browser_entry_suffix| is the // suffix for default browser entry name in the registry (optional). -bool DeleteChromeRegistrationKeys(HKEY root, +bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, const std::wstring& browser_entry_suffix, installer_util::InstallStatus& exit_code); // Removes any legacy registry keys from earlier versions of Chrome that are no // longer needed. This is used during autoupdate since we don't do full // uninstalls/reinstalls to update. -void RemoveLegacyRegistryKeys(); +void RemoveLegacyRegistryKeys(BrowserDistribution* dist); // This function uninstalls Chrome. // -// exe_path: Path to the executable (setup.exe) as it will be copied +// setup_path: Path to the executable (setup.exe) as it will be copied // to temp folder before deleting Chrome folder. -// system_uninstall: if true, the function uninstalls Chrome installed system -// wise. otherwise, it uninstalls Chrome installed for the -// current user. +// dist: Represents the distribution to be uninstalled. // remove_all: Remove all shared files, registry entries as well. // force_uninstall: Uninstall without prompting for user confirmation or // any checks for Chrome running. // cmd_line: CommandLine that contains information about the command that // was used to launch current uninstaller. installer_util::InstallStatus UninstallChrome( - const std::wstring& exe_path, bool system_uninstall, - bool remove_all, bool force_uninstall, - const CommandLine& cmd_line); + const FilePath& setup_path, const Product& dist, bool remove_all, + bool force_uninstall, const CommandLine& cmd_line); } // namespace installer_setup diff --git a/chrome/installer/util/browser_distribution.cc b/chrome/installer/util/browser_distribution.cc index 2e2ccf2..db98298 100644 --- a/chrome/installer/util/browser_distribution.cc +++ b/chrome/installer/util/browser_distribution.cc @@ -9,10 +9,12 @@ #include "chrome/installer/util/browser_distribution.h" +#include "base/atomicops.h" #include "base/command_line.h" #include "base/file_path.h" #include "base/path_service.h" #include "base/lock.h" +#include "base/logging.h" #include "base/win/registry.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/env_vars.h" @@ -25,6 +27,11 @@ #include "installer_util_strings.h" namespace { +// The BrowserDistribution objects are never freed. +BrowserDistribution* g_browser_distribution = NULL; +BrowserDistribution* g_chrome_frame_distribution = NULL; +BrowserDistribution* g_ceee_distribution = NULL; + // Returns true if currently running in npchrome_frame.dll bool IsChromeFrameModule() { FilePath module_path; @@ -41,40 +48,69 @@ bool IsCeeeBrokerProcess() { installer_util::kCeeeBrokerExe); } +BrowserDistribution::DistributionType GetCurrentDistributionType() { + static BrowserDistribution::DistributionType type = + (InstallUtil::IsChromeFrameProcess() || IsChromeFrameModule()) ? + BrowserDistribution::CHROME_FRAME : + (IsCeeeBrokerProcess() ? BrowserDistribution::CEEE : + BrowserDistribution::CHROME_BROWSER); + return type; +} + } // end namespace +template<class DistributionClass> +BrowserDistribution* BrowserDistribution::GetOrCreateBrowserDistribution( + BrowserDistribution** dist) { + if (!*dist) { + DistributionClass* temp = new DistributionClass(); + if (base::subtle::NoBarrier_CompareAndSwap( + reinterpret_cast<base::subtle::AtomicWord*>(dist), NULL, + reinterpret_cast<base::subtle::AtomicWord>(temp)) != NULL) + delete temp; + } + + return *dist; +} + BrowserDistribution* BrowserDistribution::GetDistribution() { - return GetDistribution(InstallUtil::IsChromeFrameProcess() || - IsChromeFrameModule() || - IsCeeeBrokerProcess()); -} - -BrowserDistribution* BrowserDistribution::GetDistribution(bool chrome_frame) { - static BrowserDistribution* dist = NULL; - static Lock dist_lock; - AutoLock lock(dist_lock); - if (dist == NULL) { - if (chrome_frame) { - // TODO(robertshield): Make one of these for Google Chrome vs - // non Google Chrome builds? - dist = new ChromeFrameDistribution(); - } else { + return GetSpecificDistribution(GetCurrentDistributionType()); +} + +// static +BrowserDistribution* BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::DistributionType type) { + BrowserDistribution* dist = NULL; + + // TODO(tommi): initialize g_ceee_distribution when appropriate. Right now + // we treat CEEE as Chrome Frame. + + if (type == CHROME_FRAME || type == CEEE) { + // TODO(robertshield): Make one of these for Google Chrome vs + // non Google Chrome builds? + dist = GetOrCreateBrowserDistribution<ChromeFrameDistribution>( + &g_chrome_frame_distribution); + } else { + DCHECK_EQ(CHROME_BROWSER, type); #if defined(GOOGLE_CHROME_BUILD) if (InstallUtil::IsChromeSxSProcess()) { - dist = new GoogleChromeSxSDistribution(); + dist = GetOrCreateBrowserDistribution<GoogleChromeSxSDistribution>( + &g_browser_distribution); } else { - dist = new GoogleChromeDistribution(); + dist = GetOrCreateBrowserDistribution<GoogleChromeDistribution>( + &g_browser_distribution); } #else - dist = new BrowserDistribution(); + dist = GetOrCreateBrowserDistribution<BrowserDistribution>( + &g_browser_distribution); #endif - } } + return dist; } void BrowserDistribution::DoPostUninstallOperations( - const installer::Version& version, const std::wstring& local_data_path, + const installer::Version& version, const FilePath& local_data_path, const std::wstring& distribution_data) { } @@ -116,6 +152,7 @@ std::wstring BrowserDistribution::GetLongAppDescription() { return app_description; } +// static int BrowserDistribution::GetInstallReturnCode( installer_util::InstallStatus status) { switch (status) { @@ -145,7 +182,7 @@ std::wstring BrowserDistribution::GetStatsServerURL() { return L""; } -std::wstring BrowserDistribution::GetDistributionData(base::win::RegKey* key) { +std::wstring BrowserDistribution::GetDistributionData(HKEY root_key) { return L""; } @@ -183,10 +220,10 @@ void BrowserDistribution::UpdateDiffInstallStatus(bool system_install, void BrowserDistribution::LaunchUserExperiment( installer_util::InstallStatus status, const installer::Version& version, - bool system_install) { + const installer::Product& installation, bool system_level) { } void BrowserDistribution::InactiveUserToastExperiment(int flavor, - bool system_install) { + const installer::Product& installation) { } diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h index 39eecdd..749d6af 100644 --- a/chrome/installer/util/browser_distribution.h +++ b/chrome/installer/util/browser_distribution.h @@ -9,23 +9,38 @@ #pragma once #include "base/basictypes.h" +#include "base/file_path.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/version.h" -namespace base { -namespace win { -class RegKey; -} // namespace win -} // namespace base +#if defined(OS_WIN) +#include <windows.h> +#endif + +namespace installer { +class Product; +} class BrowserDistribution { public: virtual ~BrowserDistribution() {} + typedef enum DistributionType { + CHROME_BROWSER, + CHROME_FRAME, + CEEE, + }; + static BrowserDistribution* GetDistribution(); + static BrowserDistribution* GetSpecificDistribution(DistributionType type); + + DistributionType GetType() const { return type_; } + + static int GetInstallReturnCode(installer_util::InstallStatus install_status); + virtual void DoPostUninstallOperations(const installer::Version& version, - const std::wstring& local_data_path, + const FilePath& local_data_path, const std::wstring& distribution_data); virtual std::wstring GetAppGuid(); @@ -46,9 +61,6 @@ class BrowserDistribution { virtual std::wstring GetLongAppDescription(); - virtual int GetInstallReturnCode( - installer_util::InstallStatus install_status); - virtual std::string GetSafeBrowsingName(); virtual std::wstring GetStateKey(); @@ -57,7 +69,9 @@ class BrowserDistribution { virtual std::wstring GetStatsServerURL(); - virtual std::wstring GetDistributionData(base::win::RegKey* key); +#if defined(OS_WIN) + virtual std::wstring GetDistributionData(HKEY root_key); +#endif virtual std::wstring GetUninstallLinkName(); @@ -80,17 +94,23 @@ class BrowserDistribution { // experiment. This function determines if the user qualifies and if so it // sets the wheels in motion or in simple cases does the experiment itself. virtual void LaunchUserExperiment(installer_util::InstallStatus status, - const installer::Version& version, - bool system_install); + const installer::Version& version, + const installer::Product& installation, + bool system_level); // The user has qualified for the inactive user toast experiment and this // function just performs it. - virtual void InactiveUserToastExperiment(int flavor, bool system_install); + virtual void InactiveUserToastExperiment(int flavor, + const installer::Product& installation); protected: - BrowserDistribution() {} + BrowserDistribution() : type_(CHROME_BROWSER) {} + + template<class DistributionClass> + static BrowserDistribution* GetOrCreateBrowserDistribution( + BrowserDistribution** dist); - static BrowserDistribution* GetDistribution(bool chrome_frame); + DistributionType type_; private: DISALLOW_COPY_AND_ASSIGN(BrowserDistribution); diff --git a/chrome/installer/util/browser_distribution_unittest.cc b/chrome/installer/util/browser_distribution_unittest.cc index 55d0d8f..2733d1f 100644 --- a/chrome/installer/util/browser_distribution_unittest.cc +++ b/chrome/installer/util/browser_distribution_unittest.cc @@ -24,14 +24,23 @@ class BrowserDistributionTest : public testing::Test { // The distribution strings should not be empty. The unit tests are not linking // with the chrome resources so we cannot test official build. TEST(BrowserDistributionTest, StringsTest) { - BrowserDistribution *dist = BrowserDistribution::GetDistribution(); - ASSERT_TRUE(dist != NULL); - std::wstring name = dist->GetApplicationName(); - EXPECT_FALSE(name.empty()); - std::wstring desc = dist->GetAppDescription(); - EXPECT_FALSE(desc.empty()); - std::wstring alt_name = dist->GetAlternateApplicationName(); - EXPECT_FALSE(alt_name.empty()); + BrowserDistribution::DistributionType types[] = { + BrowserDistribution::CHROME_BROWSER, + BrowserDistribution::CHROME_FRAME, + // TODO(tommi): Also include CEEE. + }; + + for (int i = 0; i < arraysize(types); ++i) { + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution(types[i]); + ASSERT_TRUE(dist != NULL); + std::wstring name = dist->GetApplicationName(); + EXPECT_FALSE(name.empty()); + std::wstring desc = dist->GetAppDescription(); + EXPECT_FALSE(desc.empty()); + std::wstring alt_name = dist->GetAlternateApplicationName(); + EXPECT_FALSE(alt_name.empty()); + } } // The shortcut strings obtained by the shell utility functions should not @@ -39,8 +48,9 @@ TEST(BrowserDistributionTest, StringsTest) { TEST(BrowserDistributionTest, AlternateAndNormalShortcutName) { std::wstring normal_name; std::wstring alternate_name; - EXPECT_TRUE(ShellUtil::GetChromeShortcutName(&normal_name, false)); - EXPECT_TRUE(ShellUtil::GetChromeShortcutName(&alternate_name, true)); + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + EXPECT_TRUE(ShellUtil::GetChromeShortcutName(dist, &normal_name, false)); + EXPECT_TRUE(ShellUtil::GetChromeShortcutName(dist, &alternate_name, true)); EXPECT_NE(normal_name, alternate_name); EXPECT_FALSE(normal_name.empty()); EXPECT_FALSE(alternate_name.empty()); diff --git a/chrome/installer/util/chrome_frame_distribution.h b/chrome/installer/util/chrome_frame_distribution.h index f42d41f..6b071ae 100644 --- a/chrome/installer/util/chrome_frame_distribution.h +++ b/chrome/installer/util/chrome_frame_distribution.h @@ -54,11 +54,13 @@ class ChromeFrameDistribution : public BrowserDistribution { virtual void UpdateDiffInstallStatus(bool system_install, bool incremental_install, installer_util::InstallStatus install_status); - private: + protected: friend class BrowserDistribution; // Disallow construction from non-friends. - ChromeFrameDistribution() {} + ChromeFrameDistribution() { + type_ = BrowserDistribution::CHROME_FRAME; + } }; diff --git a/chrome/installer/util/google_chrome_distribution.cc b/chrome/installer/util/google_chrome_distribution.cc index 34a6333..375a4e1 100644 --- a/chrome/installer/util/google_chrome_distribution.cc +++ b/chrome/installer/util/google_chrome_distribution.cc @@ -26,6 +26,7 @@ #include "chrome/common/json_value_serializer.h" #include "chrome/common/pref_names.h" #include "chrome/common/result_codes.h" +#include "chrome/installer/util/product.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/l10n_string_util.h" #include "chrome/installer/util/google_update_constants.h" @@ -324,7 +325,7 @@ bool GoogleChromeDistribution::ExtractUninstallMetrics( #endif void GoogleChromeDistribution::DoPostUninstallOperations( - const installer::Version& version, const std::wstring& local_data_path, + const installer::Version& version, const FilePath& local_data_path, const std::wstring& distribution_data) { // Send the Chrome version and OS version as params to the form. // It would be nice to send the locale, too, but I don't see an @@ -357,7 +358,8 @@ void GoogleChromeDistribution::DoPostUninstallOperations( os_version; std::wstring uninstall_metrics; - if (ExtractUninstallMetricsFromFile(local_data_path, &uninstall_metrics)) { + if (ExtractUninstallMetricsFromFile(local_data_path.value(), + &uninstall_metrics)) { // The user has opted into anonymous usage data collection, so append // metrics and distribution data. command += uninstall_metrics; @@ -436,14 +438,12 @@ std::wstring GoogleChromeDistribution::GetStatsServerURL() { return L"https://clients4.google.com/firefox/metrics/collect"; } -std::wstring GoogleChromeDistribution::GetDistributionData( - base::win::RegKey* key) { - DCHECK(NULL != key); +std::wstring GoogleChromeDistribution::GetDistributionData(HKEY root_key) { std::wstring sub_key(google_update::kRegPathClientState); sub_key.append(L"\\"); sub_key.append(product_guid()); - base::win::RegKey client_state_key(key->Handle(), sub_key.c_str(), KEY_READ); + base::win::RegKey client_state_key(root_key, sub_key.c_str(), KEY_READ); std::wstring result; std::wstring brand_value; if (client_state_key.ReadValue(google_update::kRegRLZBrandField, @@ -557,8 +557,8 @@ void SetClient(std::wstring experiment_group, bool last_write) { // this function with |system_install| true and a REENTRY_SYS_UPDATE status. void GoogleChromeDistribution::LaunchUserExperiment( installer_util::InstallStatus status, const installer::Version& version, - bool system_install) { - if (system_install) { + const installer::Product& installation, bool system_level) { + if (system_level) { if (installer_util::NEW_VERSION_UPDATED == status) { // We need to relaunch as the interactive user. RelaunchSetupAsConsoleUser(installer_util::switches::kSystemLevelToast); @@ -586,10 +586,12 @@ void GoogleChromeDistribution::LaunchUserExperiment( } else { // Check browser usage inactivity by the age of the last-write time of the // chrome user data directory. - std::wstring user_data_dir = installer::GetChromeUserDataPath(); + FilePath user_data_dir(installation.GetUserDataPath()); + // TODO(cpu): re-enable experiment. const int kThirtyDays = 3000 * 24; - int dir_age_hours = GetDirectoryWriteAgeInHours(user_data_dir.c_str()); + int dir_age_hours = GetDirectoryWriteAgeInHours( + user_data_dir.value().c_str()); if (dir_age_hours < 0) { // This means that we failed to find the user data dir. The most likely // cause is that this user has not ever used chrome at all which can @@ -617,26 +619,32 @@ void GoogleChromeDistribution::LaunchUserExperiment( // System level: We have already been relaunched, so we don't need to be // quick, but we relaunch to follow the exact same codepath. RelaunchSetup(installer_util::switches::kInactiveUserToast, flavor, - system_install); + system_level); } // User qualifies for the experiment. Launch chrome with --try-chrome=flavor. void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, - bool system_install) { + const installer::Product& installation) { bool has_welcome_url = (flavor == 0); // Possibly add a url to launch depending on the experiment flavor. - std::wstring options(StringPrintf(L"--%ls=%d", - ASCIIToUTF16(switches::kTryChromeAgain).c_str(), flavor)); + CommandLine options(CommandLine::NO_PROGRAM); + options.AppendSwitchNative(switches::kTryChromeAgain, + base::IntToString16(flavor)); if (has_welcome_url) { - const std::wstring url(GetWelcomeBackUrl()); - options.append(L" -- "); - options.append(url); + // Prepend the url with a space. + std::wstring url(GetWelcomeBackUrl()); + options.AppendArg("--"); + options.AppendArgNative(url); + // The command line should now have the url added as: + // "chrome.exe -- <url>" + DCHECK_NE(std::wstring::npos, + options.command_line_string().find(L" -- " + url)); } // Launch chrome now. It will show the toast UI. int32 exit_code = 0; - if (!installer::LaunchChromeAndWaitForResult(system_install, - options, &exit_code)) + if (!installation.LaunchChromeAndWait(options, &exit_code)) return; + // The chrome process has exited, figure out what happened. const wchar_t* outcome = NULL; switch (exit_code) { @@ -658,10 +666,16 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, if (outcome != kToastExpUninstallGroup) return; + // The user wants to uninstall. This is a best effort operation. Note that // we waited for chrome to exit so the uninstall would not detect chrome // running. - base::LaunchApp(InstallUtil::GetChromeUninstallCmd(system_install), - false, false, NULL); + bool system_level_toast = CommandLine::ForCurrentProcess()->HasSwitch( + installer_util::switches::kSystemLevelToast); + + std::wstring cmd(InstallUtil::GetChromeUninstallCmd( + system_level_toast, this)); + + base::LaunchApp(cmd, false, false, NULL); } #endif diff --git a/chrome/installer/util/google_chrome_distribution.h b/chrome/installer/util/google_chrome_distribution.h index 53d754b..d6fa281 100644 --- a/chrome/installer/util/google_chrome_distribution.h +++ b/chrome/installer/util/google_chrome_distribution.h @@ -27,7 +27,7 @@ class GoogleChromeDistribution : public BrowserDistribution { // concatenated to the survey url if the file in local_data_path indicates // the user has opted in to providing anonymous usage data. virtual void DoPostUninstallOperations(const installer::Version& version, - const std::wstring& local_data_path, + const FilePath& local_data_path, const std::wstring& distribution_data); virtual std::wstring GetAppGuid(); @@ -55,7 +55,7 @@ class GoogleChromeDistribution : public BrowserDistribution { // This method reads data from the Google Update ClientState key for // potential use in the uninstall survey. It must be called before the // key returned by GetVersionKey() is deleted. - virtual std::wstring GetDistributionData(base::win::RegKey* key); + virtual std::wstring GetDistributionData(HKEY root_key); virtual std::wstring GetUninstallLinkName(); @@ -69,13 +69,15 @@ class GoogleChromeDistribution : public BrowserDistribution { bool incremental_install, installer_util::InstallStatus install_status); virtual void LaunchUserExperiment(installer_util::InstallStatus status, - const installer::Version& version, - bool system_install); + const installer::Version& version, + const installer::Product& installation, + bool system_level); // Assuming that the user qualifies, this function performs the inactive user // toast experiment. It will use chrome to show the UI and it will record the // outcome in the registry. - virtual void InactiveUserToastExperiment(int flavor, bool system_install); + virtual void InactiveUserToastExperiment(int flavor, + const installer::Product& installation); std::wstring product_guid() { return product_guid_; } diff --git a/chrome/installer/util/google_chrome_distribution_dummy.cc b/chrome/installer/util/google_chrome_distribution_dummy.cc index a50dd11..0cb540c 100644 --- a/chrome/installer/util/google_chrome_distribution_dummy.cc +++ b/chrome/installer/util/google_chrome_distribution_dummy.cc @@ -19,7 +19,7 @@ GoogleChromeDistribution::GoogleChromeDistribution() { void GoogleChromeDistribution::DoPostUninstallOperations( const installer::Version& version, - const std::wstring& local_data_path, + const FilePath& local_data_path, const std::wstring& distribution_data) { } @@ -78,8 +78,7 @@ std::wstring GoogleChromeDistribution::GetStatsServerURL() { return std::wstring(); } -std::wstring GoogleChromeDistribution::GetDistributionData( - base::win::RegKey* key) { +std::wstring GoogleChromeDistribution::GetDistributionData(HKEY root_key) { NOTREACHED(); return std::wstring(); } @@ -111,13 +110,12 @@ void GoogleChromeDistribution::UpdateDiffInstallStatus(bool system_install, void GoogleChromeDistribution::LaunchUserExperiment( installer_util::InstallStatus status, const installer::Version& version, - bool system_install) { + const installer::Product& installation, bool system_level) { NOTREACHED(); } -void GoogleChromeDistribution::InactiveUserToastExperiment( - int flavor, - bool system_install) { +void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, + const installer::Product& installation) { NOTREACHED(); } diff --git a/chrome/installer/util/google_chrome_distribution_unittest.cc b/chrome/installer/util/google_chrome_distribution_unittest.cc index 1e0ede8..10c2022 100644 --- a/chrome/installer/util/google_chrome_distribution_unittest.cc +++ b/chrome/installer/util/google_chrome_distribution_unittest.cc @@ -51,7 +51,8 @@ TEST(GoogleChromeDistTest, TestExtractUninstallMetrics) { ASSERT_TRUE(root.get()); std::wstring uninstall_metrics_string; GoogleChromeDistribution* dist = static_cast<GoogleChromeDistribution*>( - BrowserDistribution::GetDistribution()); + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER)); EXPECT_TRUE( dist->ExtractUninstallMetrics(*static_cast<DictionaryValue*>(root.get()), diff --git a/chrome/installer/util/helper.cc b/chrome/installer/util/helper.cc index 551b9c6..4da09a8 100644 --- a/chrome/installer/util/helper.cc +++ b/chrome/installer/util/helper.cc @@ -2,126 +2,40 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <windows.h> +#include "chrome/installer/util/helper.h" -#include "base/file_util.h" -#include "base/logging.h" #include "base/path_service.h" -#include "base/process_util.h" -#include "base/scoped_ptr.h" #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/delete_tree_work_item.h" -#include "chrome/installer/util/helper.h" -#include "chrome/installer/util/logging_installer.h" -#include "chrome/installer/util/util_constants.h" -#include "chrome/installer/util/version.h" -#include "chrome/installer/util/work_item_list.h" namespace { -std::wstring GetChromeInstallBasePath(bool system_install, - const wchar_t* subpath) { +FilePath GetChromeInstallBasePath(bool system, + BrowserDistribution* distribution, + const wchar_t* sub_path) { FilePath install_path; - if (system_install) { + if (system) { PathService::Get(base::DIR_PROGRAM_FILES, &install_path); } else { PathService::Get(base::DIR_LOCAL_APP_DATA, &install_path); } + if (!install_path.empty()) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - install_path = install_path.Append(dist->GetInstallSubDir()); - install_path = install_path.Append(subpath); + install_path = install_path.Append(distribution->GetInstallSubDir()); + install_path = install_path.Append(sub_path); } - return install_path.ToWStringHack(); + + return install_path; } } // namespace -std::wstring installer::GetChromeInstallPath(bool system_install) { - return GetChromeInstallBasePath(system_install, +FilePath installer::GetChromeInstallPath(bool system_install, + BrowserDistribution* dist) { + return GetChromeInstallBasePath(system_install, dist, installer_util::kInstallBinaryDir); } -std::wstring installer::GetChromeUserDataPath() { - return GetChromeInstallBasePath(false, installer_util::kInstallUserDataDir); -} - -bool installer::LaunchChrome(bool system_install) { - std::wstring chrome_exe(L"\""); - chrome_exe.append(installer::GetChromeInstallPath(system_install)); - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); - chrome_exe.append(L"\""); - return base::LaunchApp(chrome_exe, false, false, NULL); -} - -bool installer::LaunchChromeAndWaitForResult(bool system_install, - const std::wstring& options, - int32* exit_code) { - std::wstring chrome_exe(installer::GetChromeInstallPath(system_install)); - if (chrome_exe.empty()) - return false; - file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe); - - std::wstring command_line(L"\"" + chrome_exe + L"\""); - command_line.append(options); - STARTUPINFOW si = {sizeof(si)}; - PROCESS_INFORMATION pi = {0}; - if (!::CreateProcessW(chrome_exe.c_str(), - const_cast<wchar_t*>(command_line.c_str()), - NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, - &si, &pi)) { - return false; - } - - DWORD wr = ::WaitForSingleObject(pi.hProcess, INFINITE); - DWORD ret; - if (::GetExitCodeProcess(pi.hProcess, &ret) == 0) - return false; - - if (exit_code) - *exit_code = ret; - - ::CloseHandle(pi.hProcess); - ::CloseHandle(pi.hThread); - return true; -} - -void installer::RemoveOldVersionDirs(const std::wstring& chrome_path, - const std::wstring& latest_version_str) { - std::wstring search_path(chrome_path); - file_util::AppendToPath(&search_path, L"*"); - - WIN32_FIND_DATA find_file_data; - HANDLE file_handle = FindFirstFile(search_path.c_str(), &find_file_data); - if (file_handle == INVALID_HANDLE_VALUE) - return; - - BOOL ret = TRUE; - scoped_ptr<installer::Version> version; - scoped_ptr<installer::Version> latest_version( - installer::Version::GetVersionFromString(latest_version_str)); - - // We try to delete all directories whose versions are lower than - // latest_version. - while (ret) { - if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - VLOG(1) << "directory found: " << find_file_data.cFileName; - version.reset( - installer::Version::GetVersionFromString(find_file_data.cFileName)); - if (version.get() && latest_version->IsHigherThan(version.get())) { - std::wstring remove_dir(chrome_path); - file_util::AppendToPath(&remove_dir, find_file_data.cFileName); - std::wstring chrome_dll_path(remove_dir); - file_util::AppendToPath(&chrome_dll_path, installer_util::kChromeDll); - VLOG(1) << "deleting directory " << remove_dir; - scoped_ptr<DeleteTreeWorkItem> item; - item.reset(WorkItem::CreateDeleteTreeWorkItem(remove_dir, - chrome_dll_path)); - item->Do(); - } - } - ret = FindNextFile(file_handle, &find_file_data); - } - - FindClose(file_handle); +FilePath installer::GetChromeUserDataPath(BrowserDistribution* dist) { + return GetChromeInstallBasePath(false, dist, + installer_util::kInstallUserDataDir); } diff --git a/chrome/installer/util/helper.h b/chrome/installer/util/helper.h index dd4c3b8..747e11b 100644 --- a/chrome/installer/util/helper.h +++ b/chrome/installer/util/helper.h @@ -8,7 +8,9 @@ #define CHROME_INSTALLER_UTIL_HELPER_H_ #pragma once -#include <string> +#include "base/file_path.h" + +class BrowserDistribution; namespace installer { @@ -17,34 +19,13 @@ namespace installer { // system_install: if true, the function returns system wide location // (ProgramFiles\Google). Otherwise it returns user specific // location (Document And Settings\<user>\Local Settings...) -std::wstring GetChromeInstallPath(bool system_install); +FilePath GetChromeInstallPath(bool system_install, BrowserDistribution* dist); // This function returns the path to the directory that holds the user data, // this is always inside "Document And Settings\<user>\Local Settings.". Note // that this is the default user data directory and does not take into account // that it can be overriden with a command line parameter. -std::wstring GetChromeUserDataPath(); - -// Launches Chrome without waiting for its exit. -bool LaunchChrome(bool system_install); - -// Launches Chrome with given command line, waits for Chrome indefinitely -// (until it terminates), and gets the process exit code if available. -// The function returns true as long as Chrome is successfully launched. -// The status of Chrome at the return of the function is given by exit_code. -bool LaunchChromeAndWaitForResult(bool system_install, - const std::wstring& options, - int32* exit_code); - -// This function tries to remove all previous version directories after a new -// Chrome update. If an instance of Chrome with older version is still running -// on the system, its corresponding version directory will be left intact. -// (The version directory is subject for removal again during next update.) -// -// chrome_path: the root path of Chrome installation. -// latest_version_str: the latest version of Chrome installed. -void RemoveOldVersionDirs(const std::wstring& chrome_path, - const std::wstring& latest_version_str); +FilePath GetChromeUserDataPath(BrowserDistribution* dist); } // namespace installer diff --git a/chrome/installer/util/helper_unittest.cc b/chrome/installer/util/helper_unittest.cc index 7604e49..e9133bf 100644 --- a/chrome/installer/util/helper_unittest.cc +++ b/chrome/installer/util/helper_unittest.cc @@ -11,10 +11,15 @@ #include "base/path_service.h" #include "base/process_util.h" #include "base/string_util.h" +#include "chrome/installer/util/package.h" #include "chrome/installer/util/helper.h" +#include "chrome/installer/util/version.h" #include "chrome/installer/util/work_item.h" #include "testing/gtest/include/gtest/gtest.h" +using installer::Version; +using installer::Package; + namespace { class SetupHelperTest : public testing::Test { protected: @@ -103,8 +108,9 @@ TEST_F(SetupHelperTest, Delete) { CreateTextFile(chrome_dll_4.value(), text_content_1); ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); - std::wstring latest_version(L"1.0.4.0"); - installer::RemoveOldVersionDirs(chrome_dir.value(), latest_version); + scoped_ptr<Version> latest_version(Version::GetVersionFromString(L"1.0.4.0")); + scoped_refptr<Package> package(new Package(chrome_dir)); + package->RemoveOldVersionDirectories(*latest_version.get()); // old versions should be gone EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); @@ -176,8 +182,9 @@ TEST_F(SetupHelperTest, DeleteInUsed) { CreateTextFile(chrome_dll_4.value(), text_content_1); ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); - std::wstring latest_version(L"1.0.4.0"); - installer::RemoveOldVersionDirs(chrome_dir.value(), latest_version); + scoped_ptr<Version> latest_version(Version::GetVersionFromString(L"1.0.4.0")); + scoped_refptr<Package> install_path(new Package(chrome_dir)); + install_path->RemoveOldVersionDirectories(*latest_version.get()); // old versions not in used should be gone EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index 6c11f99..ae9c67d 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -73,21 +73,23 @@ bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) { return true; } -std::wstring InstallUtil::GetChromeUninstallCmd(bool system_install) { +std::wstring InstallUtil::GetChromeUninstallCmd(bool system_install, + BrowserDistribution* dist) { + DCHECK(dist); HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); RegKey key(root, dist->GetUninstallRegPath().c_str(), KEY_READ); std::wstring uninstall_cmd; key.ReadValue(installer_util::kUninstallStringField, &uninstall_cmd); return uninstall_cmd; } -installer::Version* InstallUtil::GetChromeVersion(bool system_install) { +installer::Version* InstallUtil::GetChromeVersion(BrowserDistribution* dist, + bool system_install) { + DCHECK(dist); RegKey key; std::wstring version_str; HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); if (!key.Open(reg_root, dist->GetVersionKey().c_str(), KEY_READ) || !key.ReadValue(google_update::kRegVersionField, &version_str)) { VLOG(1) << "No existing Chrome install found."; @@ -111,34 +113,6 @@ bool InstallUtil::IsOSSupported() { (version == base::win::VERSION_XP && major >= 2); } -void InstallUtil::WriteInstallerResult(bool system_install, - installer_util::InstallStatus status, - int string_resource_id, - const std::wstring* const launch_cmd) { - HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - std::wstring key = dist->GetStateKey(); - int installer_result = (dist->GetInstallReturnCode(status) == 0) ? 0 : 1; - scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); - install_list->AddCreateRegKeyWorkItem(root, key); - install_list->AddSetRegValueWorkItem(root, key, L"InstallerResult", - installer_result, true); - install_list->AddSetRegValueWorkItem(root, key, L"InstallerError", - status, true); - if (string_resource_id != 0) { - std::wstring msg = installer_util::GetLocalizedString(string_resource_id); - install_list->AddSetRegValueWorkItem(root, key, L"InstallerResultUIString", - msg, true); - } - if (launch_cmd != NULL && !launch_cmd->empty()) { - install_list->AddSetRegValueWorkItem(root, key, - L"InstallerSuccessLaunchCmdLine", - *launch_cmd, true); - } - if (!install_list->Do()) - LOG(ERROR) << "Failed to record installer error information in registry."; -} - bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) { wchar_t program_files_path[MAX_PATH] = {0}; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, @@ -155,7 +129,7 @@ bool InstallUtil::IsChromeFrameProcess() { return prefs.install_chrome_frame(); } -bool InstallUtil::IsChromeSxSProcess() { +bool CheckIsChromeSxSProcess() { CommandLine* command_line = CommandLine::ForCurrentProcess(); CHECK(command_line); @@ -173,64 +147,9 @@ bool InstallUtil::IsChromeSxSProcess() { chrome_sxs_dir); } -bool InstallUtil::IsMSIProcess(bool system_level) { - // Initialize the static msi flags. - static bool is_msi_ = false; - static bool msi_checked_ = false; - - if (!msi_checked_) { - msi_checked_ = true; - - const MasterPreferences& prefs = GetMasterPreferencesForCurrentProcess(); - - bool is_msi = false; - prefs.GetBool(installer_util::master_preferences::kMsi, &is_msi); - - if (!is_msi) { - // We didn't find it in the preferences, try looking in the registry. - HKEY reg_root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - RegKey key; - DWORD msi_value; - - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - DCHECK(dist); - - bool success = false; - std::wstring reg_key(dist->GetStateKey()); - if (key.Open(reg_root, reg_key.c_str(), KEY_READ | KEY_WRITE)) { - if (key.ReadValueDW(google_update::kRegMSIField, &msi_value)) { - is_msi = (msi_value == 1); - } - } - } - - is_msi_ = is_msi; - } - - return is_msi_; -} - -bool InstallUtil::SetMSIMarker(bool system_level, bool set) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - DCHECK(dist); - std::wstring client_state_path(dist->GetStateKey()); - - bool success = false; - HKEY reg_root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - RegKey client_state_key; - if (client_state_key.Open(reg_root, client_state_path.c_str(), - KEY_READ | KEY_WRITE)) { - DWORD msi_value = set ? 1 : 0; - if (client_state_key.WriteValue(google_update::kRegMSIField, msi_value)) { - success = true; - } else { - LOG(ERROR) << "Could not write msi value to client state key."; - } - } else { - LOG(ERROR) << "Could not open client state key!"; - } - - return success; +bool InstallUtil::IsChromeSxSProcess() { + static bool sxs = CheckIsChromeSxSProcess(); + return sxs; } bool InstallUtil::BuildDLLRegistrationList(const std::wstring& install_path, @@ -257,8 +176,8 @@ bool InstallUtil::DeleteRegistryKey(RegKey& root_key, const std::wstring& key_path) { VLOG(1) << "Deleting registry key " << key_path; if (!root_key.DeleteKey(key_path.c_str()) && - ::GetLastError() != ERROR_MOD_NOT_FOUND) { - LOG(ERROR) << "Failed to delete registry key: " << key_path; + ::GetLastError() != ERROR_FILE_NOT_FOUND) { + PLOG(ERROR) << "Failed to delete registry key: " << key_path; return false; } return true; diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h index 027a8ee..5fd87be 100644 --- a/chrome/installer/util/install_util.h +++ b/chrome/installer/util/install_util.h @@ -21,6 +21,7 @@ #include "chrome/installer/util/version.h" class WorkItemList; +class BrowserDistribution; namespace base { namespace win { @@ -39,24 +40,20 @@ class InstallUtil { // Reads the uninstall command for Chromium from registry and returns it. // If system_install is true the command is read from HKLM, otherwise // from HKCU. - static std::wstring GetChromeUninstallCmd(bool system_install); + static std::wstring GetChromeUninstallCmd(bool system_install, + BrowserDistribution* dist); + // Find the version of Chrome installed on the system by checking the // Google Update registry key. Returns the version or NULL if no version is // found. // system_install: if true, looks for version number under the HKLM root, // otherwise looks under the HKCU. - static installer::Version* GetChromeVersion(bool system_install); + static installer::Version* GetChromeVersion(BrowserDistribution* dist, + bool system_install); // This function checks if the current OS is supported for Chromium. static bool IsOSSupported(); - // This function sets installer error information in registry so that Google - // Update can read it and display to the user. - static void WriteInstallerResult(bool system_install, - installer_util::InstallStatus status, - int string_resource_id, - const std::wstring* const launch_cmd); - // Returns true if this installation path is per user, otherwise returns // false (per machine install, meaning: the exe_path contains path to // Program Files). @@ -74,21 +71,6 @@ class InstallUtil { // by either --chrome-sxs or the executable path). static bool IsChromeSxSProcess(); - // Returns true if this setup process is running as an install managed by an - // MSI wrapper. There are three things that are checked: - // 1) the presence of --msi on the command line - // 2) the presence of "msi": true in the master preferences file - // 3) the presence of a DWORD value in the ClientState key called msi with - // value 1 - // NOTE: This method is NOT thread safe. - static bool IsMSIProcess(bool system_level); - - - // Sets the boolean MSI marker for this installation if set is true or clears - // it otherwise. The MSI marker is stored in the registry under the - // ClientState key. - static bool SetMSIMarker(bool system_level, bool set); - // Adds all DLLs in install_path whose names are given by dll_names to a // work item list containing registration or unregistration actions. // diff --git a/chrome/installer/util/package.cc b/chrome/installer/util/package.cc new file mode 100644 index 0000000..37dc97c --- /dev/null +++ b/chrome/installer/util/package.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/installer/util/package.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/win/registry.h" +#include "chrome/installer/util/delete_tree_work_item.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/product.h" + +using base::win::RegKey; + +namespace installer { + +Package::Package(const FilePath& path) : path_(path) { +} + +Package::~Package() { +} + +const FilePath& Package::path() const { + return path_; +} + +const Products& Package::products() const { + return products_; +} + +bool Package::IsEqual(const FilePath& path) const { + return FilePath::CompareEqualIgnoreCase(path_.value(), path.value()); +} + +void Package::AssociateProduct(const Product* product) { +#ifndef NDEBUG + for (size_t i = 0; i < products_.size(); ++i) { + DCHECK_EQ(product->system_level(), products_[i]->system_level()); + DCHECK_NE(product->distribution()->GetType(), + products_[i]->distribution()->GetType()); + } +#endif + products_.push_back(product); +} + +bool Package::system_level() const { + // Convenience getter that returns the system_level value of the first + // product for this folder. All distributions must have the same + // value, so the function also checks this in debug builds. + if (!products_.size()) { + NOTREACHED() << "this should not be possible"; + return false; + } + return products_[0]->system_level(); +} + +FilePath Package::GetInstallerDirectory( + const Version& version) const { + return path_.Append(version.GetString()) + .Append(installer_util::kInstallerDir); +} + +Version* Package::GetCurrentVersion() const { + scoped_ptr<Version> current_version; + // Be aware that there might be a pending "new_chrome.exe" already in the + // installation path. + FilePath new_chrome_exe(path_.Append(installer_util::kChromeNewExe)); + bool new_chrome_exists = file_util::PathExists(new_chrome_exe); + + for (size_t i = 0; i < products_.size(); ++i) { + const Product* product = products_[i]; + HKEY root = product->system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; + RegKey chrome_key(root, product->distribution()->GetVersionKey().c_str(), + KEY_READ); + std::wstring version; + if (new_chrome_exists) + chrome_key.ReadValue(google_update::kRegOldVersionField, &version); + + if (version.empty()) + chrome_key.ReadValue(google_update::kRegVersionField, &version); + + if (!version.empty()) { + scoped_ptr<Version> this_version(Version::GetVersionFromString(version)); + if (this_version.get()) { + if (!current_version.get() || + current_version->IsHigherThan(this_version.get())) { + current_version.reset(this_version.release()); + } else if (current_version.get()) { + DCHECK_EQ(current_version->GetString(), this_version->GetString()) + << "found distributions of different versions in the same " + "installation folder!"; + } + } + } + } + + return current_version.release(); +} + +void Package::RemoveOldVersionDirectories( + const Version& latest_version) const { + std::wstring search_path(path_.value()); + file_util::AppendToPath(&search_path, L"*"); + + // TODO(tommi): use file_util::FileEnumerator. + WIN32_FIND_DATA find_file_data; + HANDLE file_handle = FindFirstFile(search_path.c_str(), &find_file_data); + if (file_handle == INVALID_HANDLE_VALUE) { + VLOG(1) << "No directories found under: " << search_path; + return; + } + + BOOL ret = TRUE; + scoped_ptr<Version> version; + + // We try to delete all directories whose versions are lower than + // latest_version. + while (ret) { + if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + lstrcmpW(find_file_data.cFileName, L"..") != 0 && + lstrcmpW(find_file_data.cFileName, L".") != 0) { + VLOG(1) << "directory found: " << find_file_data.cFileName; + version.reset(Version::GetVersionFromString(find_file_data.cFileName)); + if (version.get() && latest_version.IsHigherThan(version.get())) { + std::wstring remove_dir(path_.value()); + file_util::AppendToPath(&remove_dir, find_file_data.cFileName); + std::wstring chrome_dll_path(remove_dir); + file_util::AppendToPath(&chrome_dll_path, installer_util::kChromeDll); + VLOG(1) << "Deleting directory: " << remove_dir; + // TODO(tommi): We should support more "key files". One for each + // associated Product. Maybe the relative key file path should + // be a property of BrowserDistribution. + scoped_ptr<DeleteTreeWorkItem> item( + WorkItem::CreateDeleteTreeWorkItem(remove_dir, chrome_dll_path)); + if (!item->Do()) + item->Rollback(); + } + } + ret = FindNextFile(file_handle, &find_file_data); + } + + FindClose(file_handle); +} + +} // namespace installer + diff --git a/chrome/installer/util/package.h b/chrome/installer/util/package.h new file mode 100644 index 0000000..c7318e1 --- /dev/null +++ b/chrome/installer/util/package.h @@ -0,0 +1,73 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_INSTALLER_UTIL_PACKAGE_H_ +#define CHROME_INSTALLER_UTIL_PACKAGE_H_ +#pragma once + +#include <vector> + +#include "base/file_path.h" +#include "base/ref_counted.h" + +class CommandLine; + +namespace installer { + +class Product; +class Version; + +typedef std::vector<scoped_refptr<const Product> > Products; + +// Represents a physical installation. An instance of this class is associated +// with one or more Product instances. Product objects can share a Package but +// not vice versa. +class Package : public base::RefCounted<Package> { + public: + explicit Package(const FilePath& path); + + // Returns the path of the installation folder. + const FilePath& path() const; + + const Products& products() const; + + bool system_level() const; + + bool IsEqual(const FilePath& path) const; + + void AssociateProduct(const Product* distribution); + + // Get path to the installer under Chrome version folder + // (for example <path>\Google\Chrome\<Version>\installer) + FilePath GetInstallerDirectory(const Version& version) const; + + // Figure out the oldest currently installed version for this package + // Returns NULL if none is found. Caller is responsible for freeing + // the returned Version object if valid. + // The function DCHECKs if it finds that not all products in this + // folder have the same current version. + Version* GetCurrentVersion() const; + + // Tries to remove all previous version directories (after a new Chrome + // update). If an instance of Chrome with older version is still running + // on the system, its corresponding version directory will be left intact. + // (The version directory is subject for removal again during next update.) + // + // latest_version: the latest version of Chrome installed. + void RemoveOldVersionDirectories(const Version& latest_version) const; + + protected: + FilePath path_; + Products products_; + + private: + friend class base::RefCounted<Package>; + ~Package(); + + DISALLOW_COPY_AND_ASSIGN(Package); +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_PACKAGE_H_ diff --git a/chrome/installer/util/package_unittest.cc b/chrome/installer/util/package_unittest.cc new file mode 100644 index 0000000..575716d --- /dev/null +++ b/chrome/installer/util/package_unittest.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/package.h" +#include "chrome/installer/util/product.h" +#include "chrome/installer/util/product_unittest.h" +#include "chrome/installer/util/util_constants.h" +#include "chrome/installer/util/version.h" + +using base::win::RegKey; +using base::win::ScopedHandle; +using installer::Package; +using installer::Product; +using installer::Version; + +class PackageTest : public TestWithTempDirAndDeleteTempOverrideKeys { + protected: +}; + +// Tests a few basic things of the Package class. Makes sure that the path +// operations are correct +TEST_F(PackageTest, Basic) { + scoped_refptr<Package> package(new Package(test_dir_.path())); + EXPECT_EQ(test_dir_.path().value(), package->path().value()); + EXPECT_TRUE(package->IsEqual(test_dir_.path())); + EXPECT_EQ(0U, package->products().size()); + + const wchar_t kOldVersion[] = L"1.2.3.4"; + const wchar_t kNewVersion[] = L"2.3.4.5"; + + scoped_ptr<Version> new_version(Version::GetVersionFromString(kNewVersion)); + scoped_ptr<Version> old_version(Version::GetVersionFromString(kOldVersion)); + ASSERT_TRUE(new_version.get() != NULL); + ASSERT_TRUE(old_version.get() != NULL); + + FilePath installer_dir(package->GetInstallerDirectory(*new_version.get())); + EXPECT_FALSE(installer_dir.empty()); + + FilePath new_version_dir(package->path().Append(new_version->GetString())); + FilePath old_version_dir(package->path().Append(old_version->GetString())); + + EXPECT_FALSE(file_util::PathExists(new_version_dir)); + EXPECT_FALSE(file_util::PathExists(old_version_dir)); + + EXPECT_FALSE(file_util::PathExists(installer_dir)); + file_util::CreateDirectory(installer_dir); + EXPECT_TRUE(file_util::PathExists(new_version_dir)); + + file_util::CreateDirectory(old_version_dir); + EXPECT_TRUE(file_util::PathExists(old_version_dir)); + + // Create a fake chrome.dll key file in the old version directory. This + // should prevent the old version directory from getting deleted. + FilePath old_chrome_dll(old_version_dir.Append(installer_util::kChromeDll)); + EXPECT_FALSE(file_util::PathExists(old_chrome_dll)); + + // Hold on to the file exclusively to prevent the directory from + // being deleted. + ScopedHandle file(::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ, + 0, NULL, OPEN_ALWAYS, 0, NULL)); + EXPECT_TRUE(file.IsValid()); + EXPECT_TRUE(file_util::PathExists(old_chrome_dll)); + + package->RemoveOldVersionDirectories(*new_version.get()); + // The old directory should still exist. + EXPECT_TRUE(file_util::PathExists(old_version_dir)); + EXPECT_TRUE(file_util::PathExists(new_version_dir)); + + // Now close the file handle to make it possible to delete our key file. + file.Close(); + + package->RemoveOldVersionDirectories(*new_version.get()); + // The new directory should still exist. + EXPECT_TRUE(file_util::PathExists(new_version_dir)); + + // Now, the old directory and key file should be gone. + EXPECT_FALSE(file_util::PathExists(old_chrome_dll)); + EXPECT_FALSE(file_util::PathExists(old_version_dir)); +} + +TEST_F(PackageTest, WithProduct) { + TempRegKeyOverride::DeleteAllTempKeys(); + + // TODO(tommi): We should mock this and use our mocked distribution. + const bool system_level = true; + BrowserDistribution* distribution = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + scoped_refptr<Package> package(new Package(test_dir_.path())); + scoped_refptr<Product> product(new Product(distribution, + system_level, + package.get())); + EXPECT_EQ(1U, package->products().size()); + EXPECT_EQ(system_level, package->system_level()); + + const wchar_t kCurrentVersion[] = L"1.2.3.4"; + scoped_ptr<Version> current_version( + Version::GetVersionFromString(kCurrentVersion)); + + HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + { + TempRegKeyOverride override(root, L"root_pit"); + RegKey chrome_key(root, distribution->GetVersionKey().c_str(), + KEY_ALL_ACCESS); + EXPECT_TRUE(chrome_key.Valid()); + if (chrome_key.Valid()) { + chrome_key.WriteValue(google_update::kRegVersionField, + current_version->GetString().c_str()); + // TODO(tommi): Also test for when there exists a new_chrome.exe. + scoped_ptr<Version> found_version(package->GetCurrentVersion()); + EXPECT_TRUE(found_version.get() != NULL); + if (found_version.get()) { + EXPECT_TRUE(current_version->IsEqual(*found_version.get())); + } + } + } + + TempRegKeyOverride::DeleteAllTempKeys(); +} diff --git a/chrome/installer/util/product.cc b/chrome/installer/util/product.cc new file mode 100644 index 0000000..60846e3 --- /dev/null +++ b/chrome/installer/util/product.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/installer/util/product.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/process_util.h" +#include "base/win/registry.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/l10n_string_util.h" +#include "chrome/installer/util/master_preferences.h" +#include "chrome/installer/util/master_preferences_constants.h" +#include "chrome/installer/util/package.h" +#include "chrome/installer/util/work_item_list.h" + +using base::win::RegKey; +using installer_util::MasterPreferences; + +namespace { +class ProductIsOfType { + public: + explicit ProductIsOfType(BrowserDistribution::DistributionType type) + : type_(type) { } + bool operator()( + const scoped_refptr<const installer::Product>& pi) const { + return pi->distribution()->GetType() == type_; + } + private: + BrowserDistribution::DistributionType type_; +}; // class ProductIsOfType +} // end namespace + +namespace installer { + +const Product* FindProduct(const Products& products, + BrowserDistribution::DistributionType type) { + Products::const_iterator i = + std::find_if(products.begin(), products.end(), ProductIsOfType(type)); + return i == products.end() ? NULL : *i; +} + +void WriteInstallerResult(const Products& products, + installer_util::InstallStatus status, + int string_resource_id, + const std::wstring* const launch_cmd) { + Products::const_iterator end = products.end(); + for (Products::const_iterator i = products.begin(); i != end; ++i) + (*i)->WriteInstallerResult(status, string_resource_id, launch_cmd); +} + +//////////////////////////////////////////////////////////////////////////////// + +Product::Product(BrowserDistribution* distribution, bool system_level, + Package* package) + : distribution_(distribution), + system_level_(system_level), + package_(package), + msi_(MSI_NOT_CHECKED) { + package_->AssociateProduct(this); +} + +const Package& Product::package() const { + return *package_.get(); +} + +FilePath Product::GetUserDataPath() const { + return GetChromeUserDataPath(distribution_); +} + +bool Product::LaunchChrome() const { + const FilePath& install_package = package_->path(); + bool success = !install_package.empty(); + if (success) { + CommandLine cmd(install_package.Append(installer_util::kChromeExe)); + success = base::LaunchApp(cmd, false, false, NULL); + } + return success; +} + +bool Product::LaunchChromeAndWait(const CommandLine& options, + int32* exit_code) const { + const FilePath& install_package = package_->path(); + if (install_package.empty()) + return false; + + CommandLine cmd(install_package.Append(installer_util::kChromeExe)); + cmd.AppendArguments(options, false); + + bool success = false; + STARTUPINFOW si = { sizeof(si) }; + PROCESS_INFORMATION pi = {0}; + // Cast away constness of the command_line_string() since CreateProcess + // might modify the string (insert \0 to separate the program from the + // arguments). Since we're not using the cmd variable beyond this point + // we don't care. + if (!::CreateProcess(cmd.GetProgram().value().c_str(), + const_cast<wchar_t*>(cmd.command_line_string().c_str()), + NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, + &si, &pi)) { + PLOG(ERROR) << "Failed to launch: " << cmd.command_line_string(); + } else { + ::CloseHandle(pi.hThread); + + DWORD ret = ::WaitForSingleObject(pi.hProcess, INFINITE); + DLOG_IF(ERROR, ret != WAIT_OBJECT_0) + << "Unexpected return value from WaitForSingleObject: " << ret; + if (::GetExitCodeProcess(pi.hProcess, &ret)) { + DCHECK(ret != STILL_ACTIVE); + success = true; + if (exit_code) + *exit_code = ret; + } else { + PLOG(ERROR) << "GetExitCodeProcess failed"; + } + + ::CloseHandle(pi.hProcess); + } + + return success; +} + +bool Product::IsMsi() const { + if (msi_ == MSI_NOT_CHECKED) { + msi_ = NOT_MSI; // Covers failure cases below. + + const MasterPreferences& prefs = + InstallUtil::GetMasterPreferencesForCurrentProcess(); + + bool is_msi = false; + prefs.GetBool(installer_util::master_preferences::kMsi, &is_msi); + + if (!is_msi) { + // We didn't find it in the preferences, try looking in the registry. + HKEY reg_root = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + RegKey key; + if (key.Open(reg_root, distribution_->GetStateKey().c_str(), KEY_READ)) { + DWORD msi_value; + if (key.ReadValueDW(google_update::kRegMSIField, &msi_value)) { + msi_ = (msi_value == 1) ? IS_MSI : NOT_MSI; + } + } + } else { + msi_ = IS_MSI; + } + } + + return msi_ == IS_MSI; +} + +bool Product::SetMsiMarker(bool set) const { + bool success = false; + HKEY reg_root = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + RegKey client_state_key; + if (client_state_key.Open(reg_root, distribution_->GetStateKey().c_str(), + KEY_READ | KEY_WRITE)) { + DWORD msi_value = set ? 1 : 0; + if (client_state_key.WriteValue(google_update::kRegMSIField, msi_value)) { + success = true; + } else { + LOG(ERROR) << "Could not write MSI value to client state key."; + } + } else { + LOG(ERROR) << "SetMsiMarker: Could not open client state key!"; + } + + return success; +} + +void Product::WriteInstallerResult( + installer_util::InstallStatus status, int string_resource_id, + const std::wstring* const launch_cmd) const { + HKEY root = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + std::wstring key(distribution_->GetStateKey()); + int installer_result = distribution_->GetInstallReturnCode(status) == 0 ? + 0 : 1; + scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); + install_list->AddCreateRegKeyWorkItem(root, key); + install_list->AddSetRegValueWorkItem(root, key, + installer_util::kInstallerResult, + installer_result, true); + install_list->AddSetRegValueWorkItem(root, key, + installer_util::kInstallerError, + status, true); + if (string_resource_id != 0) { + std::wstring msg = installer_util::GetLocalizedString(string_resource_id); + install_list->AddSetRegValueWorkItem(root, key, + installer_util::kInstallerResultUIString, msg, true); + } + if (launch_cmd != NULL && !launch_cmd->empty()) { + install_list->AddSetRegValueWorkItem(root, key, + installer_util::kInstallerSuccessLaunchCmdLine, *launch_cmd, true); + } + if (!install_list->Do()) + LOG(ERROR) << "Failed to record installer error information in registry."; +} + +Version* Product::GetInstalledVersion() const { + return InstallUtil::GetChromeVersion(distribution_, system_level_); +} + +/////////////////////////////////////////////////////////////////////////////// +ProductPackageMapping::ProductPackageMapping(bool system_level) + : system_level_(system_level) { +} + +const Packages& ProductPackageMapping::packages() const { + return packages_; +} + +const Products& ProductPackageMapping::products() const { + return products_; +} + +bool ProductPackageMapping::AddDistribution(BrowserDistribution* distribution) { + DCHECK(distribution); + // Each product type can be added exactly once. + DCHECK(FindProduct(products_, distribution->GetType()) == NULL); + + FilePath install_package(GetChromeInstallPath(system_level_, distribution)); + + scoped_refptr<Package> target_package; + for (size_t i = 0; i < packages_.size(); ++i) { + if (packages_[i]->IsEqual(install_package)) { + // Use an existing Package. + target_package = packages_[i]; + break; + } + } + + if (!target_package.get()) { + // create new one and add. + target_package = new Package(install_package); + packages_.push_back(target_package); + } + + scoped_refptr<Product> product( + new Product(distribution, system_level_, target_package)); +#ifndef NDEBUG + for (size_t i = 0; i < products_.size(); ++i) { + DCHECK_EQ(product->IsMsi(), products_[i]->IsMsi()); + } +#endif + products_.push_back(product); + + return true; +} + +bool ProductPackageMapping::AddDistribution( + BrowserDistribution::DistributionType type) { + BrowserDistribution* distribution = + BrowserDistribution::GetSpecificDistribution(type); + if (!distribution) { + NOTREACHED(); + return false; + } + + return AddDistribution(distribution); +} + +} // namespace installer + diff --git a/chrome/installer/util/product.h b/chrome/installer/util/product.h new file mode 100644 index 0000000..639e48a --- /dev/null +++ b/chrome/installer/util/product.h @@ -0,0 +1,151 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_INSTALLER_UTIL_PRODUCT_H_ +#define CHROME_INSTALLER_UTIL_PRODUCT_H_ +#pragma once + +#include <vector> + +#include "base/ref_counted.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/package.h" + +class CommandLine; + +namespace installer { + +class Product; +class Package; +class Version; + +typedef std::vector<scoped_refptr<Package> > Packages; +typedef std::vector<scoped_refptr<const Product> > Products; + +const Product* FindProduct(const Products& products, + BrowserDistribution::DistributionType type); + +// Calls WriteInstallerResult for each Product object. +void WriteInstallerResult(const Products& products, + installer_util::InstallStatus status, + int string_resource_id, + const std::wstring* const launch_cmd); + +// Represents an installation of a specific product which has a one-to-one +// relation to a BrowserDistribution. A product has registry settings, related +// installation/uninstallation actions and exactly one Package that represents +// the files on disk. The Package may be shared with other Product instances, +// so only the last Product to be uninstalled should remove the package. +// Right now there are no classes that derive from Product, but in +// the future, as we move away from global functions and towards a data driven +// installation, each distribution could derive from this class and provide +// distribution specific functionality. +class Product : public base::RefCounted<Product> { + public: + Product(BrowserDistribution* distribution, bool system_level, + Package* installation_package); + + BrowserDistribution* distribution() const { + return distribution_; + } + + bool system_level() const { + return system_level_; + } + + // Returns the install package object for the installation of this product. + // If the product is installed at system level,the function returns a system + // wide location (ProgramFiles\Google). Otherwise it returns a package in a + // user specific location (Users\<user>\Local Settings...) + const Package& package() const; + + // Returns the path to the directory that holds the user data. This is always + // inside "Users\<user>\Local Settings". Note that this is the default user + // data directory and does not take into account that it can be overriden with + // a command line parameter. + FilePath GetUserDataPath() const; + + // Launches Chrome without waiting for it to exit. + bool LaunchChrome() const; + + // Launches Chrome with given command line, waits for Chrome indefinitely + // (until it terminates), and gets the process exit code if available. + // The function returns true as long as Chrome is successfully launched. + // The status of Chrome at the return of the function is given by exit_code. + // NOTE: The 'options' CommandLine object should only contain parameters. + // The program part will be ignored. + bool LaunchChromeAndWait(const CommandLine& options, int32* exit_code) const; + + // Returns true if this setup process is running as an install managed by an + // MSI wrapper. There are three things that are checked: + // 1) the presence of --msi on the command line + // 2) the presence of "msi": true in the master preferences file + // 3) the presence of a DWORD value in the ClientState key called msi with + // value 1 + bool IsMsi() const; + + // Sets the boolean MSI marker for this installation if set is true or clears + // it otherwise. The MSI marker is stored in the registry under the + // ClientState key. + bool SetMsiMarker(bool set) const; + + // Sets installer error information in registry so that Google Update can read + // it and display to the user. + void WriteInstallerResult(installer_util::InstallStatus status, + int string_resource_id, + const std::wstring* const launch_cmd) const; + + // Find the version of this product installed on the system by checking the + // Google Update registry key. Returns the version or NULL if no version is + // found. Caller must free the returned Version object. + Version* GetInstalledVersion() const; + + protected: + BrowserDistribution* distribution_; + scoped_refptr<Package> package_; + bool system_level_; + mutable enum MsiState { + MSI_NOT_CHECKED, + IS_MSI, + NOT_MSI, + } msi_; + + private: + friend class base::RefCounted<Product>; + ~Product() { + } + DISALLOW_COPY_AND_ASSIGN(Product); +}; + +// A collection of Product objects and related physical installation +// packages. Each Product is associated with a single installation +// package object, and each package object is associated with one or more +// Product objects. +class ProductPackageMapping { + public: + explicit ProductPackageMapping(bool system_level); + + bool system_level() const { + return system_level_; + } + + const Packages& packages() const; + + const Products& products() const; + + bool AddDistribution(BrowserDistribution::DistributionType type); + bool AddDistribution(BrowserDistribution* distribution); + + protected: + bool system_level_; + Packages packages_; + Products products_; + + private: + DISALLOW_COPY_AND_ASSIGN(ProductPackageMapping); +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_PRODUCT_H_ diff --git a/chrome/installer/util/product_unittest.cc b/chrome/installer/util/product_unittest.cc new file mode 100644 index 0000000..7dab702 --- /dev/null +++ b/chrome/installer/util/product_unittest.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/installer/util/product_unittest.h" + +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "chrome/installer/util/chrome_frame_distribution.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/product.h" + +using base::win::RegKey; +using base::win::ScopedHandle; +using installer::Package; +using installer::Product; +using installer::ProductPackageMapping; +using installer::Version; + +void TestWithTempDir::SetUp() { + // Name a subdirectory of the user temp directory. + ASSERT_TRUE(test_dir_.CreateUniqueTempDir()); +} + +void TestWithTempDir::TearDown() { + logging::CloseLogFile(); + ASSERT_TRUE(test_dir_.Delete()); +} + +//////////////////////////////////////////////////////////////////////////////// + +void TestWithTempDirAndDeleteTempOverrideKeys::SetUp() { + TempRegKeyOverride::DeleteAllTempKeys(); + TestWithTempDir::SetUp(); +} + +void TestWithTempDirAndDeleteTempOverrideKeys::TearDown() { + TestWithTempDir::TearDown(); + TempRegKeyOverride::DeleteAllTempKeys(); +} + +//////////////////////////////////////////////////////////////////////////////// +const wchar_t TempRegKeyOverride::kTempTestKeyPath[] = + L"Software\\Chromium\\TempTestKeys"; + +TempRegKeyOverride::TempRegKeyOverride(HKEY override, const wchar_t* temp_name) + : override_(override), temp_name_(temp_name) { + DCHECK(temp_name && lstrlenW(temp_name)); + std::wstring key_path(kTempTestKeyPath); + key_path += L"\\" + temp_name_; + EXPECT_TRUE(temp_key_.Create(HKEY_CURRENT_USER, key_path.c_str(), + KEY_ALL_ACCESS)); + EXPECT_EQ(ERROR_SUCCESS, + ::RegOverridePredefKey(override_, temp_key_.Handle())); +} + +TempRegKeyOverride::~TempRegKeyOverride() { + ::RegOverridePredefKey(override_, NULL); + // The temp key will be deleted via a call to DeleteAllTempKeys(). +} + +// static +void TempRegKeyOverride::DeleteAllTempKeys() { + RegKey key; + if (key.Open(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS)) { + key.DeleteKey(kTempTestKeyPath); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +class ProductTest : public TestWithTempDirAndDeleteTempOverrideKeys { + protected: +}; + +TEST_F(ProductTest, ProductInstallBasic) { + // TODO(tommi): We should mock this and use our mocked distribution. + const bool system_level = true; + BrowserDistribution* distribution = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + scoped_refptr<Package> package(new Package(test_dir_.path())); + scoped_refptr<Product> product(new Product(distribution, system_level, + package.get())); + + EXPECT_EQ(system_level, product->system_level()); + FilePath user_data(product->GetUserDataPath()); + EXPECT_FALSE(user_data.empty()); + EXPECT_NE(std::wstring::npos, + user_data.value().find(installer_util::kInstallUserDataDir)); + + FilePath program_files; + PathService::Get(base::DIR_PROGRAM_FILES, &program_files); + // The User Data path should never be under program files, even though + // system_level is true. + EXPECT_EQ(std::wstring::npos, + user_data.value().find(program_files.value())); + + // We started out with a non-msi product. + EXPECT_FALSE(product->IsMsi()); + + HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + { + TempRegKeyOverride override(root, L"root_pit"); + + // Create a make-believe client state key. + RegKey key; + std::wstring state_key_path(distribution->GetStateKey()); + ASSERT_TRUE(key.Create(root, state_key_path.c_str(), KEY_ALL_ACCESS)); + + // Set the MSI marker, delete the objects, create new ones and verify + // that we now see the MSI marker. + EXPECT_TRUE(product->SetMsiMarker(true)); + package = new Package(test_dir_.path()); + product = new Product(distribution, system_level, package.get()); + EXPECT_TRUE(product->IsMsi()); + + // See if WriteInstallerResult writes anything. + std::wstring launch_cmd(L"chrome.exe --this-is-a-test"); + product->WriteInstallerResult(installer_util::TEMP_DIR_FAILED, + 0, &launch_cmd); + std::wstring found_launch_cmd; + key.ReadValue(installer_util::kInstallerSuccessLaunchCmdLine, + &found_launch_cmd); + EXPECT_EQ(launch_cmd, found_launch_cmd); + + // There should be no installed version in the registry. + EXPECT_TRUE(product->GetInstalledVersion() == NULL); + + // Let's pretend chrome is installed. + RegKey version_key(root, distribution->GetVersionKey().c_str(), + KEY_ALL_ACCESS); + ASSERT_TRUE(version_key.Valid()); + + const wchar_t kCurrentVersion[] = L"1.2.3.4"; + scoped_ptr<Version> current_version( + Version::GetVersionFromString(kCurrentVersion)); + version_key.WriteValue(google_update::kRegVersionField, + current_version->GetString().c_str()); + + scoped_ptr<Version> installed(product->GetInstalledVersion()); + EXPECT_TRUE(installed.get() != NULL); + if (installed.get()) { + EXPECT_TRUE(installed->IsEqual(*current_version.get())); + } + } +} + +TEST_F(ProductTest, LaunchChrome) { + // TODO(tommi): Test Product::LaunchChrome and + // Product::LaunchChromeAndWait. + LOG(ERROR) << "Test not implemented."; +} + +// Overrides ChromeFrameDistribution for the sole purpose of returning +// the Chrome (not Chrome Frame) installation path. +class FakeChromeFrameDistribution : public ChromeFrameDistribution { + public: + virtual std::wstring GetInstallSubDir() { + return BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER)->GetInstallSubDir(); + } +}; + +TEST_F(ProductTest, ProductInstallsBasic) { + const bool system_level = true; + ProductPackageMapping installs(system_level); + EXPECT_EQ(system_level, installs.system_level()); + EXPECT_EQ(0U, installs.packages().size()); + EXPECT_EQ(0U, installs.products().size()); + + installs.AddDistribution(BrowserDistribution::CHROME_BROWSER); + FakeChromeFrameDistribution fake_chrome_frame; + installs.AddDistribution(&fake_chrome_frame); + EXPECT_EQ(2U, installs.products().size()); + // Since our fake Chrome Frame distribution class is reporting the same + // installation directory as Chrome, we should have only one package object. + EXPECT_EQ(1U, installs.packages().size()); +} diff --git a/chrome/installer/util/product_unittest.h b/chrome/installer/util/product_unittest.h new file mode 100644 index 0000000..31f630c --- /dev/null +++ b/chrome/installer/util/product_unittest.h @@ -0,0 +1,49 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_INSTALLER_UTIL_PRODUCT_UNITTEST_H_ +#define CHROME_INSTALLER_UTIL_PRODUCT_UNITTEST_H_ +#pragma once + +#include <windows.h> + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/scoped_temp_dir.h" +#include "base/win/registry.h" +#include "testing/gtest/include/gtest/gtest.h" + +class TestWithTempDir : public testing::Test { + protected: + virtual void SetUp(); + virtual void TearDown(); + + ScopedTempDir test_dir_; +}; + +class TestWithTempDirAndDeleteTempOverrideKeys : public TestWithTempDir { + protected: + virtual void SetUp(); + virtual void TearDown(); +}; + +// TODO(tommi): This is "borrowed" from Chrome Frame test code. It should be +// moved to some common test utility file. +class TempRegKeyOverride { + public: + static const wchar_t kTempTestKeyPath[]; + + TempRegKeyOverride(HKEY override, const wchar_t* temp_name); + ~TempRegKeyOverride(); + + static void DeleteAllTempKeys(); + + protected: + HKEY override_; + base::win::RegKey temp_key_; + std::wstring temp_name_; +}; + +#endif // CHROME_INSTALLER_UTIL_PRODUCT_UNITTEST_H_ diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc index b82bbf8..734c90d9 100644 --- a/chrome/installer/util/shell_util.cc +++ b/chrome/installer/util/shell_util.cc @@ -47,10 +47,11 @@ class RegistryEntry { public: // This method returns a list of all the registry entries that // are needed to register Chromium ProgIds. - static bool GetProgIdEntries(const std::wstring& chrome_exe, + static bool GetProgIdEntries(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& suffix, std::list<RegistryEntry*>* entries) { - std::wstring icon_path = ShellUtil::GetChromeIcon(chrome_exe); + std::wstring icon_path = ShellUtil::GetChromeIcon(dist, chrome_exe); std::wstring open_cmd = ShellUtil::GetChromeShellOpenCmd(chrome_exe); // File association ProgId @@ -71,13 +72,13 @@ class RegistryEntry { // This method returns a list of all the system level registry entries that // are needed to register Chromium on the machine. - static bool GetSystemEntries(const std::wstring& chrome_exe, + static bool GetSystemEntries(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& suffix, std::list<RegistryEntry*>* entries) { - std::wstring icon_path = ShellUtil::GetChromeIcon(chrome_exe); + std::wstring icon_path = ShellUtil::GetChromeIcon(dist, chrome_exe); std::wstring quoted_exe_path = L"\"" + chrome_exe + L"\""; - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); std::wstring app_name = dist->GetApplicationName() + suffix; std::wstring start_menu_entry(ShellUtil::kRegStartMenuInternet); start_menu_entry.append(L"\\" + app_name); @@ -137,7 +138,8 @@ class RegistryEntry { // This method returns a list of all the user level registry entries that // are needed to make Chromium default browser. - static bool GetUserEntries(const std::wstring& chrome_exe, + static bool GetUserEntries(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& suffix, std::list<RegistryEntry*>* entries) { // File extension associations. @@ -151,7 +153,7 @@ class RegistryEntry { // Protocols associations. std::wstring chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); - std::wstring chrome_icon = ShellUtil::GetChromeIcon(chrome_exe); + std::wstring chrome_icon = ShellUtil::GetChromeIcon(dist, chrome_exe); for (int i = 0; ShellUtil::kProtocolAssociations[i] != NULL; i++) { std::wstring url_key(ShellUtil::kRegClasses); file_util::AppendToPath(&url_key, ShellUtil::kProtocolAssociations[i]); @@ -175,7 +177,6 @@ class RegistryEntry { // start->Internet shortcut. std::wstring start_menu(ShellUtil::kRegStartMenuInternet); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); std::wstring app_name = dist->GetApplicationName() + suffix; entries->push_front(new RegistryEntry(start_menu, app_name)); return true; @@ -279,13 +280,14 @@ bool AddRegistryEntries(HKEY root, const std::list<RegistryEntry*>& entries) { // This method checks if Chrome is already registered on the local machine. // It gets all the required registry entries for Chrome and then checks if // they exist in HKLM. Returns true if all the entries exist, otherwise false. -bool IsChromeRegistered(const std::wstring& chrome_exe, +bool IsChromeRegistered(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& suffix) { bool registered = true; std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); - RegistryEntry::GetProgIdEntries(chrome_exe, suffix, &entries); - RegistryEntry::GetSystemEntries(chrome_exe, suffix, &entries); + RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); + RegistryEntry::GetSystemEntries(dist, chrome_exe, suffix, &entries); for (std::list<RegistryEntry*>::const_iterator itr = entries.begin(); itr != entries.end() && registered; ++itr) { // We do not need registered = registered && ... since the loop condition @@ -299,13 +301,13 @@ bool IsChromeRegistered(const std::wstring& chrome_exe, // That will show the user the standard Vista elevation prompt. If the user // accepts it the new process will make the necessary changes and return SUCCESS // that we capture and return. -bool ElevateAndRegisterChrome(const std::wstring& chrome_exe, +bool ElevateAndRegisterChrome(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& suffix) { FilePath exe_path = FilePath::FromWStringHack(chrome_exe).DirName() .Append(installer_util::kSetupExe); if (!file_util::PathExists(exe_path)) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); HKEY reg_root = InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; RegKey key(reg_root, dist->GetUninstallRegPath().c_str(), KEY_READ); @@ -348,9 +350,9 @@ bool ElevateAndRegisterChrome(const std::wstring& chrome_exe, // - Finally to handle the default install path (C:\Document and Settings\ // <user>\Local Settings\Application Data\Chromium\Application) the value // of the above key should differ from |chrome_exe| only in user name. -bool AnotherUserHasDefaultBrowser(const std::wstring& chrome_exe) { +bool AnotherUserHasDefaultBrowser(BrowserDistribution* dist, + const std::wstring& chrome_exe) { std::wstring reg_key(ShellUtil::kRegStartMenuInternet); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); reg_key.append(L"\\" + dist->GetApplicationName() + ShellUtil::kRegShellOpen); RegKey key(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_READ); std::wstring registry_chrome_exe; @@ -424,12 +426,13 @@ const wchar_t* ShellUtil::kProtocolAssociations[] = {L"ftp", L"http", L"https", NULL}; const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol"; -bool ShellUtil::AdminNeededForRegistryCleanup(const std::wstring& suffix) { +bool ShellUtil::AdminNeededForRegistryCleanup(BrowserDistribution* dist, + const std::wstring& suffix) { bool cleanup_needed = false; std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); - RegistryEntry::GetProgIdEntries(L"chrome.exe", suffix, &entries); - RegistryEntry::GetSystemEntries(L"chrome.exe", suffix, &entries); + RegistryEntry::GetProgIdEntries(dist, L"chrome.exe", suffix, &entries); + RegistryEntry::GetSystemEntries(dist, L"chrome.exe", suffix, &entries); for (std::list<RegistryEntry*>::const_iterator itr = entries.begin(); itr != entries.end() && !cleanup_needed; ++itr) { cleanup_needed = (*itr)->NameExistsInHKLM(); @@ -437,12 +440,13 @@ bool ShellUtil::AdminNeededForRegistryCleanup(const std::wstring& suffix) { return cleanup_needed; } -bool ShellUtil::CreateChromeDesktopShortcut(const std::wstring& chrome_exe, +bool ShellUtil::CreateChromeDesktopShortcut(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& description, int shell_change, bool alternate, bool create_new) { std::wstring shortcut_name; - if (!ShellUtil::GetChromeShortcutName(&shortcut_name, alternate)) + if (!ShellUtil::GetChromeShortcutName(dist, &shortcut_name, alternate)) return false; bool ret = true; @@ -450,7 +454,7 @@ bool ShellUtil::CreateChromeDesktopShortcut(const std::wstring& chrome_exe, std::wstring shortcut_path; if (ShellUtil::GetDesktopPath(false, &shortcut_path)) { file_util::AppendToPath(&shortcut_path, shortcut_name); - ret = ShellUtil::UpdateChromeShortcut(chrome_exe, shortcut_path, + ret = ShellUtil::UpdateChromeShortcut(dist, chrome_exe, shortcut_path, description, create_new); } else { ret = false; @@ -462,7 +466,7 @@ bool ShellUtil::CreateChromeDesktopShortcut(const std::wstring& chrome_exe, file_util::AppendToPath(&shortcut_path, shortcut_name); // Note we need to call the create operation and then AND the result // with the create operation of user level shortcut. - ret = ShellUtil::UpdateChromeShortcut(chrome_exe, shortcut_path, + ret = ShellUtil::UpdateChromeShortcut(dist, chrome_exe, shortcut_path, description, create_new) && ret; } else { ret = false; @@ -471,11 +475,12 @@ bool ShellUtil::CreateChromeDesktopShortcut(const std::wstring& chrome_exe, return ret; } -bool ShellUtil::CreateChromeQuickLaunchShortcut(const std::wstring& chrome_exe, +bool ShellUtil::CreateChromeQuickLaunchShortcut(BrowserDistribution* dist, + const std::wstring& chrome_exe, int shell_change, bool create_new) { std::wstring shortcut_name; - if (!ShellUtil::GetChromeShortcutName(&shortcut_name, false)) + if (!ShellUtil::GetChromeShortcutName(dist, &shortcut_name, false)) return false; bool ret = true; @@ -484,7 +489,7 @@ bool ShellUtil::CreateChromeQuickLaunchShortcut(const std::wstring& chrome_exe, std::wstring user_ql_path; if (ShellUtil::GetQuickLaunchPath(false, &user_ql_path)) { file_util::AppendToPath(&user_ql_path, shortcut_name); - ret = ShellUtil::UpdateChromeShortcut(chrome_exe, user_ql_path, + ret = ShellUtil::UpdateChromeShortcut(dist, chrome_exe, user_ql_path, L"", create_new); } else { ret = false; @@ -497,7 +502,7 @@ bool ShellUtil::CreateChromeQuickLaunchShortcut(const std::wstring& chrome_exe, std::wstring default_ql_path; if (ShellUtil::GetQuickLaunchPath(true, &default_ql_path)) { file_util::AppendToPath(&default_ql_path, shortcut_name); - ret = ShellUtil::UpdateChromeShortcut(chrome_exe, default_ql_path, + ret = ShellUtil::UpdateChromeShortcut(dist, chrome_exe, default_ql_path, L"", create_new) && ret; } else { ret = false; @@ -507,8 +512,8 @@ bool ShellUtil::CreateChromeQuickLaunchShortcut(const std::wstring& chrome_exe, return ret; } -std::wstring ShellUtil::GetChromeIcon(const std::wstring& chrome_exe) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); +std::wstring ShellUtil::GetChromeIcon(BrowserDistribution* dist, + const std::wstring& chrome_exe) { std::wstring chrome_icon(chrome_exe); chrome_icon.append(L","); chrome_icon.append(base::IntToString16(dist->GetIconIndex())); @@ -519,8 +524,8 @@ std::wstring ShellUtil::GetChromeShellOpenCmd(const std::wstring& chrome_exe) { return L"\"" + chrome_exe + L"\" -- \"%1\""; } -bool ShellUtil::GetChromeShortcutName(std::wstring* shortcut, bool alternate) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); +bool ShellUtil::GetChromeShortcutName(BrowserDistribution* dist, + std::wstring* shortcut, bool alternate) { shortcut->assign(alternate ? dist->GetAlternateApplicationName() : dist->GetAppShortCutName()); shortcut->append(L".lnk"); @@ -567,7 +572,8 @@ bool ShellUtil::GetQuickLaunchPath(bool system_level, std::wstring* path) { return true; } -void ShellUtil::GetRegisteredBrowsers(std::map<std::wstring, +void ShellUtil::GetRegisteredBrowsers(BrowserDistribution* dist, + std::map<std::wstring, std::wstring>* browsers) { std::wstring base_key(ShellUtil::kRegStartMenuInternet); HKEY root = HKEY_LOCAL_MACHINE; @@ -587,14 +593,14 @@ void ShellUtil::GetRegisteredBrowsers(std::map<std::wstring, if (!install_info.Valid() || !install_info.ReadValue(L"ReinstallCommand", &command)) continue; - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); if (!name.empty() && !command.empty() && name.find(dist->GetApplicationName()) == std::wstring::npos) (*browsers)[name] = command; } } -bool ShellUtil::GetUserSpecificDefaultBrowserSuffix(std::wstring* entry) { +bool ShellUtil::GetUserSpecificDefaultBrowserSuffix(BrowserDistribution* dist, + std::wstring* entry) { wchar_t user_name[256]; DWORD size = _countof(user_name); if (::GetUserName(user_name, &size) == 0) @@ -603,19 +609,19 @@ bool ShellUtil::GetUserSpecificDefaultBrowserSuffix(std::wstring* entry) { entry->append(user_name); std::wstring start_menu_entry(ShellUtil::kRegStartMenuInternet); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); start_menu_entry.append(L"\\" + dist->GetApplicationName() + *entry); RegKey key(HKEY_LOCAL_MACHINE, start_menu_entry.c_str(), KEY_READ); return key.Valid(); } -bool ShellUtil::MakeChromeDefault(int shell_change, +bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, + int shell_change, const std::wstring& chrome_exe, bool elevate_if_not_admin) { - if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) + if (!dist->CanSetAsDefault()) return false; - ShellUtil::RegisterChromeBrowser(chrome_exe, L"", elevate_if_not_admin); + ShellUtil::RegisterChromeBrowser(dist, chrome_exe, L"", elevate_if_not_admin); bool ret = true; // First use the new "recommended" way on Vista to make Chrome default @@ -627,10 +633,9 @@ bool ShellUtil::MakeChromeDefault(int shell_change, NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration), (void**)&pAAR); if (SUCCEEDED(hr)) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); std::wstring app_name = dist->GetApplicationName(); std::wstring suffix; - if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(&suffix)) + if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) app_name += suffix; hr = pAAR->SetAppAsDefaultAll(app_name.c_str()); @@ -650,9 +655,9 @@ bool ShellUtil::MakeChromeDefault(int shell_change, std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); std::wstring suffix; - if (!GetUserSpecificDefaultBrowserSuffix(&suffix)) + if (!GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) suffix = L""; - RegistryEntry::GetUserEntries(chrome_exe, suffix, &entries); + RegistryEntry::GetUserEntries(dist, chrome_exe, suffix, &entries); // Change the default browser for current user. if ((shell_change & ShellUtil::CURRENT_USER) && !AddRegistryEntries(HKEY_CURRENT_USER, entries)) @@ -669,10 +674,11 @@ bool ShellUtil::MakeChromeDefault(int shell_change, return ret; } -bool ShellUtil::RegisterChromeBrowser(const std::wstring& chrome_exe, +bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& unique_suffix, bool elevate_if_not_admin) { - if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) + if (!dist->CanSetAsDefault()) return false; // First figure out we need to append a suffix to the registry entries to @@ -681,41 +687,42 @@ bool ShellUtil::RegisterChromeBrowser(const std::wstring& chrome_exe, if (!unique_suffix.empty()) { suffix = unique_suffix; } else if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) && - !GetUserSpecificDefaultBrowserSuffix(&suffix) && - !AnotherUserHasDefaultBrowser(chrome_exe)) { + !GetUserSpecificDefaultBrowserSuffix(dist, &suffix) && + !AnotherUserHasDefaultBrowser(dist, chrome_exe)) { suffix = L""; } // Check if Chromium is already registered with this suffix. - if (IsChromeRegistered(chrome_exe, suffix)) + if (IsChromeRegistered(dist, chrome_exe, suffix)) return true; // If user is an admin try to register and return the status. if (IsUserAnAdmin()) { std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); - RegistryEntry::GetProgIdEntries(chrome_exe, suffix, &entries); - RegistryEntry::GetSystemEntries(chrome_exe, suffix, &entries); + RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); + RegistryEntry::GetSystemEntries(dist, chrome_exe, suffix, &entries); return AddRegistryEntries(HKEY_LOCAL_MACHINE, entries); } // If user is not an admin and OS is Vista, try to elevate and register. if (elevate_if_not_admin && base::win::GetVersion() >= base::win::VERSION_VISTA && - ElevateAndRegisterChrome(chrome_exe, suffix)) + ElevateAndRegisterChrome(dist, chrome_exe, suffix)) return true; // If we got to this point then all we can do is create ProgIds under HKCU // on XP as well as Vista. std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*>> entries_deleter(&entries); - RegistryEntry::GetProgIdEntries(chrome_exe, L"", &entries); + RegistryEntry::GetProgIdEntries(dist, chrome_exe, L"", &entries); return AddRegistryEntries(HKEY_CURRENT_USER, entries); } -bool ShellUtil::RemoveChromeDesktopShortcut(int shell_change, bool alternate) { +bool ShellUtil::RemoveChromeDesktopShortcut(BrowserDistribution* dist, + int shell_change, bool alternate) { std::wstring shortcut_name; - if (!ShellUtil::GetChromeShortcutName(&shortcut_name, alternate)) + if (!ShellUtil::GetChromeShortcutName(dist, &shortcut_name, alternate)) return false; bool ret = true; @@ -741,9 +748,10 @@ bool ShellUtil::RemoveChromeDesktopShortcut(int shell_change, bool alternate) { return ret; } -bool ShellUtil::RemoveChromeQuickLaunchShortcut(int shell_change) { +bool ShellUtil::RemoveChromeQuickLaunchShortcut(BrowserDistribution* dist, + int shell_change) { std::wstring shortcut_name; - if (!ShellUtil::GetChromeShortcutName(&shortcut_name, false)) + if (!ShellUtil::GetChromeShortcutName(dist, &shortcut_name, false)) return false; bool ret = true; @@ -772,11 +780,11 @@ bool ShellUtil::RemoveChromeQuickLaunchShortcut(int shell_change) { return ret; } -bool ShellUtil::UpdateChromeShortcut(const std::wstring& chrome_exe, +bool ShellUtil::UpdateChromeShortcut(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& shortcut, const std::wstring& description, bool create_new) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); std::wstring chrome_path = file_util::GetDirectoryFromPath(chrome_exe); FilePath prefs_path(chrome_path); diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h index ad72f64..5df73c6 100644 --- a/chrome/installer/util/shell_util.h +++ b/chrome/installer/util/shell_util.h @@ -17,6 +17,8 @@ #include "base/basictypes.h" #include "chrome/installer/util/work_item_list.h" +class BrowserDistribution; + // This is a utility class that provides common shell integration methods // that can be used by installer as well as Chrome. class ShellUtil { @@ -74,7 +76,8 @@ class ShellUtil { // Checks if we need Admin rights for registry cleanup by checking if any // entry exists in HKLM. - static bool AdminNeededForRegistryCleanup(const std::wstring& suffix); + static bool AdminNeededForRegistryCleanup(BrowserDistribution* dist, + const std::wstring& suffix); // Create Chrome shortcut on Desktop // If shell_change is CURRENT_USER, the shortcut is created in the @@ -84,7 +87,8 @@ class ShellUtil { // If alternate is true, an alternate text for the shortcut is used. // create_new: If false, will only update the shortcut. If true, the function // will create a new shortcut if it doesn't exist already. - static bool CreateChromeDesktopShortcut(const std::wstring& chrome_exe, + static bool CreateChromeDesktopShortcut(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& description, int shell_change, bool alternate, bool create_new); @@ -98,7 +102,8 @@ class ShellUtil { // system. // create_new: If false, will only update the shortcut. If true, the function // will create a new shortcut if it doesn't exist already. - static bool CreateChromeQuickLaunchShortcut(const std::wstring& chrome_exe, + static bool CreateChromeQuickLaunchShortcut(BrowserDistribution* dist, + const std::wstring& chrome_exe, int shell_change, bool create_new); @@ -106,7 +111,8 @@ class ShellUtil { // chrome.exe path passed in as input, to generate the full path for // Chrome icon that can be used as value for Windows registry keys. // |chrome_exe| full path to chrome.exe. - static std::wstring GetChromeIcon(const std::wstring& chrome_exe); + static std::wstring GetChromeIcon(BrowserDistribution* dist, + const std::wstring& chrome_exe); // This method returns the command to open URLs/files using chrome. Typically // this command is written to the registry under shell\open\command key. @@ -116,7 +122,8 @@ class ShellUtil { // Returns the localized name of Chrome shortcut. If |alternate| is true // it returns a second localized text that is better suited for certain // scenarios. - static bool GetChromeShortcutName(std::wstring* shortcut, bool alternate); + static bool GetChromeShortcutName(BrowserDistribution* dist, + std::wstring* shortcut, bool alternate); // Gets the desktop path for the current user or all users (if system_level // is true) and returns it in 'path' argument. Return true if successful, @@ -131,8 +138,9 @@ class ShellUtil { static bool GetQuickLaunchPath(bool system_level, std::wstring* path); // Gets a mapping of all registered browser (on local machine) names and - // thier reinstall command (which usually sets browser as default). - static void GetRegisteredBrowsers(std::map<std::wstring, + // their reinstall command (which usually sets browser as default). + static void GetRegisteredBrowsers(BrowserDistribution* dist, + std::map<std::wstring, std::wstring>* browsers); // This function gets a suffix (user's login name) that can be added @@ -142,7 +150,8 @@ class ShellUtil { // This suffix value is assigned to |entry|. The function also checks for // existence of Default Browser registry key with this suffix and // returns true if it exists. In all other cases it returns false. - static bool GetUserSpecificDefaultBrowserSuffix(std::wstring* entry); + static bool GetUserSpecificDefaultBrowserSuffix(BrowserDistribution* dist, + std::wstring* entry); // Make Chrome default browser. // shell_change: Defined whether to register as default browser at system @@ -151,7 +160,8 @@ class ShellUtil { // chrome_exe: The chrome.exe path to register as default browser. // elevate_if_not_admin: On Vista if user is not admin, try to elevate for // Chrome registration. - static bool MakeChromeDefault(int shell_change, + static bool MakeChromeDefault(BrowserDistribution* dist, + int shell_change, const std::wstring& chrome_exe, bool elevate_if_not_admin); @@ -176,7 +186,8 @@ class ShellUtil { // to default browser entries names that it creates in the registry. // |elevate_if_not_admin| if true will make this method try alternate methods // as described above. - static bool RegisterChromeBrowser(const std::wstring& chrome_exe, + static bool RegisterChromeBrowser(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& unique_suffix, bool elevate_if_not_admin); @@ -187,21 +198,24 @@ class ShellUtil { // Desktop folder of "All Users" profile. // If alternate is true, the shortcut with the alternate name is removed. See // CreateChromeDesktopShortcut() for more information. - static bool RemoveChromeDesktopShortcut(int shell_change, bool alternate); + static bool RemoveChromeDesktopShortcut(BrowserDistribution* dist, + int shell_change, bool alternate); // Remove Chrome shortcut from Quick Launch Bar. // If shell_change is CURRENT_USER, the shortcut is removed from // the Quick Launch folder of current user's profile. // If shell_change is SYSTEM_LEVEL, the shortcut is removed from // the Quick Launch folder of "Default User" profile. - static bool RemoveChromeQuickLaunchShortcut(int shell_change); + static bool RemoveChromeQuickLaunchShortcut(BrowserDistribution* dist, + int shell_change); // Updates shortcut (or creates a new shortcut) at destination given by // shortcut to a target given by chrome_exe. The arguments is left NULL // for the target and icon is set as icon at index 0 from exe. // If create_new is set to true, the function will create a new shortcut if // if doesn't exist. - static bool UpdateChromeShortcut(const std::wstring& chrome_exe, + static bool UpdateChromeShortcut(BrowserDistribution* dist, + const std::wstring& chrome_exe, const std::wstring& shortcut, const std::wstring& description, bool create_new); diff --git a/chrome/installer/util/shell_util_unittest.cc b/chrome/installer/util/shell_util_unittest.cc index ae74556..a5d917e 100644 --- a/chrome/installer/util/shell_util_unittest.cc +++ b/chrome/installer/util/shell_util_unittest.cc @@ -11,6 +11,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/scoped_comptr_win.h" +#include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/shell_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -108,6 +109,8 @@ class ShellUtilTest : public testing::Test { // Test that we can open archives successfully. TEST_F(ShellUtilTest, UpdateChromeShortcutTest) { + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + ASSERT_TRUE(dist != NULL); // Create an executable in test path by copying ourself to it. wchar_t exe_full_path_str[MAX_PATH]; EXPECT_FALSE(::GetModuleFileName(NULL, exe_full_path_str, MAX_PATH) == 0); @@ -118,7 +121,7 @@ TEST_F(ShellUtilTest, UpdateChromeShortcutTest) { FilePath shortcut_path = test_dir_.AppendASCII("shortcut.lnk"); const std::wstring description(L"dummy description"); - EXPECT_TRUE(ShellUtil::UpdateChromeShortcut(exe_path.value(), + EXPECT_TRUE(ShellUtil::UpdateChromeShortcut(dist, exe_path.value(), shortcut_path.value(), description, true)); EXPECT_TRUE(VerifyChromeShortcut(exe_path.value(), @@ -139,7 +142,7 @@ TEST_F(ShellUtilTest, UpdateChromeShortcutTest) { "}"; file.close(); ASSERT_TRUE(file_util::Delete(shortcut_path, false)); - EXPECT_TRUE(ShellUtil::UpdateChromeShortcut(exe_path.value(), + EXPECT_TRUE(ShellUtil::UpdateChromeShortcut(dist, exe_path.value(), shortcut_path.value(), description, true)); EXPECT_TRUE(VerifyChromeShortcut(exe_path.value(), @@ -149,7 +152,7 @@ TEST_F(ShellUtilTest, UpdateChromeShortcutTest) { // Now change only description to update shortcut and make sure icon index // doesn't change. const std::wstring description2(L"dummy description 2"); - EXPECT_TRUE(ShellUtil::UpdateChromeShortcut(exe_path.value(), + EXPECT_TRUE(ShellUtil::UpdateChromeShortcut(dist, exe_path.value(), shortcut_path.value(), description2, false)); EXPECT_TRUE(VerifyChromeShortcut(exe_path.value(), diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc index 66a0636..3b1d651 100644 --- a/chrome/installer/util/util_constants.cc +++ b/chrome/installer/util/util_constants.cc @@ -153,4 +153,10 @@ const wchar_t kUninstallArgumentsField[] = L"UninstallArguments"; const wchar_t kUninstallDisplayNameField[] = L"DisplayName"; const char kUninstallMetricsName[] = "uninstall_metrics"; const wchar_t kUninstallInstallationDate[] = L"installation_date"; +const wchar_t kInstallerResult[] = L"InstallerResult"; +const wchar_t kInstallerError[] = L"InstallerError"; +const wchar_t kInstallerResultUIString[] = L"InstallerResultUIString"; +const wchar_t kInstallerSuccessLaunchCmdLine[] = + L"InstallerSuccessLaunchCmdLine"; + } // namespace installer_util diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index 7cc4042..3cd21b0 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -110,6 +110,12 @@ extern const wchar_t kUninstallInstallationDate[]; extern const char kUninstallMetricsName[]; extern const wchar_t kUninstallStringField[]; +// Used by ProductInstall::WriteInstallerResult. +extern const wchar_t kInstallerResult[]; +extern const wchar_t kInstallerError[]; +extern const wchar_t kInstallerResultUIString[]; +extern const wchar_t kInstallerSuccessLaunchCmdLine[]; + } // namespace installer_util #endif // CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H_ diff --git a/chrome/installer/util/version.h b/chrome/installer/util/version.h index 72e8271..e237260 100644 --- a/chrome/installer/util/version.h +++ b/chrome/installer/util/version.h @@ -11,6 +11,7 @@ namespace installer { +// TODO(tommi): We should be using the Version class from base. class Version { public: virtual ~Version(); @@ -24,6 +25,10 @@ class Version { return version_str_; } + bool IsEqual(const Version& other) const { + return version_str_ == other.GetString(); + } + // Assume that the version string is specified by four integers separated // by character '.'. Return NULL if string is not of this format. // Caller is responsible for freeing the Version object once done. diff --git a/chrome_frame/test/test_with_web_server.cc b/chrome_frame/test/test_with_web_server.cc index a030a76..8d4e3ba 100644 --- a/chrome_frame/test/test_with_web_server.cc +++ b/chrome_frame/test/test_with_web_server.cc @@ -8,9 +8,11 @@ #include "base/file_version_info.h" #include "base/path_service.h" #include "base/stringprintf.h" +#include "base/test/test_timeouts.h" #include "base/utf_string_conversions.h" #include "base/win/windows_version.h" #include "chrome/common/chrome_switches.h" +#include "chrome/installer/util/product.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/helper.h" #include "chrome_frame/html_utils.h" @@ -26,8 +28,6 @@ using testing::_; using testing::StrCaseEq; const wchar_t kDocRoot[] = L"chrome_frame\\test\\data"; -const int kLongWaitTimeout = 15 * 1000; -const int kShortWaitTimeout = 5 * 1000; namespace { @@ -92,7 +92,7 @@ void ChromeFrameTestWithWebServer::SetUp() { // Make sure our playground is clean before we start. CloseAllBrowsers(); - // Make sure that we are not accidently enabling gcf protocol. + // Make sure that we are not accidentally enabling gcf protocol. SetConfigBool(kAllowUnsafeURLs, false); FilePath chrome_frame_source_path; @@ -220,7 +220,7 @@ void ChromeFrameTestWithWebServer::SimpleBrowserTestExpectedResult( ASSERT_TRUE(LaunchBrowser(browser, page)); server_mock_.ExpectAndHandlePostedResult(CFInvocation(CFInvocation::NONE), kPostedResultSubstring); - WaitForTestToComplete(kLongWaitTimeout); + WaitForTestToComplete(TestTimeouts::action_max_timeout_ms()); ASSERT_EQ(result, server_mock_.posted_result()); } @@ -237,7 +237,7 @@ void ChromeFrameTestWithWebServer::OptionalBrowserTest(BrowserKind browser, } else { server_mock_.ExpectAndHandlePostedResult(CFInvocation(CFInvocation::NONE), kPostedResultSubstring); - WaitForTestToComplete(kLongWaitTimeout); + WaitForTestToComplete(TestTimeouts::action_max_timeout_ms()); ASSERT_EQ("OK", server_mock_.posted_result()); } } @@ -259,15 +259,17 @@ void ChromeFrameTestWithWebServer::VersionTest(BrowserKind browser, // If we can't find the Chrome Frame DLL in the src tree, we turn to // the directory where chrome is installed. if (!version_info) { - installer::Version* ver_system = InstallUtil::GetChromeVersion(true); - installer::Version* ver_user = InstallUtil::GetChromeVersion(false); - ASSERT_TRUE(ver_system || ver_user); - - bool system_install = ver_system ? true : false; - FilePath cf_dll_path = FilePath::FromWStringHack( - installer::GetChromeInstallPath(system_install)); + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + scoped_ptr<installer::Version> ver_system( + InstallUtil::GetChromeVersion(dist, true)); + scoped_ptr<installer::Version> ver_user( + InstallUtil::GetChromeVersion(dist, false)); + ASSERT_TRUE(ver_system.get() || ver_user.get()); + + bool system_install = ver_system.get() ? true : false; + FilePath cf_dll_path(installer::GetChromeInstallPath(system_install, dist)); cf_dll_path = cf_dll_path.Append( - ver_system ? ver_system->GetString() : ver_user->GetString()); + ver_system.get() ? ver_system->GetString() : ver_user->GetString()); cf_dll_path = cf_dll_path.Append(kChromeFrameDllName); version_info = FileVersionInfo::CreateFileVersionInfo(cf_dll_path); if (version_info) @@ -281,7 +283,7 @@ void ChromeFrameTestWithWebServer::VersionTest(BrowserKind browser, EXPECT_TRUE(LaunchBrowser(browser, page)); server_mock_.ExpectAndHandlePostedResult(CFInvocation(CFInvocation::NONE), kPostedResultSubstring); - WaitForTestToComplete(kLongWaitTimeout); + WaitForTestToComplete(TestTimeouts::action_max_timeout_ms()); ASSERT_EQ(version, UTF8ToWide(server_mock_.posted_result())); } @@ -294,7 +296,7 @@ void ChromeFrameTestWithWebServer::SessionIdTest(BrowserKind browser, server_mock_.set_expected_result(expected_result); server_mock_.ExpectAndHandlePostedResult(CFInvocation(CFInvocation::NONE), kPostedResultSubstring); - WaitForTestToComplete(kLongWaitTimeout); + WaitForTestToComplete(TestTimeouts::action_max_timeout_ms()); ASSERT_EQ(expected_result, server_mock_.posted_result()); } @@ -489,7 +491,7 @@ TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeOpera_ObjectFocus) { if (!LaunchBrowser(OPERA, kNavigateSimpleObjectFocus)) { LOG(ERROR) << "Failed to launch browser " << ToString(OPERA); } else { - ASSERT_TRUE(WaitForOnLoad(kLongWaitTimeout)); + ASSERT_TRUE(WaitForOnLoad(TestTimeouts::action_max_timeout_ms())); server_mock_.ExpectAndHandlePostedResult(CFInvocation(CFInvocation::NONE), kPostedResultSubstring); BringBrowserToTop(); |