diff options
author | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-26 20:11:54 +0000 |
---|---|---|
committer | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-26 20:11:54 +0000 |
commit | b1b7394fa3880dab9f9bd0cfc40ea8c614f0b49e (patch) | |
tree | d6e9f57d10714b67c4c7fbed0e874d4e337f406e | |
parent | a8e4a8fa39604cd309e1e3d62cf9c552dcfe541d (diff) | |
download | chromium_src-b1b7394fa3880dab9f9bd0cfc40ea8c614f0b49e.zip chromium_src-b1b7394fa3880dab9f9bd0cfc40ea8c614f0b49e.tar.gz chromium_src-b1b7394fa3880dab9f9bd0cfc40ea8c614f0b49e.tar.bz2 |
Implement upgrade notifications.
When we detect that the installed version is newer than the version you are
running we show a little throbbing orange dot over the wrench menu.
If you open the wrench menu and close it again, the throbbing will stop.
However, if you look at the contents of the wrench menu you'll notice that
the About box menu item has been removed and in its place is a menu item
"Update Chrome Now" with a bright orange icon to draw your attention to it.
Clicking on the icon shows a dialog box asking whether you want to restart
Chrome. If you do, the browser restarts with your session restored
(even if you have Session Restore turned off).
Known issues:
- Currently this is Windows only. We'll have to port this to Linux and do
something differnet for Mac (which doesn't have the wrench menu).
- Showing an icon in front of Update Chrome causes the checkbox for the
bookmark bar menu to go away. Given that we will soon redesign the menus I'm
not going to spend much time trying to fix it.
BUG=27941
TEST=Wait for Chrome to be upgraded in the background, an orange dot should
appear over the wrench menu and if you select Update Chrome your session should
be retained.
Review URL: http://codereview.chromium.org/2225003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@48318 0039d316-1c4b-4281-b951-d872f2087c98
26 files changed, 586 insertions, 20 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 814e228..1ccfad1 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -985,6 +985,9 @@ each locale. --> <message name="IDS_ABOUT" desc="The text label of the About Chrome menu item"> About &<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> </message> + <message name="IDS_UPDATE_NOW" desc="The text label of the Update Chrome Now menu item"> + Update &<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> + </message> <message name="IDS_EXIT" desc="The text label of the Exit menu item"> E&xit </message> @@ -1020,6 +1023,9 @@ each locale. --> <message name="IDS_ABOUT" desc="In Title Case: The text label of the About Chrome menu item"> About &<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> </message> + <message name="IDS_UPDATE_NOW" desc="The text label of the Update Chrome Now menu item"> + Update &<ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> + </message> <message name="IDS_EXIT" desc="In Title Case: The text label of the Exit menu item"> E&xit </message> @@ -7210,6 +7216,17 @@ Keep your key file in a safe place. You will need it to create new versions of y Loading... </message> + <!-- Update Recommended dialog --> + <message name="IDS_UPDATE_RECOMMENDED" desc="The main text of the Update Recommended dialog."> + Old school's not cool. <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> is woefully out of date because it hasn't crashed or restarted in at least two weeks. Restart to apply update. + </message> + <message name="IDS_RESTART_AND_UPDATE" desc="The button label for restarting and updating Chrome."> + Restart and update + </message> + <message name="IDS_NOT_NOW" desc="The button label for delaying the restart and updating Chrome."> + Not now + </message> + <!-- Extra Mac UI Strings --> <if expr="os == 'darwin'"> <message name="IDS_PLEASE_RESTART_BROWSER" desc="Title of the alert when Chrome needs to be restart for a change/update to take effect."> diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index 126b546..5c6b2a1 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd @@ -345,6 +345,11 @@ <include name="IDR_SIDETABS_SHADOW_MIDDLE" file="sidetabs_shadow_middle.png" type="BINDATA" /> <include name="IDR_SIDETABS_SHADOW_BOTTOM" file="sidetabs_shadow_bottom.png" type="BINDATA" /> + <!-- Upgrade notification --> + + <include name="IDR_UPGRADE_DOT_INACTIVE" file="upgrade_dot_inactive.png" type="BINDATA" /> + <include name="IDR_UPGRADE_DOT_ACTIVE" file="upgrade_dot_active.png" type="BINDATA" /> + <!-- Geolocation --> <include name="IDR_GEOLOCATION_INFOBAR_ICON" file="geolocation_infobar_icon.png" type="BINDATA" /> <include name="IDR_GEOLOCATION_ALLOWED_LOCATIONBAR_ICON" file="geolocation_allowed_locationbar_icon.png" type="BINDATA" /> diff --git a/chrome/browser/app_menu_model.cc b/chrome/browser/app_menu_model.cc index a4cb383..ac418b7 100644 --- a/chrome/browser/app_menu_model.cc +++ b/chrome/browser/app_menu_model.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "app/l10n_util.h" +#include "app/resource_bundle.h" #include "base/command_line.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/browser.h" @@ -14,9 +15,11 @@ #include "chrome/browser/profile.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/sync_ui_util.h" +#include "chrome/browser/upgrade_detector.h" #include "chrome/common/chrome_switches.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "grit/theme_resources.h" AppMenuModel::AppMenuModel(menus::SimpleMenuModel::Delegate* delegate, Browser* browser) @@ -29,12 +32,33 @@ AppMenuModel::~AppMenuModel() { } bool AppMenuModel::IsLabelDynamicAt(int index) const { - return IsSyncItem(index) || SimpleMenuModel::IsLabelDynamicAt(index); + return IsDynamicItem(index) || SimpleMenuModel::IsLabelDynamicAt(index); } string16 AppMenuModel::GetLabelAt(int index) const { - return IsSyncItem(index) ? - GetSyncMenuLabel() : SimpleMenuModel::GetLabelAt(index); + if (!IsDynamicItem(index)) + return SimpleMenuModel::GetLabelAt(index); + + int command_id = GetCommandIdAt(index); + + switch (command_id) { + case IDC_ABOUT: return GetAboutEntryMenuLabel(); break; + case IDC_SYNC_BOOKMARKS: return GetSyncMenuLabel(); break; + default: + NOTREACHED(); + return string16(); + } +} + +bool AppMenuModel::GetIconAt(int index, SkBitmap* icon) const { + if (GetCommandIdAt(index) == IDC_ABOUT && + Singleton<UpgradeDetector>::get()->notify_upgrade()) { + // Show the exclamation point next to the menu item. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + *icon = *rb.GetBitmapNamed(IDR_UPDATE_AVAILABLE); + return true; + } + return false; } void AppMenuModel::Build() { @@ -160,6 +184,17 @@ string16 AppMenuModel::GetSyncMenuLabel() const { browser_->profile()->GetOriginalProfile()->GetProfileSyncService()); } -bool AppMenuModel::IsSyncItem(int index) const { - return GetCommandIdAt(index) == IDC_SYNC_BOOKMARKS; +string16 AppMenuModel::GetAboutEntryMenuLabel() const { + if (Singleton<UpgradeDetector>::get()->notify_upgrade()) { + return l10n_util::GetStringFUTF16( + IDS_UPDATE_NOW, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); + } + return l10n_util::GetStringFUTF16( + IDS_ABOUT, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); +} + +bool AppMenuModel::IsDynamicItem(int index) const { + int command_id = GetCommandIdAt(index); + return command_id == IDC_SYNC_BOOKMARKS || + command_id == IDC_ABOUT; } diff --git a/chrome/browser/app_menu_model.h b/chrome/browser/app_menu_model.h index b0daac7..962368e 100644 --- a/chrome/browser/app_menu_model.h +++ b/chrome/browser/app_menu_model.h @@ -22,10 +22,11 @@ class AppMenuModel : public menus::SimpleMenuModel { Browser* browser); virtual ~AppMenuModel(); - // Override this to handle the sync menu item (whose label is - // updated dynamically). + // Overridden from menus::SimpleMenuModel: virtual bool IsLabelDynamicAt(int index) const; virtual string16 GetLabelAt(int index) const; + virtual bool HasIcons() const { return true; } + virtual bool GetIconAt(int index, SkBitmap* icon) const; // Build/update profile submenu. Return true if profiles submenu is built or // updated. False otherwise. @@ -37,7 +38,8 @@ class AppMenuModel : public menus::SimpleMenuModel { bool ProfilesChanged(const std::vector<std::wstring>& profiles) const; string16 GetSyncMenuLabel() const; - bool IsSyncItem(int index) const; + string16 GetAboutEntryMenuLabel() const; + bool IsDynamicItem(int index) const; // Contents of the profiles menu to populate with profile names. scoped_ptr<menus::SimpleMenuModel> profiles_menu_contents_; diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index e2b2ed3..e25da85 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -74,6 +74,7 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_contents_view.h" #include "chrome/browser/tab_menu_model.h" +#include "chrome/browser/upgrade_detector.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/browser/window_sizer.h" #include "chrome/common/chrome_constants.h" @@ -129,7 +130,6 @@ static const std::string kBrokenPageUrl = "http://www.google.com/support/chrome/bin/request.py?contact_type=" "broken_website&format=inproduct&p.page_title=$1&p.page_url=$2"; - /////////////////////////////////////////////////////////////////////////////// namespace { @@ -1748,6 +1748,11 @@ void Browser::OpenAboutChromeDialog() { window_->ShowAboutChromeDialog(); } +void Browser::OpenUpdateChromeDialog() { + UserMetrics::RecordAction(UserMetricsAction("UpdateChrome"), profile_); + window_->ShowUpdateChromeDialog(); +} + void Browser::OpenHelpTab() { GURL help_url = google_util::AppendGoogleLocaleParam(GURL(kHelpContentUrl)); AddTabWithURL(help_url, GURL(), PageTransition::AUTO_BOOKMARK, @@ -2025,7 +2030,12 @@ void Browser::ExecuteCommandWithDisposition( case IDC_VIEW_PASSWORDS: OpenPasswordManager(); break; case IDC_CLEAR_BROWSING_DATA: OpenClearBrowsingDataDialog(); break; case IDC_IMPORT_SETTINGS: OpenImportSettingsDialog(); break; - case IDC_ABOUT: OpenAboutChromeDialog(); break; + case IDC_ABOUT: + if (Singleton<UpgradeDetector>::get()->notify_upgrade()) + OpenUpdateChromeDialog(); + else + OpenAboutChromeDialog(); + break; case IDC_HELP_PAGE: OpenHelpTab(); break; #if defined(OS_CHROMEOS) case IDC_SYSTEM_OPTIONS: OpenSystemOptionsDialog(); break; diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index 252e9f1..4fe3694 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -532,6 +532,7 @@ class Browser : public TabStripModelDelegate, void OpenSyncMyBookmarksDialog(); void OpenImportSettingsDialog(); void OpenAboutChromeDialog(); + void OpenUpdateChromeDialog(); void OpenHelpTab(); // Used by the "Get themes" link in the options dialog. void OpenThemeGalleryTabAndActivate(); diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc index 891d1f5..1dc0463 100644 --- a/chrome/browser/browser_prefs.cc +++ b/chrome/browser/browser_prefs.cc @@ -43,6 +43,7 @@ #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) @@ -79,6 +80,9 @@ void RegisterLocalState(PrefService* local_state) { #if defined(TOOLKIT_VIEWS) BrowserView::RegisterBrowserViewPrefs(local_state); #endif +#if defined(OS_WIN) + UpdateRecommendedMessageBox::RegisterUpdateRecommendedPrefs(local_state); +#endif 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 c052790..af5bc26 100644 --- a/chrome/browser/browser_shutdown.cc +++ b/chrome/browser/browser_shutdown.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -28,6 +28,7 @@ #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/chrome_plugin_lib.h" #include "net/dns_global.h" @@ -128,6 +129,14 @@ void Shutdown() { shutdown_num_processes_slow_); } + // Check local state for the restart flag so we can restart the session below. + bool restart_last_session = false; + if (prefs->HasPrefPath(prefs::kRestartLastSessionOnShutdown)) { + restart_last_session = + prefs->GetBoolean(prefs::kRestartLastSessionOnShutdown); + prefs->ClearPref(prefs::kRestartLastSessionOnShutdown); + } + prefs->SavePersistentPrefs(); #if defined(OS_WIN) @@ -152,6 +161,20 @@ void Shutdown() { shutdown_type_ != browser_shutdown::END_SESSION) { Upgrade::SwapNewChromeExeIfPresent(); } + + if (restart_last_session) { + // 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()); + if (!command_line.HasSwitch(switches::kRestoreLastSession)) + command_line.AppendSwitch(switches::kRestoreLastSession); + Upgrade::RelaunchChromeBrowser(command_line); + } +#endif +#if !defined(OS_WIN) + if (restart_last_session) + NOTIMPLEMENTED(); #endif if (shutdown_type_ > NOT_VALID && shutdown_num_processes_ > 0) { diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h index ad10784..93ae25f 100644 --- a/chrome/browser/browser_window.h +++ b/chrome/browser/browser_window.h @@ -183,6 +183,9 @@ class BrowserWindow { // Shows the About Chrome dialog box. virtual views::Window* ShowAboutChromeDialog() = 0; + // Shows the Update Recommended dialog box. + virtual void ShowUpdateChromeDialog() = 0; + // Shows the Task manager. virtual void ShowTaskManager() = 0; diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h index b50feb9..91fb085 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.h +++ b/chrome/browser/cocoa/browser_window_cocoa.h @@ -67,6 +67,7 @@ class BrowserWindowCocoa : public BrowserWindow, virtual void ToggleBookmarkBar(); virtual void ToggleExtensionShelf(); virtual views::Window* ShowAboutChromeDialog(); + virtual void ShowUpdateChromeDialog(); virtual void ShowTaskManager(); virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked); virtual bool IsDownloadShelfVisible() const; diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index 821e35f..58a62bc 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -259,6 +259,10 @@ views::Window* BrowserWindowCocoa::ShowAboutChromeDialog() { return NULL; } +void BrowserWindowCocoa::ShowUpdateChromeDialog() { + NOTIMPLEMENTED(); +} + void BrowserWindowCocoa::ShowTaskManager() { TaskManagerMac::Show(); } diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index e5ac25e..5c32ca7 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -883,6 +883,10 @@ views::Window* BrowserWindowGtk::ShowAboutChromeDialog() { return NULL; } +void BrowserWindowGtk::ShowUpdateChromeDialog() { + NOTIMPLEMENTED(); +} + void BrowserWindowGtk::ShowTaskManager() { TaskManagerGtk::Show(); } diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h index 3404f3b..fdbf27c 100644 --- a/chrome/browser/gtk/browser_window_gtk.h +++ b/chrome/browser/gtk/browser_window_gtk.h @@ -85,6 +85,7 @@ class BrowserWindowGtk : public BrowserWindow, virtual void ToggleBookmarkBar(); virtual void ToggleExtensionShelf(); virtual views::Window* ShowAboutChromeDialog(); + virtual void ShowUpdateChromeDialog(); virtual void ShowTaskManager(); virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked); virtual bool IsDownloadShelfVisible() const; diff --git a/chrome/browser/upgrade_detector.cc b/chrome/browser/upgrade_detector.cc new file mode 100644 index 0000000..3aedb9a --- /dev/null +++ b/chrome/browser/upgrade_detector.cc @@ -0,0 +1,96 @@ +// 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/upgrade_detector.h" + +#include "base/file_version_info.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "base/version.h" +#include "chrome/app/chrome_version_info.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/installer/util/browser_distribution.h" + +#if defined(OS_WIN) +#include "chrome/installer/util/install_util.h" +#endif + +// TODO(finnur): For the stable channel we want to check daily and notify +// the user if more than 2 weeks have passed since the upgrade happened +// (without a reboot). For the dev channel however, I want quicker feedback +// on how the feature works so I'm checking every hour and notifying the +// user immediately. + +// How often to check for an upgrade. +static int kCheckForUpgradeEveryMs = 60 * 60 * 1000; // 1 hour. + +// How long to wait before notifying the user about the upgrade. +static int kNotifyUserAfterMs = 0; + +UpgradeDetector::UpgradeDetector() : upgrade_detected_(false) { +#if !defined(OS_WIN) + return; +#endif + + detect_upgrade_timer_.Start( + base::TimeDelta::FromMilliseconds(kCheckForUpgradeEveryMs), + this, &UpgradeDetector::CheckForUpgrade); +} + +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<FileVersionInfo> version(chrome_app::GetChromeVersionInfo()); + if (version.get() == NULL) { + NOTREACHED() << L"Failed to get current file version"; + return; + } + scoped_ptr<Version> running_version(Version::GetVersionFromString( + version->file_version())); + + if (installed_version->IsHigherThan(running_version.get())) { + // Stop the recurring timer (that is checking for changes). + detect_upgrade_timer_.Stop(); + + upgrade_detected_ = true; + + NotificationService::current()->Notify( + NotificationType::UPGRADE_DETECTED, + Source<UpgradeDetector>(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 +} + +void UpgradeDetector::NotifyOnUpgrade() { + notify_upgrade_ = true; + + NotificationService::current()->Notify( + NotificationType::UPGRADE_RECOMMENDED, + Source<UpgradeDetector>(this), + NotificationService::NoDetails()); +} diff --git a/chrome/browser/upgrade_detector.h b/chrome/browser/upgrade_detector.h new file mode 100644 index 0000000..9a7da11 --- /dev/null +++ b/chrome/browser/upgrade_detector.h @@ -0,0 +1,57 @@ +// 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_UPGRADE_DETECTOR_H_ +#define CHROME_BROWSER_UPGRADE_DETECTOR_H_ + +#include "base/singleton.h" +#include "base/timer.h" + +/////////////////////////////////////////////////////////////////////////////// +// UpgradeDetector +// +// This class is a singleton class that monitors when an upgrade happens in the +// background. We basically ask Omaha what it thinks the latest version is and +// if our version is lower we send out a notification upon: +// a) Detecting an upgrade and... +// b) When we think the user should be notified about the upgrade. +// The latter happens much later, since we don't want to be too annoying. +// +class UpgradeDetector { + public: + ~UpgradeDetector(); + + bool notify_upgrade() { return notify_upgrade_; } + + private: + UpgradeDetector(); + friend struct DefaultSingletonTraits<UpgradeDetector>; + + // 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. + void CheckForUpgrade(); + + // 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. + void NotifyOnUpgrade(); + + // We periodically check to see if Chrome has been upgraded. + base::RepeatingTimer<UpgradeDetector> detect_upgrade_timer_; + + // After we detect an upgrade we wait a set time before notifying the user. + base::OneShotTimer<UpgradeDetector> upgrade_notification_timer_; + + // Whether we have detected an upgrade happening while we were running. + bool upgrade_detected_; + + // Whether we have waited long enough after detecting an upgrade (to see + // is we should start nagging about upgrading). + bool notify_upgrade_; + + DISALLOW_COPY_AND_ASSIGN(UpgradeDetector); +}; + +#endif // CHROME_BROWSER_UPGRADE_DETECTOR_H_ diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index 2d36e9c..3dc71af 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -42,6 +42,7 @@ #include "chrome/browser/views/tabs/side_tab_strip.h" #include "chrome/browser/views/theme_install_bubble_view.h" #include "chrome/browser/views/toolbar_view.h" +#include "chrome/browser/views/update_recommended_message_box.h" #include "chrome/browser/window_sizer.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_resource.h" @@ -966,6 +967,12 @@ views::Window* BrowserView::ShowAboutChromeDialog() { browser_->profile()); } +void BrowserView::ShowUpdateChromeDialog() { +#if defined(OS_WIN) + UpdateRecommendedMessageBox::ShowMessageBox(GetWindow()->GetNativeWindow()); +#endif +} + void BrowserView::ShowTaskManager() { browser::ShowTaskManager(); } @@ -980,9 +987,10 @@ void BrowserView::SetDownloadShelfVisible(bool visible) { if (browser_ == NULL) return; - if (visible && IsDownloadShelfVisible() != visible) + if (visible && IsDownloadShelfVisible() != visible) { // Invoke GetDownloadShelf to force the shelf to be created. GetDownloadShelf(); + } if (browser_ != NULL) browser_->UpdateDownloadShelfVisibility(visible); diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h index 0f2f12c..2d62c77 100644 --- a/chrome/browser/views/frame/browser_view.h +++ b/chrome/browser/views/frame/browser_view.h @@ -295,6 +295,7 @@ class BrowserView : public BrowserBubbleHost, virtual void ToggleBookmarkBar(); virtual void ToggleExtensionShelf(); virtual views::Window* ShowAboutChromeDialog(); + virtual void ShowUpdateChromeDialog(); virtual void ShowTaskManager(); virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked); virtual void SetDownloadShelfVisible(bool visible); @@ -597,9 +598,9 @@ class BrowserView : public BrowserBubbleHost, UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; scoped_ptr<AccessibleViewHelper> accessible_view_helper_; - #if defined(OS_LINUX) +#if defined(OS_LINUX) scoped_ptr<AccessibleWidgetHelper> accessible_widget_helper_; - #endif +#endif // Loads extension_app_icon_ asynchronously on the file thread. ImageLoadingTracker extension_app_icon_loader_; diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc index cc00ba5..4d15f5c 100644 --- a/chrome/browser/views/toolbar_view.cc +++ b/chrome/browser/views/toolbar_view.cc @@ -13,6 +13,7 @@ #include "chrome/browser/browser_window.h" #include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" +#include "chrome/browser/upgrade_detector.h" #include "chrome/browser/view_ids.h" #include "chrome/browser/views/bookmark_menu_button.h" #include "chrome/browser/views/browser_actions_container.h" @@ -24,6 +25,7 @@ #include "gfx/canvas.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "gfx/skbitmap_operations.h" #include "grit/theme_resources.h" #include "views/controls/button/button_dropdown.h" #include "views/focus/view_storage.h" @@ -36,6 +38,16 @@ static const int kControlVertOffset = 6; static const int kControlIndent = 3; static const int kStatusBubbleWidth = 480; +// The length of time to run the upgrade notification animation (the time it +// takes one pulse to run its course and go back to its original brightness). +static const int kPulseDuration = 2000; + +// How long to wait between pulsating the upgrade notifier. +static const int kPulsateEveryMs = 8000; + +// The offset in pixels of the upgrade dot on the app menu. +static const int kUpgradeDotOffset = 11; + // Separation between the location bar and the menus. static const int kMenuButtonOffset = 3; @@ -89,6 +101,12 @@ ToolbarView::ToolbarView(Browser* browser) kPopupBackgroundEdge = ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_LOCATIONBG_POPUPMODE_EDGE); } + + if (!Singleton<UpgradeDetector>::get()->notify_upgrade()) { + registrar_.Add(this, + NotificationType::UPGRADE_RECOMMENDED, + NotificationService::AllSources()); + } } ToolbarView::~ToolbarView() { @@ -280,6 +298,14 @@ void ToolbarView::OnInputInProgress(bool in_progress) { } //////////////////////////////////////////////////////////////////////////////// +// ToolbarView, AnimationDelegate implementation: + +void ToolbarView::AnimationProgressed(const Animation* animation) { + app_menu_->SetIcon(GetAppMenuIcon()); + SchedulePaint(); +} + +//////////////////////////////////////////////////////////////////////////////// // ToolbarView, CommandUpdater::CommandObserver implementation: void ToolbarView::EnabledStateChangedForCommand(int id, bool enabled) { @@ -340,6 +366,8 @@ void ToolbarView::Observe(NotificationType type, Layout(); SchedulePaint(); } + } else if (type == NotificationType::UPGRADE_RECOMMENDED) { + ShowUpgradeReminder(); } } @@ -665,6 +693,10 @@ void ToolbarView::CreateRightSideControls(Profile* profile) { bookmark_menu_ = NULL; } + // Catch the case where the window is created after we detect a new version. + if (Singleton<UpgradeDetector>::get()->notify_upgrade()) + ShowUpgradeReminder(); + LoadRightSideControlsImages(); AddChildView(browser_actions_); @@ -734,6 +766,77 @@ void ToolbarView::LoadCenterStackImages() { tp->GetBitmapNamed(IDR_GO_MASK)); } +void ToolbarView::ShowUpgradeReminder() { + update_reminder_animation_.reset(new SlideAnimation(this)); + update_reminder_animation_->SetSlideDuration(kPulseDuration); + + // Then start the recurring timer for pulsating it. + upgrade_reminder_pulse_timer_.Start( + base::TimeDelta::FromMilliseconds(kPulsateEveryMs), + this, &ToolbarView::PulsateUpgradeNotifier); +} + +void ToolbarView::PulsateUpgradeNotifier() { + // Start the pulsating animation. + update_reminder_animation_->Reset(0.0); + update_reminder_animation_->Show(); +} + +SkBitmap ToolbarView::GetAppMenuIcon() { + ThemeProvider* tp = GetThemeProvider(); + + SkBitmap icon; + + // We use different menu button images if the locale is right-to-left. + if (base::i18n::IsRTL()) + icon = *tp->GetBitmapNamed(IDR_MENU_CHROME_RTL); + else + icon = *tp->GetBitmapNamed(IDR_MENU_CHROME); + + if (!Singleton<UpgradeDetector>::get()->notify_upgrade()) + return icon; + + // Draw the chrome app menu icon onto the canvas. + scoped_ptr<gfx::Canvas> canvas( + new gfx::Canvas(icon.width(), icon.height(), false)); + canvas->DrawBitmapInt(icon, 0, 0); + + SkBitmap badge; + + static bool has_faded_in = false; + if (!has_faded_in) { + SkBitmap* dot = tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE); + SkBitmap transparent; + transparent.setConfig(dot->getConfig(), dot->width(), dot->height()); + transparent.allocPixels(); + transparent.eraseARGB(0, 0, 0, 0); + badge = SkBitmapOperations::CreateBlendedBitmap( + *dot, transparent, 1.0 - update_reminder_animation_->GetCurrentValue()); + if (update_reminder_animation_->GetCurrentValue() == 1.0) + has_faded_in = true; + } else { + // Convert animation values that start from 0.0 and incrementally go + // up to 1.0 into values that start in 0.0, go to 1.0 and then back + // to 0.0 (to create a pulsing effect). + double value = 1.0 - + abs(2.0 * update_reminder_animation_->GetCurrentValue() - + 1.0); + + // Add the badge to it. + badge = SkBitmapOperations::CreateBlendedBitmap( + *tp->GetBitmapNamed(IDR_UPGRADE_DOT_INACTIVE), + *tp->GetBitmapNamed(IDR_UPGRADE_DOT_ACTIVE), + value); + } + + int x_pos = kUpgradeDotOffset; + if (base::i18n::IsRTL()) + x_pos = icon.width() - badge.width(); + canvas->DrawBitmapInt(badge, x_pos, icon.height() - badge.height()); + + return canvas->ExtractBitmap(); +} + void ToolbarView::LoadRightSideControlsImages() { ThemeProvider* tp = GetThemeProvider(); @@ -742,10 +845,8 @@ void ToolbarView::LoadRightSideControlsImages() { page_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_PAGE_RTL)); else page_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_PAGE)); - if (base::i18n::IsRTL()) - app_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_CHROME_RTL)); - else - app_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_CHROME)); + + app_menu_->SetIcon(GetAppMenuIcon()); if (bookmark_menu_ != NULL) bookmark_menu_->SetIcon(*tp->GetBitmapNamed(IDR_MENU_BOOKMARK)); @@ -789,6 +890,9 @@ void ToolbarView::RunAppMenu(const gfx::Point& pt) { destroyed_flag_ = NULL; + // Stop pulsating the upgrade reminder on the app menu, if active. + upgrade_reminder_pulse_timer_.Stop(); + for (unsigned int i = 0; i < menu_listeners_.size(); i++) { app_menu_menu_->RemoveMenuListener(menu_listeners_[i]); } diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h index 9c7b472..99e1d28 100644 --- a/chrome/browser/views/toolbar_view.h +++ b/chrome/browser/views/toolbar_view.h @@ -8,6 +8,7 @@ #include <vector> #include "app/menus/simple_menu_model.h" +#include "app/slide_animation.h" #include "base/scoped_ptr.h" #include "chrome/browser/app_menu_model.h" #include "chrome/browser/back_forward_menu_model.h" @@ -38,6 +39,7 @@ class ToolbarView : public AccessibleToolbarView, public views::FocusChangeListener, public menus::SimpleMenuModel::Delegate, public LocationBarView::Delegate, + public AnimationDelegate, public NotificationObserver, public CommandUpdater::CommandObserver, public views::ButtonListener { @@ -107,6 +109,9 @@ class ToolbarView : public AccessibleToolbarView, virtual TabContents* GetTabContents(); virtual void OnInputInProgress(bool in_progress); + // Overridden from AnimationDelegate: + virtual void AnimationProgressed(const Animation* animation); + // Overridden from CommandUpdater::CommandObserver: virtual void EnabledStateChangedForCommand(int id, bool enabled); @@ -173,6 +178,17 @@ class ToolbarView : public AccessibleToolbarView, // was called. void RestoreLastFocusedView(); + // Starts the recurring timer that periodically asks the upgrade notifier + // to pulsate. + void ShowUpgradeReminder(); + + // Show the reminder, tempting the user to upgrade by pulsating. + void PulsateUpgradeNotifier(); + + // Gets a canvas with the icon for the app menu. It will possibly contain + // an overlaid badge if an update is recommended. + SkBitmap GetAppMenuIcon(); + scoped_ptr<BackForwardMenuModel> back_menu_model_; scoped_ptr<BackForwardMenuModel> forward_menu_model_; @@ -222,6 +238,13 @@ class ToolbarView : public AccessibleToolbarView, // Vector of listeners to receive callbacks when the menu opens. std::vector<views::MenuListener*> menu_listeners_; + // The animation that makes the update reminder pulse. + scoped_ptr<SlideAnimation> update_reminder_animation_; + + // We periodically restart the animation after it has been showed + // once, to create a pulsating effect. + base::RepeatingTimer<ToolbarView> upgrade_reminder_pulse_timer_; + // Are we in the menu bar emulation mode, where the app and page menu // are temporarily focusable? bool menu_bar_emulation_mode_; @@ -229,7 +252,9 @@ class ToolbarView : public AccessibleToolbarView, // Used to post tasks to switch to the next/previous menu. ScopedRunnableMethodFactory<ToolbarView> method_factory_; - // If non-null the destuctor sets this to true. This is set to a non-null + NotificationRegistrar registrar_; + + // If non-null the destructor sets this to true. This is set to a non-null // while the menu is showing and used to detect if the menu was deleted while // running. bool* destroyed_flag_; diff --git a/chrome/browser/views/update_recommended_message_box.cc b/chrome/browser/views/update_recommended_message_box.cc new file mode 100644 index 0000000..dc1bf05 --- /dev/null +++ b/chrome/browser/views/update_recommended_message_box.cc @@ -0,0 +1,90 @@ +// 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/views/update_recommended_message_box.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/pref_service.h" +#include "chrome/common/pref_names.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "views/controls/message_box_view.h" +#include "views/window/window.h" + +//////////////////////////////////////////////////////////////////////////////// +// UpdateRecommendedMessageBox, public: + +// static +void UpdateRecommendedMessageBox::ShowMessageBox( + gfx::NativeWindow parent_window) { + // When the window closes, it will delete itself. + 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(); + pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); + + BrowserList::CloseAllBrowsersAndExit(); + + return true; +} + +int UpdateRecommendedMessageBox::GetDialogButtons() const { + return MessageBoxFlags::DIALOGBUTTON_OK | + MessageBoxFlags::DIALOGBUTTON_CANCEL; +} + +std::wstring UpdateRecommendedMessageBox::GetDialogButtonLabel( + MessageBoxFlags::DialogButton button) const { + DCHECK(button == MessageBoxFlags::DIALOGBUTTON_OK || + button == MessageBoxFlags::DIALOGBUTTON_CANCEL); + return button == MessageBoxFlags::DIALOGBUTTON_OK ? + l10n_util::GetString(IDS_RESTART_AND_UPDATE) : + l10n_util::GetString(IDS_NOT_NOW); +} + +std::wstring UpdateRecommendedMessageBox::GetWindowTitle() const { + return l10n_util::GetString(IDS_PRODUCT_NAME); +} + +void UpdateRecommendedMessageBox::DeleteDelegate() { + delete this; +} + +bool UpdateRecommendedMessageBox::IsModal() const { + return true; +} + +views::View* UpdateRecommendedMessageBox::GetContentsView() { + return message_box_view_; +} + +//////////////////////////////////////////////////////////////////////////////// +// UpdateRecommendedMessageBox, private: + +UpdateRecommendedMessageBox::UpdateRecommendedMessageBox( + gfx::NativeWindow parent_window) { + const int kDialogWidth = 400; + // Also deleted when the window closes. + message_box_view_ = new MessageBoxView( + MessageBoxFlags::kFlagHasMessage | MessageBoxFlags::kFlagHasOKButton, + l10n_util::GetStringF(IDS_UPDATE_RECOMMENDED, + l10n_util::GetString(IDS_PRODUCT_NAME)), + std::wstring(), + kDialogWidth); + views::Window::CreateChromeWindow(parent_window, gfx::Rect(), this)->Show(); +} + +UpdateRecommendedMessageBox::~UpdateRecommendedMessageBox() { +} diff --git a/chrome/browser/views/update_recommended_message_box.h b/chrome/browser/views/update_recommended_message_box.h new file mode 100644 index 0000000..9751db4 --- /dev/null +++ b/chrome/browser/views/update_recommended_message_box.h @@ -0,0 +1,49 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_UPDATE_RECOMMENDED_MESSAGE_BOX_H_ +#define CHROME_BROWSER_VIEWS_UPDATE_RECOMMENDED_MESSAGE_BOX_H_ + +#include "base/basictypes.h" +#include "gfx/native_widget_types.h" +#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. +class UpdateRecommendedMessageBox : public views::DialogDelegate { + public: + // 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(); + + protected: + // Overridden from views::DialogDelegate: + virtual int GetDialogButtons() const; + virtual std::wstring GetDialogButtonLabel( + MessageBoxFlags::DialogButton button) const; + virtual std::wstring GetWindowTitle() const; + + // Overridden from views::WindowDelegate: + virtual void DeleteDelegate(); + virtual bool IsModal() const; + virtual views::View* GetContentsView(); + + private: + explicit UpdateRecommendedMessageBox(gfx::NativeWindow parent_window); + virtual ~UpdateRecommendedMessageBox(); + + MessageBoxView* message_box_view_; + + DISALLOW_COPY_AND_ASSIGN(UpdateRecommendedMessageBox); +}; + +#endif // CHROME_BROWSER_VIEWS_UPDATE_RECOMMENDED_MESSAGE_BOX_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 6783f64..3423b49 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2190,6 +2190,8 @@ 'browser/translate/translate_manager.h', 'browser/translate/translate_prefs.cc', 'browser/translate/translate_prefs.h', + 'browser/upgrade_detector.cc', + 'browser/upgrade_detector.h', 'browser/user_data_manager.cc', 'browser/user_data_manager.h', 'browser/user_style_sheet_watcher.cc', @@ -2529,6 +2531,8 @@ 'browser/views/url_picker.h', 'browser/views/unhandled_keyboard_event_handler.cc', 'browser/views/unhandled_keyboard_event_handler.h', + 'browser/views/update_recommended_message_box.cc', + 'browser/views/update_recommended_message_box.h', 'browser/views/user_data_dir_dialog.cc', 'browser/views/user_data_dir_dialog.h', 'browser/visitedlink_master.cc', diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index 17ccf04..de9efcb 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -889,6 +889,20 @@ class NotificationType { // memory in use, no source or details are passed. See memory_purger.h .cc. PURGE_MEMORY, + // Upgrade notifications --------------------------------------------------- + + // Sent when Chrome detects that it has been upgraded behind the scenes. + // NOTE: The detection mechanism is asynchronous, so this event may arrive + // quite some time after the upgrade actually happened. No details are + // expected. + UPGRADE_DETECTED, + + // Sent when Chrome believes an update has been installed and available for + // long enough with the user shutting down to let it take effect. See + // upgrade_detector.cc for details on how long it waits. No details are + // expected. + UPGRADE_RECOMMENDED, + // Accessibility Notifications --------------------------------------------- // Notification that a window in the browser UI (not the web content) diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 51eaa01..a313944 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -705,6 +705,11 @@ const wchar_t kShutdownNumProcesses[] = L"shutdown.num_processes"; // Number of processes that were shut down using the slow path. const wchar_t kShutdownNumProcessesSlow[] = L"shutdown.num_processes_slow"; +// Whether to restart the current Chrome session automatically as the last thing +// before shutting everything down. +const wchar_t kRestartLastSessionOnShutdown[] = + L"restart.last.session.on.shutdown"; + // Number of bookmarks/folders on the bookmark bar/other bookmark folder. const wchar_t kNumBookmarksOnBookmarkBar[] = L"user_experience_metrics.num_bookmarks_on_bookmark_bar"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 9d84318..dc2199a 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -251,6 +251,8 @@ extern const wchar_t kShutdownType[]; extern const wchar_t kShutdownNumProcesses[]; extern const wchar_t kShutdownNumProcessesSlow[]; +extern const wchar_t kRestartLastSessionOnShutdown[]; + extern const wchar_t kNumBookmarksOnBookmarkBar[]; extern const wchar_t kNumFoldersOnBookmarkBar[]; extern const wchar_t kNumBookmarksInOtherBookmarkFolder[]; diff --git a/chrome/test/test_browser_window.h b/chrome/test/test_browser_window.h index 2e2ddcb..77874e6 100644 --- a/chrome/test/test_browser_window.h +++ b/chrome/test/test_browser_window.h @@ -69,6 +69,7 @@ class TestBrowserWindow : public BrowserWindow { virtual void ToggleBookmarkBar() {} virtual void ToggleExtensionShelf() {} virtual views::Window* ShowAboutChromeDialog() { return NULL; } + virtual void ShowUpdateChromeDialog() {} virtual void ShowTaskManager() {} virtual void ShowBookmarkManager() {} virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) {} |