summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-12 21:46:57 +0000
committertfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-12 21:46:57 +0000
commitbc38c254e32398b444dbccd391c2ae3d6f182001 (patch)
treea1e4ba91782a44147002ac30d6a57c0c0b535830
parent75e20841bd18a6053aa41b754ddfcaa1445435d8 (diff)
downloadchromium_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.cc10
-rw-r--r--chrome/browser/browser_main.cc7
-rw-r--r--chrome/browser/browser_main_win.cc1
-rw-r--r--chrome/browser/browser_process_impl.cc2
-rw-r--r--chrome/browser/browser_shutdown.cc2
-rw-r--r--chrome/browser/first_run/first_run.cc15
-rw-r--r--chrome/browser/first_run/first_run.h77
-rw-r--r--chrome/browser/first_run/first_run_gtk.cc37
-rw-r--r--chrome/browser/first_run/first_run_win.cc406
-rw-r--r--chrome/browser/first_run/try_chrome_dialog_view.cc249
-rw-r--r--chrome/browser/first_run/try_chrome_dialog_view.h95
-rw-r--r--chrome/browser/first_run/upgrade.cc21
-rw-r--r--chrome/browser/first_run/upgrade.h96
-rw-r--r--chrome/browser/first_run/upgrade_gtk.cc55
-rw-r--r--chrome/browser/first_run/upgrade_win.cc157
-rw-r--r--chrome/chrome_browser.gypi20
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',{