diff --git a/app/throb_animation.cc b/app/throb_animation.cc index 922d77d4dfb115..65596b34f956a0 100644 --- a/app/throb_animation.cc +++ b/app/throb_animation.cc @@ -4,6 +4,8 @@ #include "app/throb_animation.h" +#include + static const int kDefaultThrobDurationMS = 400; ThrobAnimation::ThrobAnimation(AnimationDelegate* target) @@ -15,6 +17,8 @@ ThrobAnimation::ThrobAnimation(AnimationDelegate* target) } void ThrobAnimation::StartThrobbing(int cycles_til_stop) { + cycles_til_stop = cycles_til_stop >= 0 ? cycles_til_stop : + std::numeric_limits::max(); cycles_remaining_ = cycles_til_stop; throbbing_ = true; SlideAnimation::SetSlideDuration(throb_duration_); diff --git a/app/throb_animation.h b/app/throb_animation.h index 2405724151f439..7dc67b35d56e18 100644 --- a/app/throb_animation.h +++ b/app/throb_animation.h @@ -20,7 +20,7 @@ class ThrobAnimation : public SlideAnimation { virtual ~ThrobAnimation() {} // Starts throbbing. cycles_til_stop gives the number of cycles to do before - // stopping. + // stopping. A negative value means "throb indefinitely". void StartThrobbing(int cycles_til_stop); // Sets the duration of the slide animation when throbbing. diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index ca682a5e7e3030..1a39c5eaa088f2 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -37,6 +37,7 @@ #include "base/at_exit.h" #include "base/command_line.h" #include "base/debug_util.h" +#include "base/file_version_info.h" #include "base/i18n/icu_util.h" #include "base/message_loop.h" #include "base/path_service.h" @@ -45,6 +46,7 @@ #include "base/stats_counters.h" #include "base/stats_table.h" #include "base/string_util.h" +#include "chrome/app/chrome_version_info.h" #include "chrome/browser/diagnostics/diagnostics_main.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/chrome_constants.h" @@ -475,6 +477,15 @@ int ChromeMain(int argc, char** argv) { #endif const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + if (parsed_command_line.HasSwitch(switches::kProductVersion)) { + scoped_ptr version(chrome_app::GetChromeVersionInfo()); + printf("%s\n", WideToASCII(version->product_version()).c_str()); + return 0; + } +#endif + std::string process_type = parsed_command_line.GetSwitchValueASCII(switches::kProcessType); diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc index 0ad895a6829265..7864ad56d0ced4 100644 --- a/chrome/browser/browser_prefs.cc +++ b/chrome/browser/browser_prefs.cc @@ -40,11 +40,11 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/task_manager.h" #include "chrome/browser/translate/translate_prefs.h" +#include "chrome/browser/upgrade_detector.h" #if defined(TOOLKIT_VIEWS) // TODO(port): whittle this down as we port #include "chrome/browser/views/browser_actions_container.h" #include "chrome/browser/views/frame/browser_view.h" -#include "chrome/browser/views/update_recommended_message_box.h" #endif #if defined(TOOLKIT_GTK) @@ -81,9 +81,7 @@ void RegisterLocalState(PrefService* local_state) { #if defined(TOOLKIT_VIEWS) BrowserView::RegisterBrowserViewPrefs(local_state); #endif -#if defined(OS_WIN) - UpdateRecommendedMessageBox::RegisterUpdateRecommendedPrefs(local_state); -#endif + UpgradeDetector::RegisterPrefs(local_state); TaskManager::RegisterPrefs(local_state); CookiePromptModalDialog::RegisterPrefs(local_state); geolocation::RegisterPrefs(local_state); diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc index af5bc26e42f558..dc558538173b45 100644 --- a/chrome/browser/browser_shutdown.cc +++ b/chrome/browser/browser_shutdown.cc @@ -161,21 +161,20 @@ void Shutdown() { shutdown_type_ != browser_shutdown::END_SESSION) { Upgrade::SwapNewChromeExeIfPresent(); } +#endif if (restart_last_session) { +#if defined(OS_WIN) || defined(OS_LINUX) // Make sure to relaunch the browser with the same command line and add // Restore Last Session flag if session restore is not set. - CommandLine command_line = CommandLine::FromString( - CommandLine::ForCurrentProcess()->command_line_string()); + CommandLine command_line(*CommandLine::ForCurrentProcess()); if (!command_line.HasSwitch(switches::kRestoreLastSession)) command_line.AppendSwitch(switches::kRestoreLastSession); Upgrade::RelaunchChromeBrowser(command_line); - } -#endif -#if !defined(OS_WIN) - if (restart_last_session) +#else NOTIMPLEMENTED(); #endif + } if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { // Measure total shutdown time as late in the process as possible diff --git a/chrome/browser/first_run.h b/chrome/browser/first_run.h index a08e7532abe23b..92c80c0d620d6a 100644 --- a/chrome/browser/first_run.h +++ b/chrome/browser/first_run.h @@ -157,10 +157,6 @@ class Upgrade { // handle to the event. static bool IsBrowserAlreadyRunning(); - // 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 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; @@ -180,6 +176,10 @@ class Upgrade { static TryResult ShowTryChromeDialog(size_t version); #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 diff --git a/chrome/browser/first_run_gtk.cc b/chrome/browser/first_run_gtk.cc index 4183fe3eb7b1b7..202d095e4396c3 100644 --- a/chrome/browser/first_run_gtk.cc +++ b/chrome/browser/first_run_gtk.cc @@ -9,6 +9,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/path_service.h" +#include "base/process_util.h" #include "chrome/browser/gtk/first_run_dialog.h" #include "chrome/browser/profile_manager.h" #include "chrome/browser/shell_integration.h" @@ -152,6 +153,11 @@ 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 void Upgrade::RelaunchChromeBrowserWithNewCommandLineIfNeeded() { if (new_command_line_) { diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc index 822bf22fcc8441..a73b1db6b5719a 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_toolbar_gtk.cc @@ -41,12 +41,15 @@ #include "chrome/browser/profile.h" #include "chrome/browser/sync/sync_ui_util.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/upgrade_detector.h" #include "chrome/common/notification_details.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" +#include "gfx/canvas_paint.h" #include "gfx/gtk_util.h" +#include "gfx/skbitmap_operations.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" @@ -68,6 +71,13 @@ const int kToolbarWidgetSpacing = 2; // Amount of rounding on top corners of toolbar. Only used in Gtk theme mode. const int kToolbarCornerSize = 3; +// The offset in pixels of the upgrade dot on the app menu. +const int kUpgradeDotOffset = 11; + +// The duration of the upgrade notification animation (actually the duration +// of a half-throb). +const int kThrobDuration = 1000; + } // namespace // BrowserToolbarGtk, public --------------------------------------------------- @@ -82,7 +92,8 @@ BrowserToolbarGtk::BrowserToolbarGtk(Browser* browser, BrowserWindowGtk* window) window_(window), profile_(NULL), sync_service_(NULL), - menu_bar_helper_(this) { + menu_bar_helper_(this), + upgrade_reminder_animation_(this) { browser_->command_updater()->AddCommandObserver(IDC_BACK, this); browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this); browser_->command_updater()->AddCommandObserver(IDC_RELOAD, this); @@ -92,6 +103,14 @@ BrowserToolbarGtk::BrowserToolbarGtk(Browser* browser, BrowserWindowGtk* window) registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); + registrar_.Add(this, + NotificationType::UPGRADE_RECOMMENDED, + NotificationService::AllSources()); + + upgrade_reminder_animation_.SetThrobDuration(kThrobDuration); + + if (Singleton::get()->notify_upgrade()) + ShowUpgradeReminder(); } BrowserToolbarGtk::~BrowserToolbarGtk() { @@ -213,11 +232,15 @@ void BrowserToolbarGtk::Init(Profile* profile, app_menu_image_ = gtk_image_new_from_pixbuf( theme_provider_->GetRTLEnabledPixbufNamed(IDR_MENU_CHROME)); gtk_container_add(GTK_CONTAINER(chrome_menu), app_menu_image_); + g_signal_connect_after(app_menu_image_, "expose-event", + G_CALLBACK(OnAppMenuImageExposeThunk), this); app_menu_.reset(new MenuGtk(this, &app_menu_model_)); gtk_box_pack_start(GTK_BOX(menus_hbox), chrome_menu, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(toolbar_right_), menus_hbox, FALSE, FALSE, kToolbarWidgetSpacing); + g_signal_connect(app_menu_->widget(), "show", + G_CALLBACK(OnAppMenuShowThunk), this); gtk_box_pack_start(GTK_BOX(toolbar_), toolbar_right_, FALSE, FALSE, 0); @@ -425,6 +448,8 @@ void BrowserToolbarGtk::Observe(NotificationType type, gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), use_gtk); UpdateRoundedness(); + } else if (type == NotificationType::UPGRADE_RECOMMENDED) { + ShowUpgradeReminder(); } else { NOTREACHED(); } @@ -750,6 +775,10 @@ void BrowserToolbarGtk::NotifyPrefChanged(const std::wstring* pref) { !home_page_is_new_tab_page_.IsManaged()); } +void BrowserToolbarGtk::ShowUpgradeReminder() { + upgrade_reminder_animation_.StartThrobbing(-1); +} + bool BrowserToolbarGtk::ShouldOnlyShowLocation() const { // If we're a popup window, only show the location bar (omnibox). return browser_->type() != Browser::TYPE_NORMAL; @@ -763,6 +792,7 @@ void BrowserToolbarGtk::PopupForButton(GtkWidget* button) { GTK_STATE_ACTIVE); MenuGtk* menu = button == page_menu_button_.get() ? page_menu_.get() : app_menu_.get(); + menu->PopupAsFromKeyEvent(button); menu_bar_helper_.MenuStartedShowing(button, menu->widget()); } @@ -773,3 +803,51 @@ void BrowserToolbarGtk::PopupForButtonNextTo(GtkWidget* button, app_menu_button_.get() : page_menu_button_.get(); PopupForButton(other_button); } + +void BrowserToolbarGtk::AnimationEnded(const Animation* animation) { + AnimationProgressed(animation); +} + +void BrowserToolbarGtk::AnimationProgressed(const Animation* animation) { + DCHECK_EQ(animation, &upgrade_reminder_animation_); + gtk_widget_queue_draw(app_menu_image_); +} + +void BrowserToolbarGtk::AnimationCanceled(const Animation* animation) { + AnimationProgressed(animation); +} + +void BrowserToolbarGtk::OnAppMenuShow(GtkWidget* sender) { + upgrade_reminder_animation_.Reset(); +} + +gboolean BrowserToolbarGtk::OnAppMenuImageExpose(GtkWidget* sender, + GdkEventExpose* expose) { + if (!Singleton::get()->notify_upgrade()) + return FALSE; + + SkBitmap badge; + if (upgrade_reminder_animation_.cycles_remaining() > 0 && + // This funky looking math makes the badge throb for 2 seconds once + // every 8 seconds. + ((upgrade_reminder_animation_.cycles_remaining() - 1) / 2) % 4 == 0) { + badge = SkBitmapOperations::CreateBlendedBitmap( + *theme_provider_->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE), + *theme_provider_->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE), + upgrade_reminder_animation_.GetCurrentValue()); + } else { + badge = *theme_provider_->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE); + } + + // Draw the chrome app menu icon onto the canvas. + gfx::CanvasPaint canvas(expose, false); + int x_offset = base::i18n::IsRTL() ? + sender->allocation.width - kUpgradeDotOffset - badge.width() : + kUpgradeDotOffset; + canvas.DrawBitmapInt( + badge, + sender->allocation.x + x_offset, + sender->allocation.y + sender->allocation.height - badge.height()); + + return FALSE; +} diff --git a/chrome/browser/gtk/browser_toolbar_gtk.h b/chrome/browser/gtk/browser_toolbar_gtk.h index 2627ac2d0094b2..26df479780eeb4 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.h +++ b/chrome/browser/gtk/browser_toolbar_gtk.h @@ -10,6 +10,7 @@ #include "app/gtk_signal.h" #include "app/menus/simple_menu_model.h" +#include "app/throb_animation.h" #include "base/scoped_ptr.h" #include "chrome/browser/app_menu_model.h" #include "chrome/browser/command_updater.h" @@ -43,7 +44,8 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, public menus::SimpleMenuModel::Delegate, public MenuGtk::Delegate, public NotificationObserver, - public MenuBarHelper::Delegate { + public MenuBarHelper::Delegate, + public AnimationDelegate { public: explicit BrowserToolbarGtk(Browser* browser, BrowserWindowGtk* window); virtual ~BrowserToolbarGtk(); @@ -116,6 +118,11 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, virtual void PopupForButtonNextTo(GtkWidget* button, GtkMenuDirectionType dir); + // AnimationDelegate implementation ------------------------------------------ + virtual void AnimationEnded(const Animation* animation); + virtual void AnimationProgressed(const Animation* animation); + virtual void AnimationCanceled(const Animation* animation); + private: // Builds a toolbar button with all the properties set. // |spacing| is the width of padding (in pixels) on the left and right of the @@ -165,12 +172,22 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, GdkDragContext*, gint, gint, GtkSelectionData*, guint, guint); + // Used to stop the upgrade notification animation. + CHROMEGTK_CALLBACK_0(BrowserToolbarGtk, void, OnAppMenuShow); + + // Used to draw the upgrade notification badge. + CHROMEGTK_CALLBACK_1(BrowserToolbarGtk, gboolean, OnAppMenuImageExpose, + GdkEventExpose*); + // ProfileSyncServiceObserver method. virtual void OnStateChanged(); // Updates preference-dependent state. void NotifyPrefChanged(const std::wstring* pref); + // Start the upgrade notification animation. + void ShowUpgradeReminder(); + static void SetSyncMenuLabel(GtkWidget* widget, gpointer userdata); // Sometimes we only want to show the location w/o the toolbar buttons (e.g., @@ -252,6 +269,8 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, // Manages the home button drop signal handler. scoped_ptr drop_handler_; + ThrobAnimation upgrade_reminder_animation_; + DISALLOW_COPY_AND_ASSIGN(BrowserToolbarGtk); }; diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index 5c32ca73d6f400..d7fbfb65ba75ac 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -65,6 +65,7 @@ #include "chrome/browser/gtk/tabs/tab_strip_gtk.h" #include "chrome/browser/gtk/task_manager_gtk.h" #include "chrome/browser/gtk/theme_install_bubble_view_gtk.h" +#include "chrome/browser/gtk/update_recommended_dialog.h" #include "chrome/browser/location_bar.h" #include "chrome/browser/page_info_window.h" #include "chrome/browser/pref_service.h" @@ -884,7 +885,7 @@ views::Window* BrowserWindowGtk::ShowAboutChromeDialog() { } void BrowserWindowGtk::ShowUpdateChromeDialog() { - NOTIMPLEMENTED(); + UpdateRecommendedDialog::Show(window_); } void BrowserWindowGtk::ShowTaskManager() { diff --git a/chrome/browser/gtk/update_recommended_dialog.cc b/chrome/browser/gtk/update_recommended_dialog.cc new file mode 100644 index 00000000000000..62646398ba5bf3 --- /dev/null +++ b/chrome/browser/gtk/update_recommended_dialog.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/update_recommended_dialog.h" + +#include "app/l10n_util.h" +#include "app/message_box_flags.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/gtk/gtk_util.h" +#include "chrome/browser/pref_service.h" +#include "chrome/common/pref_names.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" + +static const int kMessageWidth = 400; + +// static +void UpdateRecommendedDialog::Show(GtkWindow* parent) { + new UpdateRecommendedDialog(parent); +} + +UpdateRecommendedDialog::UpdateRecommendedDialog(GtkWindow* parent) { + dialog_ = gtk_dialog_new_with_buttons( + l10n_util::GetStringUTF8(IDS_PRODUCT_NAME).c_str(), + parent, + static_cast(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), + l10n_util::GetStringUTF8(IDS_RESTART_AND_UPDATE).c_str(), + GTK_RESPONSE_ACCEPT, + l10n_util::GetStringUTF8(IDS_NOT_NOW).c_str(), + GTK_RESPONSE_REJECT, + NULL); + + g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); + + // Create the content-holding vbox. + GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + gtk_container_set_border_width(GTK_CONTAINER(vbox), + gtk_util::kContentAreaBorder); + + // Add the message text. + std::string text( + l10n_util::GetStringFUTF8(IDS_UPDATE_RECOMMENDED, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + GtkWidget* label = gtk_label_new(text.c_str()); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_widget_set_size_request(label, kMessageWidth, -1); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + + // Add our vbox to the dialog. + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), vbox, + FALSE, FALSE, 0); + + gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE); + + gtk_widget_show_all(dialog_); +} + +UpdateRecommendedDialog::~UpdateRecommendedDialog() { +} + +void UpdateRecommendedDialog::OnResponse(GtkWidget* sender, gint response_id) { + gtk_widget_destroy(dialog_); + + if (response_id == GTK_RESPONSE_ACCEPT) { + // Set the flag to restore the last session on shutdown. + PrefService* pref_service = g_browser_process->local_state(); + pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); + + BrowserList::CloseAllBrowsersAndExit(); + } + + delete this; +} diff --git a/chrome/browser/gtk/update_recommended_dialog.h b/chrome/browser/gtk/update_recommended_dialog.h new file mode 100644 index 00000000000000..fd6ca4fc56da09 --- /dev/null +++ b/chrome/browser/gtk/update_recommended_dialog.h @@ -0,0 +1,27 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GTK_UPDATE_RECOMMENDED_DIALOG_H_ +#define CHROME_BROWSER_GTK_UPDATE_RECOMMENDED_DIALOG_H_ + +#include + +#include "app/gtk_signal.h" + +class UpdateRecommendedDialog { + public: + static void Show(GtkWindow* parent); + + private: + CHROMEGTK_CALLBACK_1(UpdateRecommendedDialog, void, OnResponse, gint); + + explicit UpdateRecommendedDialog(GtkWindow* parent); + ~UpdateRecommendedDialog(); + + GtkWidget* dialog_; + + DISALLOW_COPY_AND_ASSIGN(UpdateRecommendedDialog); +}; + +#endif // CHROME_BROWSER_GTK_UPDATE_RECOMMENDED_DIALOG_H_ diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc index 3c8735016aacfb..9927d7a6c2fd17 100644 --- a/chrome/browser/shell_integration_linux.cc +++ b/chrome/browser/shell_integration_linux.cc @@ -210,6 +210,8 @@ bool ShellIntegration::SetAsDefaultBrowser() { // static ShellIntegration::DefaultBrowserState ShellIntegration::IsDefaultBrowser() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + scoped_ptr env_getter(base::EnvVarGetter::Create()); std::vector argv; diff --git a/chrome/browser/upgrade_detector.cc b/chrome/browser/upgrade_detector.cc index 77f865af6c1728..ae92c99692bdc8 100644 --- a/chrome/browser/upgrade_detector.cc +++ b/chrome/browser/upgrade_detector.cc @@ -4,17 +4,27 @@ #include "chrome/browser/upgrade_detector.h" +#include "base/command_line.h" #include "base/file_version_info.h" #include "base/scoped_ptr.h" #include "base/time.h" +#include "base/task.h" +#include "base/utf_string_conversions.h" #include "base/version.h" #include "chrome/app/chrome_version_info.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/pref_service.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" #include "chrome/installer/util/browser_distribution.h" #if defined(OS_WIN) #include "chrome/installer/util/install_util.h" +#elif defined(OS_LINUX) +#include "base/process_util.h" +#include "chrome/installer/util/version.h" #endif // TODO(finnur): For the stable channel we want to check daily and notify @@ -23,69 +33,138 @@ // on how the feature works so I'm checking every hour and notifying the // user immediately. +namespace { + // How often to check for an upgrade. -static int kCheckForUpgradeEveryMs = 60 * 60 * 1000; // 1 hour. +const int kCheckForUpgradeEveryMs = 60 * 60 * 1000; // 1 hour. // How long to wait before notifying the user about the upgrade. -static int kNotifyUserAfterMs = 0; +const int kNotifyUserAfterMs = 0; + +// The thread to run the upgrade detection code on. We use FILE for Linux +// because we don't want to block the UI thread while launching a background +// process and reading its output. +const ChromeThread::ID kDetectUpgradeTaskID = +#if defined(OS_LINUX) + ChromeThread::FILE; +#else + ChromeThread::UI; +#endif -UpgradeDetector::UpgradeDetector() - : upgrade_detected_(false), - notify_upgrade_(false) { -#if !defined(OS_WIN) || !defined(GOOGLE_CHROME_BUILD) - return; +// This task checks the currently running version of Chrome against the +// installed version. If the installed version is newer, it runs the passed +// callback task. Otherwise it just deletes the task. +class DetectUpgradeTask : public Task { + public: + explicit DetectUpgradeTask(Task* upgrade_detected_task) + : upgrade_detected_task_(upgrade_detected_task) { + } + + virtual ~DetectUpgradeTask() { + if (upgrade_detected_task_) { + // This has to get deleted on the same thread it was created. + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + new DeleteTask(upgrade_detected_task_)); + } + } + + virtual void Run() { + DCHECK(ChromeThread::CurrentlyOn(kDetectUpgradeTaskID)); + +#if defined(OS_WIN) || defined(OS_LINUX) + using installer::Version; + +#if defined(OS_WIN) + // Get the version of the currently *installed* instance of Chrome, + // which might be newer than the *running* instance if we have been + // upgraded in the background. + Version* installed_version = InstallUtil::GetChromeVersion(false); + if (!installed_version) { + // User level Chrome is not installed, check system level. + installed_version = InstallUtil::GetChromeVersion(true); + } +#elif defined(OS_LINUX) + CommandLine command_line(*CommandLine::ForCurrentProcess()); + command_line.AppendSwitch(switches::kProductVersion); + std::string reply; + if (!base::GetAppOutput(command_line, &reply)) { + DLOG(ERROR) << "Failed to get current file version"; + return; + } + + scoped_ptr installed_version( + Version::GetVersionFromString(ASCIIToUTF16(reply))); +#endif + + // Get the version of the currently *running* instance of Chrome. + scoped_ptr version(chrome_app::GetChromeVersionInfo()); + if (version.get() == NULL) { + NOTREACHED() << "Failed to get current file version"; + return; + } + scoped_ptr running_version(Version::GetVersionFromString( + WideToUTF16(version->file_version()))); + + if (installed_version->IsHigherThan(running_version.get())) { + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, + upgrade_detected_task_); + upgrade_detected_task_ = NULL; + } +#else // !(defined(OS_WIN) || defined(OS_LINUX)) + DCHECK(kNotifyUserAfterMs >= 0); // Avoid error: var defined but not used. + NOTIMPLEMENTED(); #endif + } + + private: + Task* upgrade_detected_task_; +}; + +} // namespace +// static +void UpgradeDetector::RegisterPrefs(PrefService* prefs) { + prefs->RegisterBooleanPref(prefs::kRestartLastSessionOnShutdown, false); +} + +UpgradeDetector::UpgradeDetector() + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + notify_upgrade_(false) { + // Upgrade notifications work on Windows (only Google Chrome) and Linux + // (chromium and Google Chrome). +#if (defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)) || defined(OS_LINUX) detect_upgrade_timer_.Start( base::TimeDelta::FromMilliseconds(kCheckForUpgradeEveryMs), this, &UpgradeDetector::CheckForUpgrade); +#endif } UpgradeDetector::~UpgradeDetector() { } void UpgradeDetector::CheckForUpgrade() { -#if defined(OS_WIN) - using installer::Version; - - // Get the version of the currently *installed* instance of Chrome, - // which might be newer than the *running* instance if we have been - // upgraded in the background. - Version* installed_version = InstallUtil::GetChromeVersion(false); - if (!installed_version) { - // User level Chrome is not installed, check system level. - installed_version = InstallUtil::GetChromeVersion(true); - } - - // Get the version of the currently *running* instance of Chrome. - scoped_ptr version(chrome_app::GetChromeVersionInfo()); - if (version.get() == NULL) { - NOTREACHED() << L"Failed to get current file version"; - return; - } - scoped_ptr running_version(Version::GetVersionFromString( - version->file_version())); + method_factory_.RevokeAll(); + Task* callback_task = + method_factory_.NewRunnableMethod(&UpgradeDetector::UpgradeDetected); + ChromeThread::PostTask(kDetectUpgradeTaskID, FROM_HERE, + new DetectUpgradeTask(callback_task)); +} - if (installed_version->IsHigherThan(running_version.get())) { - // Stop the recurring timer (that is checking for changes). - detect_upgrade_timer_.Stop(); +void UpgradeDetector::UpgradeDetected() { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - upgrade_detected_ = true; + // Stop the recurring timer (that is checking for changes). + detect_upgrade_timer_.Stop(); - NotificationService::current()->Notify( - NotificationType::UPGRADE_DETECTED, - Source(this), - NotificationService::NoDetails()); + NotificationService::current()->Notify( + NotificationType::UPGRADE_DETECTED, + Source(this), + NotificationService::NoDetails()); - // Start the OneShot timer for notifying the user after a certain period. - upgrade_notification_timer_.Start( - base::TimeDelta::FromMilliseconds(kNotifyUserAfterMs), - this, &UpgradeDetector::NotifyOnUpgrade); - } -#else - DCHECK(kNotifyUserAfterMs > 0); // Avoid error: var defined but not used. - NOTIMPLEMENTED(); -#endif + // Start the OneShot timer for notifying the user after a certain period. + upgrade_notification_timer_.Start( + base::TimeDelta::FromMilliseconds(kNotifyUserAfterMs), + this, &UpgradeDetector::NotifyOnUpgrade); } void UpgradeDetector::NotifyOnUpgrade() { diff --git a/chrome/browser/upgrade_detector.h b/chrome/browser/upgrade_detector.h index 9a7da11f362768..036d43c3df4d20 100644 --- a/chrome/browser/upgrade_detector.h +++ b/chrome/browser/upgrade_detector.h @@ -8,6 +8,8 @@ #include "base/singleton.h" #include "base/timer.h" +class PrefService; + /////////////////////////////////////////////////////////////////////////////// // UpgradeDetector // @@ -22,17 +24,21 @@ class UpgradeDetector { public: ~UpgradeDetector(); + static void RegisterPrefs(PrefService* prefs); + bool notify_upgrade() { return notify_upgrade_; } private: UpgradeDetector(); friend struct DefaultSingletonTraits; - // Checks with Omaha if we have the latest version. If not, sends out a - // notification and starts a one shot timer to wait until notifying the - // user. + // Launches a task on the file thread to check if we have the latest version. void CheckForUpgrade(); + // Sends out a notification and starts a one shot timer to wait until + // notifying the user. + void UpgradeDetected(); + // The function that sends out a notification (after a certain time has // elapsed) that lets the rest of the UI know we should start notifying the // user that a new version is available. @@ -44,8 +50,10 @@ class UpgradeDetector { // After we detect an upgrade we wait a set time before notifying the user. base::OneShotTimer upgrade_notification_timer_; - // Whether we have detected an upgrade happening while we were running. - bool upgrade_detected_; + // We use this factory to create callback tasks for UpgradeDetected. We pass + // the task to the actual upgrade detection code, which is in + // DetectUpgradeTask. + ScopedRunnableMethodFactory method_factory_; // Whether we have waited long enough after detecting an upgrade (to see // is we should start nagging about upgrading). diff --git a/chrome/browser/views/update_recommended_message_box.cc b/chrome/browser/views/update_recommended_message_box.cc index dc1bf05e3512c5..5e66b72b28ff9d 100644 --- a/chrome/browser/views/update_recommended_message_box.cc +++ b/chrome/browser/views/update_recommended_message_box.cc @@ -25,11 +25,6 @@ void UpdateRecommendedMessageBox::ShowMessageBox( new UpdateRecommendedMessageBox(parent_window); } -void UpdateRecommendedMessageBox::RegisterUpdateRecommendedPrefs( - PrefService* prefs) { - prefs->RegisterBooleanPref(prefs::kRestartLastSessionOnShutdown, false); -} - bool UpdateRecommendedMessageBox::Accept() { // Set the flag to restore the last session on shutdown. PrefService* pref_service = g_browser_process->local_state(); diff --git a/chrome/browser/views/update_recommended_message_box.h b/chrome/browser/views/update_recommended_message_box.h index 9751db4ee994c1..e5e39e212a254e 100644 --- a/chrome/browser/views/update_recommended_message_box.h +++ b/chrome/browser/views/update_recommended_message_box.h @@ -10,7 +10,6 @@ #include "views/window/dialog_delegate.h" class MessageBoxView; -class PrefService; // A dialog box that tells the user that an update is recommended in order for // the latest version to be put to use. @@ -19,9 +18,6 @@ class UpdateRecommendedMessageBox : public views::DialogDelegate { // This box is modal to |parent_window|. static void ShowMessageBox(gfx::NativeWindow parent_window); - // Register preferences specific to this view. - static void RegisterUpdateRecommendedPrefs(PrefService* prefs); - // Overridden from views::DialogDelegate: virtual bool Accept(); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 2474041d864d9f..d5dc4d66e7daec 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1457,6 +1457,8 @@ 'browser/gtk/theme_install_bubble_view_gtk.h', 'browser/gtk/translate_infobars.cc', 'browser/gtk/translate_infobars.h', + 'browser/gtk/update_recommended_dialog.cc', + 'browser/gtk/update_recommended_dialog.h', 'browser/gtk/view_id_util.cc', 'browser/gtk/view_id_util.h', 'browser/hang_monitor/hung_plugin_action.cc', diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi index ed1d35ecedf091..f36a559e9fe28e 100644 --- a/chrome/chrome_installer_util.gypi +++ b/chrome/chrome_installer_util.gypi @@ -143,6 +143,8 @@ 'installer/util/master_preferences.h', 'installer/util/master_preferences_constants.cc', 'installer/util/master_preferences_constants.h', + 'installer/util/version.cc', + 'installer/util/version.h', ], 'include_dirs': [ '<(DEPTH)', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 4690749d5a22e5..491d6e55407a52 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -830,6 +830,10 @@ const char kUtilityProcess[] = "utility"; // specifies the directory that can be accessed. const char kUtilityProcessAllowedDir[] = "utility-allowed-dir"; +// Output the product version information and quit. Used as an internal api to +// detect the installed version of Chrome on Linux. +const char kProductVersion[] = "product-version"; + // Will add kWaitForDebugger to every child processes. If a value is passed, it // will be used as a filter to determine if the child process should have the // kWaitForDebugger flag passed on or not. diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index b4df9c9be6e19d..585293538cd72b 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -234,6 +234,7 @@ extern const char kUserScriptsDir[]; extern const char kUtilityCmdPrefix[]; extern const char kUtilityProcess[]; extern const char kUtilityProcessAllowedDir[]; +extern const char kProductVersion[]; extern const char kWaitForDebuggerChildren[]; extern const char kWebWorkerProcessPerCore[]; extern const char kWebWorkerShareProcesses[]; diff --git a/chrome/installer/util/version.cc b/chrome/installer/util/version.cc index 0bd94094857209..2e580763de4413 100644 --- a/chrome/installer/util/version.cc +++ b/chrome/installer/util/version.cc @@ -4,19 +4,19 @@ #include +#include "base/format_macros.h" #include "base/string_util.h" #include "chrome/installer/util/version.h" installer::Version::Version(int64 major, int64 minor, int64 build, - int64 patch) : - major_(major), minor_(minor), build_(build), patch_(patch) { - version_str_.append(Int64ToWString(major_)); - version_str_.append(L"."); - version_str_.append(Int64ToWString(minor_)); - version_str_.append(L"."); - version_str_.append(Int64ToWString(build_)); - version_str_.append(L"."); - version_str_.append(Int64ToWString(patch_)); + int64 patch) + : major_(major), + minor_(minor), + build_(build), + patch_(patch) { + version_str_ = ASCIIToUTF16( + StringPrintf("%" PRId64 ".%" PRId64 ".%" PRId64 ".%" PRId64, + major_, minor_, build_, patch_)); } installer::Version::~Version() { @@ -33,8 +33,8 @@ bool installer::Version::IsHigherThan(const installer::Version* other) const { } installer::Version* installer::Version::GetVersionFromString( - const std::wstring& version_str) { - std::vector numbers; + const string16& version_str) { + std::vector numbers; SplitString(version_str, '.', &numbers); if (numbers.size() != 4) { diff --git a/chrome/installer/util/version.h b/chrome/installer/util/version.h index 1f02e00ebac661..980fe056d14b1c 100644 --- a/chrome/installer/util/version.h +++ b/chrome/installer/util/version.h @@ -5,9 +5,8 @@ #ifndef CHROME_INSTALLER_UTIL_VERSION_H_ #define CHROME_INSTALLER_UTIL_VERSION_H_ -#include - #include "base/basictypes.h" +#include "base/string16.h" namespace installer { @@ -20,21 +19,21 @@ class Version { bool IsHigherThan(const Version* other) const; // Return the string representation of this version - const std::wstring& GetString() const { + const string16& GetString() const { return version_str_; } // Assume that the version string is specified by four integers separated // by character '.'. Return NULL if string is not of this format. // Caller is responsible for freeing the Version object once done. - static Version* GetVersionFromString(const std::wstring& version_str); + static Version* GetVersionFromString(const string16& version_str); private: int64 major_; int64 minor_; int64 build_; int64 patch_; - std::wstring version_str_; + string16 version_str_; // Classes outside this file do not have any need to create objects of // this type so declare constructor as private.