diff options
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) {} |