diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-12 21:46:57 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-12 21:46:57 +0000 |
commit | bc38c254e32398b444dbccd391c2ae3d6f182001 (patch) | |
tree | a1e4ba91782a44147002ac30d6a57c0c0b535830 | |
parent | 75e20841bd18a6053aa41b754ddfcaa1445435d8 (diff) | |
download | chromium_src-bc38c254e32398b444dbccd391c2ae3d6f182001.zip chromium_src-bc38c254e32398b444dbccd391c2ae3d6f182001.tar.gz chromium_src-bc38c254e32398b444dbccd391c2ae3d6f182001.tar.bz2 |
first-run: Refactor Upgrade class.
- Split Upgrade class implementation per platform files outside of first_run implementations.
- Move TryChromeDialog class into its own header file.
- Rename TryChromeDialog to TryChromeDialogView.
BUG=None
TEST=None
R=jhawkins@chromium.org
Review URL: http://codereview.chromium.org/6824029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81313 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/chrome_exe_main_gtk.cc | 10 | ||||
-rw-r--r-- | chrome/browser/browser_main.cc | 7 | ||||
-rw-r--r-- | chrome/browser/browser_main_win.cc | 1 | ||||
-rw-r--r-- | chrome/browser/browser_process_impl.cc | 2 | ||||
-rw-r--r-- | chrome/browser/browser_shutdown.cc | 2 | ||||
-rw-r--r-- | chrome/browser/first_run/first_run.cc | 15 | ||||
-rw-r--r-- | chrome/browser/first_run/first_run.h | 77 | ||||
-rw-r--r-- | chrome/browser/first_run/first_run_gtk.cc | 37 | ||||
-rw-r--r-- | chrome/browser/first_run/first_run_win.cc | 406 | ||||
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.cc | 249 | ||||
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.h | 95 | ||||
-rw-r--r-- | chrome/browser/first_run/upgrade.cc | 21 | ||||
-rw-r--r-- | chrome/browser/first_run/upgrade.h | 96 | ||||
-rw-r--r-- | chrome/browser/first_run/upgrade_gtk.cc | 55 | ||||
-rw-r--r-- | chrome/browser/first_run/upgrade_win.cc | 157 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 20 |
16 files changed, 704 insertions, 546 deletions
diff --git a/chrome/app/chrome_exe_main_gtk.cc b/chrome/app/chrome_exe_main_gtk.cc index 35764af..bf9589d 100644 --- a/chrome/app/chrome_exe_main_gtk.cc +++ b/chrome/app/chrome_exe_main_gtk.cc @@ -1,10 +1,12 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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/process_util.h" +#include "build/build_config.h" + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#include "chrome/browser/first_run/first_run.h" +#include "chrome/browser/first_run/upgrade.h" #endif // The entry point for all invocations of Chromium, browser and renderer. On @@ -20,10 +22,8 @@ extern "C" { int ChromeMain(int argc, const char** argv); #if defined(OS_LINUX) && defined(USE_TCMALLOC) - int tc_set_new_mode(int mode); - -#endif // defined(OS_LINUX) && defined(USE_TCMALLOC) +#endif } int main(int argc, const char** argv) { diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index b7b47b6..40696db 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -41,6 +41,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extensions_startup.h" #include "chrome/browser/first_run/first_run.h" +#include "chrome/browser/first_run/upgrade.h" #include "chrome/browser/gpu_data_manager.h" #include "chrome/browser/jankometer.h" #include "chrome/browser/metrics/histogram_synchronizer.h" @@ -1230,15 +1231,15 @@ int BrowserMain(const MainFunctionParams& parameters) { // It seems that we don't need to run the experiment since chrome // in the same profile is already running. VLOG(1) << "Retention experiment not required"; - return Upgrade::TD_NOT_NOW; + return Upgrade::NOT_NOW; } int try_chrome_int; base::StringToInt(try_chrome, &try_chrome_int); Upgrade::TryResult answer = Upgrade::ShowTryChromeDialog(try_chrome_int, &process_singleton); - if (answer == Upgrade::TD_NOT_NOW) + if (answer == Upgrade::NOT_NOW) return ResultCodes::NORMAL_EXIT_CANCEL; - if (answer == Upgrade::TD_UNINSTALL_CHROME) + if (answer == Upgrade::UNINSTALL_CHROME) return ResultCodes::NORMAL_EXIT_EXP2; #else // We don't support retention experiments on Mac or Linux. diff --git a/chrome/browser/browser_main_win.cc b/chrome/browser/browser_main_win.cc index e36b9f6..ee8f9eb 100644 --- a/chrome/browser/browser_main_win.cc +++ b/chrome/browser/browser_main_win.cc @@ -21,6 +21,7 @@ #include "base/win/windows_version.h" #include "base/win/wrapped_window_proc.h" #include "chrome/browser/first_run/first_run.h" +#include "chrome/browser/first_run/upgrade.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/views/uninstall_view.h" diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index c85e0b6..c521e11 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -26,7 +26,7 @@ #include "chrome/browser/extensions/extension_event_router_forwarder.h" #include "chrome/browser/extensions/extension_tab_id_map.h" #include "chrome/browser/extensions/user_script_listener.h" -#include "chrome/browser/first_run/first_run.h" +#include "chrome/browser/first_run/upgrade.h" #include "chrome/browser/google/google_url_tracker.h" #include "chrome/browser/gpu_process_host_ui_shim.h" #include "chrome/browser/icon_manager.h" diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc index 90b14ff..6c6d4fd 100644 --- a/chrome/browser/browser_shutdown.cc +++ b/chrome/browser/browser_shutdown.cc @@ -22,7 +22,7 @@ #include "build/build_config.h" #include "chrome/browser/about_flags.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/first_run/first_run.h" +#include "chrome/browser/first_run/upgrade.h" #include "chrome/browser/jankometer.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/prefs/pref_service.h" diff --git a/chrome/browser/first_run/first_run.cc b/chrome/browser/first_run/first_run.cc index d772ca0..62e74f2 100644 --- a/chrome/browser/first_run/first_run.cc +++ b/chrome/browser/first_run/first_run.cc @@ -468,21 +468,6 @@ bool FirstRun::GetFirstRunSentinelFilePath(FilePath* path) { return true; } -#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) -// static -void Upgrade::RelaunchChromeBrowserWithNewCommandLineIfNeeded() { - if (new_command_line_) { - if (!RelaunchChromeBrowser(*new_command_line_)) { - DLOG(ERROR) << "Launching a new instance of the browser failed."; - } else { - DLOG(WARNING) << "Launched a new instance of the browser."; - } - delete new_command_line_; - new_command_line_ = NULL; - } -} -#endif // (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) - // static void FirstRun::AutoImport( Profile* profile, diff --git a/chrome/browser/first_run/first_run.h b/chrome/browser/first_run/first_run.h index 8cb5861..25be229 100644 --- a/chrome/browser/first_run/first_run.h +++ b/chrome/browser/first_run/first_run.h @@ -213,83 +213,6 @@ class FirstRun { DISALLOW_IMPLICIT_CONSTRUCTORS(FirstRun); }; -#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) -// This class contains the actions that need to be performed when an upgrade -// is required. This involves mainly swapping the chrome exe and relaunching -// the new browser. -class Upgrade { - public: -#if defined(OS_WIN) - // Possible results of ShowTryChromeDialog(). - enum TryResult { - TD_TRY_CHROME, // Launch chrome right now. - TD_NOT_NOW, // Don't launch chrome. Exit now. - TD_UNINSTALL_CHROME, // Initiate chrome uninstall and exit. - TD_DIALOG_ERROR, // An error occurred creating the dialog. - TD_LAST_ENUM - }; - - // Check if current chrome.exe is already running as a browser process by - // trying to create a Global event with name same as full path of chrome.exe. - // This method caches the handle to this event so on subsequent calls also - // it can first close the handle and check for any other process holding the - // handle to the event. - static bool IsBrowserAlreadyRunning(); - - // If the new_chrome.exe exists (placed by the installer then is swapped - // to chrome.exe and the old chrome is renamed to old_chrome.exe. If there - // is no new_chrome.exe or the swap fails the return is false; - static bool SwapNewChromeExeIfPresent(); - - // Combines the two methods, RelaunchChromeBrowser and - // SwapNewChromeExeIfPresent, to perform the rename and relaunch of - // the browser. Note that relaunch does NOT exit the existing browser process. - // If this is called before message loop is executed, simply exit the main - // function. If browser is already running, you will need to exit it. - static bool DoUpgradeTasks(const CommandLine& command_line); - - // Shows a modal dialog asking the user to give chrome another try. See - // above for the possible outcomes of the function. This is an experimental, - // non-localized dialog. - // |version| can be 0, 1 or 2 and selects what strings to present. - // |process_singleton| needs to be valid and it will be locked while - // the dialog is shown. - static TryResult ShowTryChromeDialog(size_t version, - ProcessSingleton* process_singleton); -#endif // OS_WIN - - // Launches chrome again simulating a 'user' launch. If chrome could not - // be launched the return is false. - static bool RelaunchChromeBrowser(const CommandLine& command_line); - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - static void SaveLastModifiedTimeOfExe(); -#endif - - static void SetNewCommandLine(CommandLine* new_command_line) { - // Takes ownership of the pointer. - new_command_line_ = new_command_line; - } - - // Launches a new instance of the browser if the current instance in - // persistent mode an upgrade is detected. - static void RelaunchChromeBrowserWithNewCommandLineIfNeeded(); - - // Windows: - // Checks if chrome_new.exe is present in the current instance's install. - // Linux: - // Checks if the last modified time of chrome is newer than that of the - // current running instance. - static bool IsUpdatePendingRestart(); - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - private: - static double GetLastModifiedTimeOfExe(); - static double saved_last_modified_time_of_exe_; -#endif - static CommandLine* new_command_line_; -}; -#endif // (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS) // A subclass of BrowserProcessImpl that does not have a GoogleURLTracker or // IntranetRedirectDetector so we don't do any URL fetches (as we have no IO diff --git a/chrome/browser/first_run/first_run_gtk.cc b/chrome/browser/first_run/first_run_gtk.cc index b039f43..93dd7f4 100644 --- a/chrome/browser/first_run/first_run_gtk.cc +++ b/chrome/browser/first_run/first_run_gtk.cc @@ -45,43 +45,6 @@ bool FirstRun::ImportBookmarks(const FilePath& import_bookmarks_path) { return base::LaunchApp(import_cmd, true, false, NULL); } -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -CommandLine* Upgrade::new_command_line_ = NULL; -double Upgrade::saved_last_modified_time_of_exe_ = 0; - -// static -bool Upgrade::IsUpdatePendingRestart() { - return saved_last_modified_time_of_exe_ != - Upgrade::GetLastModifiedTimeOfExe(); -} - -// static -void Upgrade::SaveLastModifiedTimeOfExe() { - saved_last_modified_time_of_exe_ = Upgrade::GetLastModifiedTimeOfExe(); -} - -// static -bool Upgrade::RelaunchChromeBrowser(const CommandLine& command_line) { - return base::LaunchApp(command_line, false, false, NULL); -} - -// static -double Upgrade::GetLastModifiedTimeOfExe() { - FilePath exe_file_path; - if (!PathService::Get(base::FILE_EXE, &exe_file_path)) { - LOG(WARNING) << "Failed to get FilePath object for FILE_EXE."; - return saved_last_modified_time_of_exe_; - } - base::PlatformFileInfo exe_file_info; - if (!file_util::GetFileInfo(exe_file_path, &exe_file_info)) { - LOG(WARNING) << "Failed to get FileInfo object for FILE_EXE - " - << exe_file_path.value(); - return saved_last_modified_time_of_exe_; - } - return exe_file_info.last_modified.ToDoubleT(); -} -#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) - // static void FirstRun::ShowFirstRunDialog(Profile* profile, bool randomize_search_engine_experiment) { diff --git a/chrome/browser/first_run/first_run_win.cc b/chrome/browser/first_run/first_run_win.cc index 13863b9..57c94ce 100644 --- a/chrome/browser/first_run/first_run_win.cc +++ b/chrome/browser/first_run/first_run_win.cc @@ -4,7 +4,6 @@ #include "chrome/browser/first_run/first_run.h" -#include <shellapi.h> #include <shlobj.h> #include <windows.h> @@ -19,8 +18,6 @@ #include "base/string_split.h" #include "base/utf_string_conversions.h" #include "base/win/object_watcher.h" -#include "base/win/registry.h" -#include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_updater.h" @@ -28,7 +25,6 @@ #include "chrome/browser/importer/importer_list.h" #include "chrome/browser/importer/importer_progress_dialog.h" #include "chrome/browser/metrics/user_metrics.h" -#include "chrome/browser/process_singleton.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/ui/views/first_run_search_engine_view.h" @@ -50,47 +46,11 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" -#include "views/controls/button/image_button.h" -#include "views/controls/button/radio_button.h" -#include "views/controls/image_view.h" -#include "views/controls/link.h" #include "views/focus/accelerator_handler.h" -#include "views/layout/grid_layout.h" -#include "views/layout/layout_constants.h" -#include "views/widget/root_view.h" -#include "views/widget/widget.h" #include "views/window/window.h" namespace { -bool GetNewerChromeFile(FilePath* path) { - if (!PathService::Get(base::DIR_EXE, path)) - return false; - *path = path->Append(installer::kChromeNewExe); - return true; -} - -bool InvokeGoogleUpdateForRename() { - base::win::ScopedComPtr<IProcessLauncher> ipl; - if (!FAILED(ipl.CreateInstance(__uuidof(ProcessLauncherClass)))) { - ULONG_PTR phandle = NULL; - DWORD id = GetCurrentProcessId(); - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - if (!FAILED(ipl->LaunchCmdElevated(dist->GetAppGuid().c_str(), - google_update::kRegRenameCmdField, - id, &phandle))) { - HANDLE handle = HANDLE(phandle); - WaitForSingleObject(handle, INFINITE); - DWORD exit_code; - ::GetExitCodeProcess(handle, &exit_code); - ::CloseHandle(handle); - if (exit_code == installer::RENAME_SUCCESSFUL) - return true; - } - } - return false; -} - // Helper class that performs delayed first-run tasks that need more of the // chrome infrastructure to be up and running before they can be attempted. class FirstRunDelayedTasks : public NotificationObserver { @@ -180,8 +140,6 @@ void FirstRun::DoDelayedInstallExtensions() { new FirstRunDelayedTasks(FirstRunDelayedTasks::INSTALL_EXTENSIONS); } -CommandLine* Upgrade::new_command_line_ = NULL; - bool FirstRun::CreateChromeDesktopShortcut() { FilePath chrome_exe; if (!PathService::Get(base::FILE_EXE, &chrome_exe)) @@ -204,83 +162,6 @@ bool FirstRun::CreateChromeQuickLaunchShortcut() { true); // create if doesn't exist. } -bool Upgrade::IsBrowserAlreadyRunning() { - static HANDLE handle = NULL; - FilePath exe_path; - PathService::Get(base::FILE_EXE, &exe_path); - std::wstring exe = exe_path.value(); - std::replace(exe.begin(), exe.end(), '\\', '!'); - std::transform(exe.begin(), exe.end(), exe.begin(), tolower); - exe = L"Global\\" + exe; - if (handle != NULL) - CloseHandle(handle); - handle = CreateEvent(NULL, TRUE, TRUE, exe.c_str()); - int error = GetLastError(); - return (error == ERROR_ALREADY_EXISTS || error == ERROR_ACCESS_DENIED); -} - -bool Upgrade::RelaunchChromeBrowser(const CommandLine& command_line) { - scoped_ptr<base::Environment> env(base::Environment::Create()); - env->UnSetVar(chrome::kChromeVersionEnvVar); - return base::LaunchApp(command_line.command_line_string(), - false, false, NULL); -} - -bool Upgrade::SwapNewChromeExeIfPresent() { - FilePath new_chrome_exe; - if (!GetNewerChromeFile(&new_chrome_exe)) - return false; - if (!file_util::PathExists(new_chrome_exe)) - return false; - FilePath cur_chrome_exe; - if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe)) - return false; - - // First try to rename exe by launching rename command ourselves. - bool user_install = - InstallUtil::IsPerUserInstall(cur_chrome_exe.value().c_str()); - HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; - BrowserDistribution *dist = BrowserDistribution::GetDistribution(); - base::win::RegKey key; - std::wstring rename_cmd; - if ((key.Open(reg_root, dist->GetVersionKey().c_str(), - KEY_READ) == ERROR_SUCCESS) && - (key.ReadValue(google_update::kRegRenameCmdField, - &rename_cmd) == ERROR_SUCCESS)) { - base::ProcessHandle handle; - if (base::LaunchApp(rename_cmd, true, true, &handle)) { - DWORD exit_code; - ::GetExitCodeProcess(handle, &exit_code); - ::CloseHandle(handle); - if (exit_code == installer::RENAME_SUCCESSFUL) - return true; - } - } - - // Rename didn't work so try to rename by calling Google Update - return InvokeGoogleUpdateForRename(); -} - -// static -bool Upgrade::DoUpgradeTasks(const CommandLine& command_line) { - if (!Upgrade::SwapNewChromeExeIfPresent()) - return false; - // At this point the chrome.exe has been swapped with the new one. - if (!Upgrade::RelaunchChromeBrowser(command_line)) { - // The re-launch fails. Feel free to panic now. - NOTREACHED(); - } - return true; -} - -// static -bool Upgrade::IsUpdatePendingRestart() { - FilePath new_chrome_exe; - if (!GetNewerChromeFile(&new_chrome_exe)) - return false; - return file_util::PathExists(new_chrome_exe); -} - namespace { // This class is used by FirstRun::ImportSettings to determine when the import @@ -544,290 +425,3 @@ int FirstRun::ImportFromBrowser(Profile* profile, return importer_observer.import_result(); } -////////////////////////////////////////////////////////////////////////// - -namespace { - -const wchar_t kHelpCenterUrl[] = - L"http://www.google.com/support/chrome/bin/answer.py?answer=150752"; - -// This class displays a modal dialog using the views system. The dialog asks -// the user to give chrome another try. This class only handles the UI so the -// resulting actions are up to the caller. One version looks like this: -// -// /----------------------------------------\ -// | |icon| You stopped using Google [x] | -// | |icon| Chrome. Would you like to.. | -// | [o] Give the new version a try | -// | [ ] Uninstall Google Chrome | -// | [ OK ] [Don't bug me] | -// | _why_am_I_seeing this?__ | -// ------------------------------------------ -class TryChromeDialog : public views::ButtonListener, - public views::LinkController { - public: - explicit TryChromeDialog(size_t version) - : version_(version), - popup_(NULL), - try_chrome_(NULL), - kill_chrome_(NULL), - result_(Upgrade::TD_LAST_ENUM) { - } - - virtual ~TryChromeDialog() { - }; - - // Shows the modal dialog asking the user to try chrome. Note that the dialog - // has no parent and it will position itself in a lower corner of the screen. - // The dialog does not steal focus and does not have an entry in the taskbar. - Upgrade::TryResult ShowModal(ProcessSingleton* process_singleton) { - using views::GridLayout; - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - - views::ImageView* icon = new views::ImageView(); - icon->SetImage(*rb.GetBitmapNamed(IDR_PRODUCT_ICON_32)); - gfx::Size icon_size = icon->GetPreferredSize(); - - // An approximate window size. After Layout() we'll get better bounds. - views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); - params.can_activate = true; - popup_ = views::Widget::CreateWidget(params); - if (!popup_) { - NOTREACHED(); - return Upgrade::TD_DIALOG_ERROR; - } - - gfx::Rect pos(310, 160); - popup_->Init(NULL, pos); - - views::RootView* root_view = popup_->GetRootView(); - // The window color is a tiny bit off-white. - root_view->set_background( - views::Background::CreateSolidBackground(0xfc, 0xfc, 0xfc)); - - views::GridLayout* layout = views::GridLayout::CreatePanel(root_view); - if (!layout) { - NOTREACHED(); - return Upgrade::TD_DIALOG_ERROR; - } - root_view->SetLayoutManager(layout); - - views::ColumnSet* columns; - // First row: [icon][pad][text][button]. - columns = layout->AddColumnSet(0); - columns->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, - GridLayout::FIXED, icon_size.width(), - icon_size.height()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); - columns->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - columns->AddColumn(GridLayout::TRAILING, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - // Second row: [pad][pad][radio 1]. - columns = layout->AddColumnSet(1); - columns->AddPaddingColumn(0, icon_size.width()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); - columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - // Third row: [pad][pad][radio 2]. - columns = layout->AddColumnSet(2); - columns->AddPaddingColumn(0, icon_size.width()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); - columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - // Fourth row: [pad][pad][button][pad][button]. - columns = layout->AddColumnSet(3); - columns->AddPaddingColumn(0, icon_size.width()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); - columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, - GridLayout::USE_PREF, 0, 0); - columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing); - columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, - GridLayout::USE_PREF, 0, 0); - // Fifth row: [pad][pad][link]. - columns = layout->AddColumnSet(4); - columns->AddPaddingColumn(0, icon_size.width()); - columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); - columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - // First row views. - layout->StartRow(0, 0); - layout->AddView(icon); - - // Find out what experiment we are conducting. - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - if (!dist) { - NOTREACHED() << "Cannot determine browser distribution"; - return Upgrade::TD_DIALOG_ERROR; - } - BrowserDistribution::UserExperiment experiment; - if (!dist->GetExperimentDetails(&experiment, version_) || - !experiment.heading) { - NOTREACHED() << "Cannot determine which headline to show."; - return Upgrade::TD_DIALOG_ERROR; - } - string16 heading = l10n_util::GetStringUTF16(experiment.heading); - views::Label* label = new views::Label(heading); - label->SetFont(rb.GetFont(ResourceBundle::MediumBoldFont)); - label->SetMultiLine(true); - label->SizeToFit(200); - label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - layout->AddView(label); - // The close button is custom. - views::ImageButton* close_button = new views::ImageButton(this); - close_button->SetImage(views::CustomButton::BS_NORMAL, - rb.GetBitmapNamed(IDR_CLOSE_BAR)); - close_button->SetImage(views::CustomButton::BS_HOT, - rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); - close_button->SetImage(views::CustomButton::BS_PUSHED, - rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); - close_button->set_tag(BT_CLOSE_BUTTON); - layout->AddView(close_button); - - // Second row views. - const string16 try_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_TRY_OPT)); - layout->StartRowWithPadding(0, 1, 0, 10); - try_chrome_ = new views::RadioButton(try_it, 1); - layout->AddView(try_chrome_); - try_chrome_->SetChecked(true); - - // Third row views. - const string16 kill_it(l10n_util::GetStringUTF16(IDS_UNINSTALL_CHROME)); - layout->StartRow(0, 2); - kill_chrome_ = new views::RadioButton(kill_it, 1); - layout->AddView(kill_chrome_); - - // Fourth row views. - const string16 ok_it(l10n_util::GetStringUTF16(IDS_OK)); - const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); - const string16 why_this(l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY)); - layout->StartRowWithPadding(0, 3, 0, 10); - views::Button* accept_button = new views::NativeButton(this, ok_it); - accept_button->set_tag(BT_OK_BUTTON); - layout->AddView(accept_button); - views::Button* cancel_button = new views::NativeButton(this, cancel_it); - cancel_button->set_tag(BT_CLOSE_BUTTON); - layout->AddView(cancel_button); - // Fifth row views. - layout->StartRowWithPadding(0, 4, 0, 10); - views::Link* link = new views::Link(why_this); - link->SetController(this); - layout->AddView(link); - - // We resize the window according to the layout manager. This takes into - // account the differences between XP and Vista fonts and buttons. - layout->Layout(root_view); - gfx::Size preferred = layout->GetPreferredSize(root_view); - pos = ComputeWindowPosition(preferred.width(), preferred.height(), - base::i18n::IsRTL()); - popup_->SetBounds(pos); - - // Carve the toast shape into the window. - SetToastRegion(popup_->GetNativeView(), - preferred.width(), preferred.height()); - - // Time to show the window in a modal loop. We don't want this chrome - // instance trying to serve WM_COPYDATA requests, as we'll surely crash. - process_singleton->Lock(popup_->GetNativeView()); - popup_->Show(); - MessageLoop::current()->Run(); - process_singleton->Unlock(); - return result_; - } - - protected: - // Overridden from ButtonListener. We have two buttons and according to - // what the user clicked we set |result_| and we should always close and - // end the modal loop. - virtual void ButtonPressed(views::Button* sender, const views::Event& event) { - if (sender->tag() == BT_CLOSE_BUTTON) { - // The user pressed cancel or the [x] button. - result_ = Upgrade::TD_NOT_NOW; - } else if (!try_chrome_) { - // We don't have radio buttons, the user pressed ok. - result_ = Upgrade::TD_TRY_CHROME; - } else { - // The outcome is according to the selected ratio button. - result_ = try_chrome_->checked() ? Upgrade::TD_TRY_CHROME : - Upgrade::TD_UNINSTALL_CHROME; - } - popup_->Close(); - MessageLoop::current()->Quit(); - } - - // Overridden from LinkController. If the user selects the link we need to - // fire off the default browser that by some convoluted logic should not be - // chrome. - virtual void LinkActivated(views::Link* source, int event_flags) { - ::ShellExecuteW(NULL, L"open", kHelpCenterUrl, NULL, NULL, SW_SHOW); - } - - private: - enum ButtonTags { - BT_NONE, - BT_CLOSE_BUTTON, - BT_OK_BUTTON, - }; - - // Returns a screen rectangle that is fit to show the window. In particular - // it has the following properties: a) is visible and b) is attached to - // the bottom of the working area. For LTR machines it returns a left side - // rectangle and for RTL it returns a right side rectangle so that the - // dialog does not compete with the standar place of the start menu. - gfx::Rect ComputeWindowPosition(int width, int height, bool is_RTL) { - // The 'Shell_TrayWnd' is the taskbar. We like to show our window in that - // monitor if we can. This code works even if such window is not found. - HWND taskbar = ::FindWindowW(L"Shell_TrayWnd", NULL); - HMONITOR monitor = - ::MonitorFromWindow(taskbar, MONITOR_DEFAULTTOPRIMARY); - MONITORINFO info = {sizeof(info)}; - if (!GetMonitorInfoW(monitor, &info)) { - // Quite unexpected. Do a best guess at a visible rectangle. - return gfx::Rect(20, 20, width + 20, height + 20); - } - // The |rcWork| is the work area. It should account for the taskbars that - // are in the screen when we called the function. - int left = is_RTL ? info.rcWork.left : info.rcWork.right - width; - int top = info.rcWork.bottom - height; - return gfx::Rect(left, top, width, height); - } - - // Create a windows region that looks like a toast of width |w| and - // height |h|. This is best effort, so we don't care much if the operation - // fails. - void SetToastRegion(HWND window, int w, int h) { - static const POINT polygon[] = { - {0, 4}, {1, 2}, {2, 1}, {4, 0}, // Left side. - {w-4, 0}, {w-2, 1}, {w-1, 2}, {w, 4}, // Right side. - {w, h}, {0, h} - }; - HRGN region = ::CreatePolygonRgn(polygon, arraysize(polygon), WINDING); - ::SetWindowRgn(window, region, FALSE); - } - - // controls which version of the text to use. - size_t version_; - - // We don't own any of this pointers. The |popup_| owns itself and owns - // the other views. - views::Widget* popup_; - views::RadioButton* try_chrome_; - views::RadioButton* kill_chrome_; - Upgrade::TryResult result_; - - DISALLOW_COPY_AND_ASSIGN(TryChromeDialog); -}; - -} // namespace - -Upgrade::TryResult Upgrade::ShowTryChromeDialog( - size_t version, - ProcessSingleton* process_singleton) { - if (version > 10000) { - // This is a test value. We want to make sure we exercise - // returning this early. See EarlyReturnTest test harness. - return Upgrade::TD_NOT_NOW; - } - TryChromeDialog td(version); - return td.ShowModal(process_singleton); -} diff --git a/chrome/browser/first_run/try_chrome_dialog_view.cc b/chrome/browser/first_run/try_chrome_dialog_view.cc new file mode 100644 index 0000000..bd017c0 --- /dev/null +++ b/chrome/browser/first_run/try_chrome_dialog_view.cc @@ -0,0 +1,249 @@ +// Copyright (c) 2011 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/browser/first_run/try_chrome_dialog_view.h" + +#include <shellapi.h> + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string16.h" +#include "chrome/browser/process_singleton.h" +#include "chrome/installer/util/browser_distribution.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "views/controls/button/image_button.h" +#include "views/controls/button/radio_button.h" +#include "views/controls/image_view.h" +#include "views/layout/grid_layout.h" +#include "views/layout/layout_constants.h" +#include "views/widget/root_view.h" +#include "views/widget/widget.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +const wchar_t kHelpCenterUrl[] = + L"http://www.google.com/support/chrome/bin/answer.py?answer=150752"; + +} // namespace + +TryChromeDialogView::TryChromeDialogView(size_t version) + : version_(version), + popup_(NULL), + try_chrome_(NULL), + kill_chrome_(NULL), + result_(Upgrade::COUNT) { +} + +TryChromeDialogView::~TryChromeDialogView() { +} + +Upgrade::TryResult TryChromeDialogView::ShowModal( + ProcessSingleton* process_singleton) { + using views::GridLayout; + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + views::ImageView* icon = new views::ImageView(); + icon->SetImage(*rb.GetBitmapNamed(IDR_PRODUCT_ICON_32)); + gfx::Size icon_size = icon->GetPreferredSize(); + + // An approximate window size. After Layout() we'll get better bounds. + views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP); + params.can_activate = true; + popup_ = views::Widget::CreateWidget(params); + if (!popup_) { + NOTREACHED(); + return Upgrade::DIALOG_ERROR; + } + + gfx::Rect pos(310, 160); + popup_->Init(NULL, pos); + + views::RootView* root_view = popup_->GetRootView(); + // The window color is a tiny bit off-white. + root_view->set_background( + views::Background::CreateSolidBackground(0xfc, 0xfc, 0xfc)); + + views::GridLayout* layout = views::GridLayout::CreatePanel(root_view); + if (!layout) { + NOTREACHED(); + return Upgrade::DIALOG_ERROR; + } + root_view->SetLayoutManager(layout); + + views::ColumnSet* columns; + // First row: [icon][pad][text][button]. + columns = layout->AddColumnSet(0); + columns->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, + GridLayout::FIXED, icon_size.width(), + icon_size.height()); + columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + columns->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + columns->AddColumn(GridLayout::TRAILING, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + // Second row: [pad][pad][radio 1]. + columns = layout->AddColumnSet(1); + columns->AddPaddingColumn(0, icon_size.width()); + columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + // Third row: [pad][pad][radio 2]. + columns = layout->AddColumnSet(2); + columns->AddPaddingColumn(0, icon_size.width()); + columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + // Fourth row: [pad][pad][button][pad][button]. + columns = layout->AddColumnSet(3); + columns->AddPaddingColumn(0, icon_size.width()); + columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, + GridLayout::USE_PREF, 0, 0); + columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing); + columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, + GridLayout::USE_PREF, 0, 0); + // Fifth row: [pad][pad][link]. + columns = layout->AddColumnSet(4); + columns->AddPaddingColumn(0, icon_size.width()); + columns->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing); + columns->AddColumn(GridLayout::LEADING, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + // First row views. + layout->StartRow(0, 0); + layout->AddView(icon); + + // Find out what experiment we are conducting. + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + if (!dist) { + NOTREACHED() << "Cannot determine browser distribution"; + return Upgrade::DIALOG_ERROR; + } + BrowserDistribution::UserExperiment experiment; + if (!dist->GetExperimentDetails(&experiment, version_) || + !experiment.heading) { + NOTREACHED() << "Cannot determine which headline to show."; + return Upgrade::DIALOG_ERROR; + } + string16 heading = l10n_util::GetStringUTF16(experiment.heading); + views::Label* label = new views::Label(heading); + label->SetFont(rb.GetFont(ResourceBundle::MediumBoldFont)); + label->SetMultiLine(true); + label->SizeToFit(200); + label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + layout->AddView(label); + // The close button is custom. + views::ImageButton* close_button = new views::ImageButton(this); + close_button->SetImage(views::CustomButton::BS_NORMAL, + rb.GetBitmapNamed(IDR_CLOSE_BAR)); + close_button->SetImage(views::CustomButton::BS_HOT, + rb.GetBitmapNamed(IDR_CLOSE_BAR_H)); + close_button->SetImage(views::CustomButton::BS_PUSHED, + rb.GetBitmapNamed(IDR_CLOSE_BAR_P)); + close_button->set_tag(BT_CLOSE_BUTTON); + layout->AddView(close_button); + + // Second row views. + const string16 try_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_TRY_OPT)); + layout->StartRowWithPadding(0, 1, 0, 10); + try_chrome_ = new views::RadioButton(try_it, 1); + layout->AddView(try_chrome_); + try_chrome_->SetChecked(true); + + // Third row views. + const string16 kill_it(l10n_util::GetStringUTF16(IDS_UNINSTALL_CHROME)); + layout->StartRow(0, 2); + kill_chrome_ = new views::RadioButton(kill_it, 1); + layout->AddView(kill_chrome_); + + // Fourth row views. + const string16 ok_it(l10n_util::GetStringUTF16(IDS_OK)); + const string16 cancel_it(l10n_util::GetStringUTF16(IDS_TRY_TOAST_CANCEL)); + const string16 why_this(l10n_util::GetStringUTF16(IDS_TRY_TOAST_WHY)); + layout->StartRowWithPadding(0, 3, 0, 10); + views::Button* accept_button = new views::NativeButton(this, ok_it); + accept_button->set_tag(BT_OK_BUTTON); + layout->AddView(accept_button); + views::Button* cancel_button = new views::NativeButton(this, cancel_it); + cancel_button->set_tag(BT_CLOSE_BUTTON); + layout->AddView(cancel_button); + // Fifth row views. + layout->StartRowWithPadding(0, 4, 0, 10); + views::Link* link = new views::Link(why_this); + link->SetController(this); + layout->AddView(link); + + // We resize the window according to the layout manager. This takes into + // account the differences between XP and Vista fonts and buttons. + layout->Layout(root_view); + gfx::Size preferred = layout->GetPreferredSize(root_view); + pos = ComputeWindowPosition(preferred.width(), preferred.height(), + base::i18n::IsRTL()); + popup_->SetBounds(pos); + + // Carve the toast shape into the window. + SetToastRegion(popup_->GetNativeView(), + preferred.width(), preferred.height()); + + // Time to show the window in a modal loop. We don't want this chrome + // instance trying to serve WM_COPYDATA requests, as we'll surely crash. + process_singleton->Lock(popup_->GetNativeView()); + popup_->Show(); + MessageLoop::current()->Run(); + process_singleton->Unlock(); + return result_; +} + +void TryChromeDialogView::ButtonPressed(views::Button* sender, + const views::Event& event) { + if (sender->tag() == BT_CLOSE_BUTTON) { + // The user pressed cancel or the [x] button. + result_ = Upgrade::NOT_NOW; + } else if (!try_chrome_) { + // We don't have radio buttons, the user pressed ok. + result_ = Upgrade::TRY_CHROME; + } else { + // The outcome is according to the selected ratio button. + result_ = try_chrome_->checked() ? Upgrade::TRY_CHROME : + Upgrade::UNINSTALL_CHROME; + } + popup_->Close(); + MessageLoop::current()->Quit(); +} + +void TryChromeDialogView::LinkActivated(views::Link* source, int event_flags) { + ::ShellExecuteW(NULL, L"open", kHelpCenterUrl, NULL, NULL, SW_SHOW); +} + +gfx::Rect TryChromeDialogView::ComputeWindowPosition(int width, + int height, + bool is_RTL) { + // The 'Shell_TrayWnd' is the taskbar. We like to show our window in that + // monitor if we can. This code works even if such window is not found. + HWND taskbar = ::FindWindowW(L"Shell_TrayWnd", NULL); + HMONITOR monitor = ::MonitorFromWindow(taskbar, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO info = {sizeof(info)}; + if (!GetMonitorInfoW(monitor, &info)) { + // Quite unexpected. Do a best guess at a visible rectangle. + return gfx::Rect(20, 20, width + 20, height + 20); + } + // The |rcWork| is the work area. It should account for the taskbars that + // are in the screen when we called the function. + int left = is_RTL ? info.rcWork.left : info.rcWork.right - width; + int top = info.rcWork.bottom - height; + return gfx::Rect(left, top, width, height); +} + +void TryChromeDialogView::SetToastRegion(HWND window, int w, int h) { + static const POINT polygon[] = { + {0, 4}, {1, 2}, {2, 1}, {4, 0}, // Left side. + {w-4, 0}, {w-2, 1}, {w-1, 2}, {w, 4}, // Right side. + {w, h}, {0, h} + }; + HRGN region = ::CreatePolygonRgn(polygon, arraysize(polygon), WINDING); + ::SetWindowRgn(window, region, FALSE); +} diff --git a/chrome/browser/first_run/try_chrome_dialog_view.h b/chrome/browser/first_run/try_chrome_dialog_view.h new file mode 100644 index 0000000..a2fb243 --- /dev/null +++ b/chrome/browser/first_run/try_chrome_dialog_view.h @@ -0,0 +1,95 @@ +// Copyright (c) 2011 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_BROWSER_FIRST_RUN_TRY_CHROME_DIALOG_VIEW_H_ +#define CHROME_BROWSER_FIRST_RUN_TRY_CHROME_DIALOG_VIEW_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/first_run/upgrade.h" +#include "views/controls/button/button.h" +#include "views/controls/link.h" +#include "ui/gfx/native_widget_types.h" + +class ProcessSingleton; + +namespace gfx { +class Rect; +} + +namespace views { +class RadioButton; +class Widget; +} + +// This class displays a modal dialog using the views system. The dialog asks +// the user to give chrome another try. This class only handles the UI so the +// resulting actions are up to the caller. One version looks like this: +// +// +-----------------------------------------------+ +// | |icon| You stopped using Google Chrome [x] | +// | |icon| Would you like to: | +// | [o] Give the new version a try | +// | [ ] Uninstall Google Chrome | +// | [ OK ] [Don't bug me] | +// | | +// | _why_am_I_seeing this?_ | +// +-----------------------------------------------+ +// +class TryChromeDialogView : public views::ButtonListener, + public views::LinkController { + public: + explicit TryChromeDialogView(size_t version); + virtual ~TryChromeDialogView(); + + // Shows the modal dialog asking the user to try chrome. Note that the dialog + // has no parent and it will position itself in a lower corner of the screen. + // The dialog does not steal focus and does not have an entry in the taskbar. + Upgrade::TryResult ShowModal(ProcessSingleton* process_singleton); + + protected: + // views::ButtonListener: + // We have two buttons and according to what the user clicked we set |result_| + // and we should always close and end the modal loop. + virtual void ButtonPressed(views::Button* sender, + const views::Event& event) OVERRIDE; + + // views::LinkController: + // If the user selects the link we need to fire off the default browser that + // by some convoluted logic should not be chrome. + virtual void LinkActivated(views::Link* source, int event_flags) OVERRIDE; + + private: + enum ButtonTags { + BT_NONE, + BT_CLOSE_BUTTON, + BT_OK_BUTTON, + }; + + // Returns a screen rectangle that is fit to show the window. In particular + // it has the following properties: a) is visible and b) is attached to the + // bottom of the working area. For LTR machines it returns a left side + // rectangle and for RTL it returns a right side rectangle so that the dialog + // does not compete with the standar place of the start menu. + gfx::Rect ComputeWindowPosition(int width, int height, bool is_RTL); + + // Create a windows region that looks like a toast of width |w| and height + // |h|. This is best effort, so we don't care much if the operation fails. + void SetToastRegion(gfx::NativeWindow window, int w, int h); + + // Controls which version of the text to use. + size_t version_; + + // We don't own any of these pointers. The |popup_| owns itself and owns the + // other views. + views::Widget* popup_; + views::RadioButton* try_chrome_; + views::RadioButton* kill_chrome_; + Upgrade::TryResult result_; + + DISALLOW_COPY_AND_ASSIGN(TryChromeDialogView); +}; + +#endif // CHROME_BROWSER_FIRST_RUN_TRY_CHROME_DIALOG_VIEW_H_ diff --git a/chrome/browser/first_run/upgrade.cc b/chrome/browser/first_run/upgrade.cc new file mode 100644 index 0000000..778f491 --- /dev/null +++ b/chrome/browser/first_run/upgrade.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2011 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/browser/first_run/upgrade.h" + +#include "base/command_line.h" +#include "base/logging.h" + +// static +void Upgrade::RelaunchChromeBrowserWithNewCommandLineIfNeeded() { + if (new_command_line_) { + if (!RelaunchChromeBrowser(*new_command_line_)) { + DLOG(ERROR) << "Launching a new instance of the browser failed."; + } else { + DLOG(WARNING) << "Launched a new instance of the browser."; + } + delete new_command_line_; + new_command_line_ = NULL; + } +} diff --git a/chrome/browser/first_run/upgrade.h b/chrome/browser/first_run/upgrade.h new file mode 100644 index 0000000..265e094 --- /dev/null +++ b/chrome/browser/first_run/upgrade.h @@ -0,0 +1,96 @@ +// Copyright (c) 2011 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_BROWSER_FIRST_RUN_UPGRADE_H_ +#define CHROME_BROWSER_FIRST_RUN_UPGRADE_H_ +#pragma once + +#include "base/basictypes.h" +#include "build/build_config.h" + +class CommandLine; + +#if defined(OS_WIN) +class ProcessSingleton; +#endif + +// This class contains the actions that need to be performed when an upgrade +// is required. This involves mainly swapping the chrome exe and relaunching +// the new browser. +class Upgrade { + public: +#if defined(OS_WIN) + // Possible results of ShowTryChromeDialog(). + enum TryResult { + TRY_CHROME, // Launch chrome right now. + NOT_NOW, // Don't launch chrome. Exit now. + UNINSTALL_CHROME, // Initiate chrome uninstall and exit. + DIALOG_ERROR, // An error occurred creating the dialog. + COUNT + }; + + // Check if current chrome.exe is already running as a browser process by + // trying to create a Global event with name same as full path of chrome.exe. + // This method caches the handle to this event so on subsequent calls also + // it can first close the handle and check for any other process holding the + // handle to the event. + static bool IsBrowserAlreadyRunning(); + + // If the new_chrome.exe exists (placed by the installer then is swapped + // to chrome.exe and the old chrome is renamed to old_chrome.exe. If there + // is no new_chrome.exe or the swap fails the return is false; + static bool SwapNewChromeExeIfPresent(); + + // Combines the two methods, RelaunchChromeBrowser and + // SwapNewChromeExeIfPresent, to perform the rename and relaunch of + // the browser. Note that relaunch does NOT exit the existing browser process. + // If this is called before message loop is executed, simply exit the main + // function. If browser is already running, you will need to exit it. + static bool DoUpgradeTasks(const CommandLine& command_line); + + // Shows a modal dialog asking the user to give chrome another try. See + // above for the possible outcomes of the function. This is an experimental, + // non-localized dialog. + // |version| can be 0, 1 or 2 and selects what strings to present. + // |process_singleton| needs to be valid and it will be locked while + // the dialog is shown. + static TryResult ShowTryChromeDialog(size_t version, + ProcessSingleton* process_singleton); +#endif // OS_WIN + + // Launches chrome again simulating a 'user' launch. If chrome could not + // be launched the return is false. + static bool RelaunchChromeBrowser(const CommandLine& command_line); + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + static void SaveLastModifiedTimeOfExe(); +#endif + + static void SetNewCommandLine(CommandLine* new_command_line) { + // Takes ownership of the pointer. + new_command_line_ = new_command_line; + } + + // Launches a new instance of the browser if the current instance in + // persistent mode an upgrade is detected. + static void RelaunchChromeBrowserWithNewCommandLineIfNeeded(); + + // Windows: + // Checks if chrome_new.exe is present in the current instance's install. + // Linux: + // Checks if the last modified time of chrome is newer than that of the + // current running instance. + static bool IsUpdatePendingRestart(); + + private: +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + static double GetLastModifiedTimeOfExe(); + static double saved_last_modified_time_of_exe_; +#endif + static CommandLine* new_command_line_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(Upgrade); +}; + +#endif // CHROME_BROWSER_FIRST_RUN_UPGRADE_H_ diff --git a/chrome/browser/first_run/upgrade_gtk.cc b/chrome/browser/first_run/upgrade_gtk.cc new file mode 100644 index 0000000..419e58f --- /dev/null +++ b/chrome/browser/first_run/upgrade_gtk.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2011 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/browser/first_run/upgrade.h" + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/platform_file.h" +#include "base/process_util.h" + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +// static +CommandLine* Upgrade::new_command_line_ = NULL; + +// static +double Upgrade::saved_last_modified_time_of_exe_ = 0; + +// static +bool Upgrade::RelaunchChromeBrowser(const CommandLine& command_line) { + return base::LaunchApp(command_line, false, false, NULL); +} + +// static +void Upgrade::SaveLastModifiedTimeOfExe() { + saved_last_modified_time_of_exe_ = Upgrade::GetLastModifiedTimeOfExe(); +} + +// static +bool Upgrade::IsUpdatePendingRestart() { + return saved_last_modified_time_of_exe_ != + Upgrade::GetLastModifiedTimeOfExe(); +} + +// static +double Upgrade::GetLastModifiedTimeOfExe() { + FilePath exe_file_path; + if (!PathService::Get(base::FILE_EXE, &exe_file_path)) { + LOG(WARNING) << "Failed to get FilePath object for FILE_EXE."; + return saved_last_modified_time_of_exe_; + } + base::PlatformFileInfo exe_file_info; + if (!file_util::GetFileInfo(exe_file_path, &exe_file_info)) { + LOG(WARNING) << "Failed to get FileInfo object for FILE_EXE - " + << exe_file_path.value(); + return saved_last_modified_time_of_exe_; + } + return exe_file_info.last_modified.ToDoubleT(); +} + +#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) diff --git a/chrome/browser/first_run/upgrade_win.cc b/chrome/browser/first_run/upgrade_win.cc new file mode 100644 index 0000000..42f3b74 --- /dev/null +++ b/chrome/browser/first_run/upgrade_win.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2011 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/browser/first_run/upgrade.h" + +#include <algorithm> +#include <string> + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/environment.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/win/registry.h" +#include "base/win/scoped_comptr.h" +#include "chrome/browser/first_run/try_chrome_dialog_view.h" +#include "chrome/browser/process_singleton.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/shell_util.h" +#include "chrome/installer/util/util_constants.h" +#include "google_update_idl.h" + +namespace { + +bool GetNewerChromeFile(FilePath* path) { + if (!PathService::Get(base::DIR_EXE, path)) + return false; + *path = path->Append(installer::kChromeNewExe); + return true; +} + +bool InvokeGoogleUpdateForRename() { + base::win::ScopedComPtr<IProcessLauncher> ipl; + if (!FAILED(ipl.CreateInstance(__uuidof(ProcessLauncherClass)))) { + ULONG_PTR phandle = NULL; + DWORD id = GetCurrentProcessId(); + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + if (!FAILED(ipl->LaunchCmdElevated(dist->GetAppGuid().c_str(), + google_update::kRegRenameCmdField, + id, + &phandle))) { + HANDLE handle = HANDLE(phandle); + WaitForSingleObject(handle, INFINITE); + DWORD exit_code; + ::GetExitCodeProcess(handle, &exit_code); + ::CloseHandle(handle); + if (exit_code == installer::RENAME_SUCCESSFUL) + return true; + } + } + return false; +} + +} // namespace + +// static +CommandLine* Upgrade::new_command_line_ = NULL; + +// static +bool Upgrade::IsBrowserAlreadyRunning() { + static HANDLE handle = NULL; + FilePath exe_path; + PathService::Get(base::FILE_EXE, &exe_path); + std::wstring exe = exe_path.value(); + std::replace(exe.begin(), exe.end(), '\\', '!'); + std::transform(exe.begin(), exe.end(), exe.begin(), tolower); + exe = L"Global\\" + exe; + if (handle != NULL) + CloseHandle(handle); + handle = CreateEvent(NULL, TRUE, TRUE, exe.c_str()); + int error = GetLastError(); + return (error == ERROR_ALREADY_EXISTS || error == ERROR_ACCESS_DENIED); +} + +// static +bool Upgrade::SwapNewChromeExeIfPresent() { + FilePath new_chrome_exe; + if (!GetNewerChromeFile(&new_chrome_exe)) + return false; + if (!file_util::PathExists(new_chrome_exe)) + return false; + FilePath cur_chrome_exe; + if (!PathService::Get(base::FILE_EXE, &cur_chrome_exe)) + return false; + + // First try to rename exe by launching rename command ourselves. + bool user_install = + InstallUtil::IsPerUserInstall(cur_chrome_exe.value().c_str()); + HKEY reg_root = user_install ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + BrowserDistribution *dist = BrowserDistribution::GetDistribution(); + base::win::RegKey key; + std::wstring rename_cmd; + if ((key.Open(reg_root, dist->GetVersionKey().c_str(), + KEY_READ) == ERROR_SUCCESS) && + (key.ReadValue(google_update::kRegRenameCmdField, + &rename_cmd) == ERROR_SUCCESS)) { + base::ProcessHandle handle; + if (base::LaunchApp(rename_cmd, true, true, &handle)) { + DWORD exit_code; + ::GetExitCodeProcess(handle, &exit_code); + ::CloseHandle(handle); + if (exit_code == installer::RENAME_SUCCESSFUL) + return true; + } + } + + // Rename didn't work so try to rename by calling Google Update + return InvokeGoogleUpdateForRename(); +} + +// static +bool Upgrade::DoUpgradeTasks(const CommandLine& command_line) { + if (!Upgrade::SwapNewChromeExeIfPresent()) + return false; + // At this point the chrome.exe has been swapped with the new one. + if (!Upgrade::RelaunchChromeBrowser(command_line)) { + // The re-launch fails. Feel free to panic now. + NOTREACHED(); + } + return true; +} + +// static +Upgrade::TryResult Upgrade::ShowTryChromeDialog( + size_t version, + ProcessSingleton* process_singleton) { + if (version > 10000) { + // This is a test value. We want to make sure we exercise + // returning this early. See EarlyReturnTest test harness. + return Upgrade::NOT_NOW; + } + TryChromeDialogView dialog(version); + return dialog.ShowModal(process_singleton); +} + +// static +bool Upgrade::RelaunchChromeBrowser(const CommandLine& command_line) { + scoped_ptr<base::Environment> env(base::Environment::Create()); + env->UnSetVar(chrome::kChromeVersionEnvVar); + return base::LaunchApp( + command_line.command_line_string(), false, false, NULL); +} + +// static +bool Upgrade::IsUpdatePendingRestart() { + FilePath new_chrome_exe; + if (!GetNewerChromeFile(&new_chrome_exe)) + return false; + return file_util::PathExists(new_chrome_exe); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 85215cd..6a3a582 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1060,6 +1060,12 @@ 'browser/first_run/first_run_gtk.cc', 'browser/first_run/first_run_mac.mm', 'browser/first_run/first_run_win.cc', + 'browser/first_run/upgrade.cc', + 'browser/first_run/upgrade.h', + 'browser/first_run/upgrade_gtk.cc', + 'browser/first_run/upgrade_win.cc', + 'browser/first_run/try_chrome_dialog_view.cc', + 'browser/first_run/try_chrome_dialog_view.h', 'browser/fullscreen.h', 'browser/fullscreen_linux.cc', 'browser/fullscreen_mac.mm', @@ -3374,7 +3380,6 @@ ['exclude', '^browser/ui/webui/chromeos'], ['exclude', '^browser/ui/webui/options/chromeos'], ['exclude', 'browser/extensions/extension_tts_api_chromeos.cc'], - ['exclude', 'browser/ui/login/login_prompt_ui.cc'], ['exclude', 'browser/oom_priority_manager.cc'], ['exclude', 'browser/oom_priority_manager.h'], ['exclude', 'browser/policy/device_policy_cache\\.(h|cc)'], @@ -3383,6 +3388,7 @@ ['exclude', 'browser/policy/proto/chrome_device_policy\\.pb\\.(h|cc)'], ['exclude', 'browser/renderer_host/offline_resource_handler.cc'], ['exclude', 'browser/renderer_host/offline_resource_handler.h'], + ['exclude', 'browser/ui/login/login_prompt_ui.cc'], ['exclude', 'browser/ui/webui/chrome_about_ui.cc'], ['exclude', 'browser/ui/webui/collected_cookies_ui_delegate.cc'], ['exclude', 'browser/ui/webui/collected_cookies_ui_delegate.h'], @@ -3396,6 +3402,8 @@ ['chromeos==1', { 'sources!': [ 'browser/background_mode_manager_linux.cc', + 'browser/first_run/upgrade.cc', + 'browser/first_run/upgrade.h', 'browser/password_manager/native_backend_gnome_x.cc', 'browser/password_manager/native_backend_gnome_x.h', 'browser/password_manager/native_backend_kwallet_x.cc', @@ -3463,6 +3471,8 @@ ], 'sources': [ 'browser/crash_handler_host_linux.h', + 'browser/first_run/upgrade.cc', + 'browser/first_run/upgrade.h', ], 'conditions': [ ['use_gnome_keyring==1', { @@ -3526,6 +3536,8 @@ 'browser/automation/automation_provider_list_generic.cc', 'browser/bookmarks/bookmark_context_menu.cc', 'browser/bookmarks/bookmark_drop_info.cc', + 'browser/first_run/upgrade.cc', + 'browser/first_run/upgrade.h', 'browser/importer/nss_decryptor_system_nss.cc', 'browser/importer/nss_decryptor_system_nss.h', 'browser/jankometer.cc', @@ -3686,6 +3698,8 @@ } }, 'sources': [ + 'browser/first_run/upgrade.cc', + 'browser/first_run/upgrade.h', # Using built-in rule in vstudio for midl. 'browser/history/history_indexer.idl', 'browser/ui/webui/conflicts_ui.cc', @@ -3722,6 +3736,10 @@ # Exclude all of views. ['exclude', '^browser/ui/views/'], + + # Exclude try chrome dialog. + ['exclude', '^browser/first_run/try_chrome_dialog_view.cc'], + ['exclude', '^browser/first_run/try_chrome_dialog_view.h'], ], 'conditions': [ ['OS=="linux" and toolkit_views==1',{ |