summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-12 23:27:12 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-12 23:27:12 +0000
commitceff840da7632736581f06bcd3f1495ef55fe42f (patch)
treedf7716a71e1066cbd8613e47aa9edeea66d20348
parenta2d4b9cfbcbed0bf23d9fe69e690bcc31fc0b780 (diff)
downloadchromium_src-ceff840da7632736581f06bcd3f1495ef55fe42f.zip
chromium_src-ceff840da7632736581f06bcd3f1495ef55fe42f.tar.gz
chromium_src-ceff840da7632736581f06bcd3f1495ef55fe42f.tar.bz2
Refactor UpgradeDetector.
- Move peroidically timer based detection code into UpgradeDetectorImpl for use on non-ChromeOS platform; - Implement ChromeOS upgrade detection via UpdateLibrary observer and apply a slightly different advisory timing for showing the badges (show green arrow immediately on upgrade detected, then 2, 4, 7 days for yellow, red and orange color); BUG=chromium-os:16146 TEST=Verify fix for chromium-os:16146. Review URL: http://codereview.chromium.org/7046096 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@88800 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/upgrade_detector_chromeos.cc78
-rw-r--r--chrome/browser/chromeos/upgrade_detector_chromeos.h40
-rw-r--r--chrome/browser/upgrade_detector.cc259
-rw-r--r--chrome/browser/upgrade_detector.h44
-rw-r--r--chrome/browser/upgrade_detector_impl.cc265
-rw-r--r--chrome/browser/upgrade_detector_impl.h57
-rw-r--r--chrome/chrome_browser.gypi6
7 files changed, 464 insertions, 285 deletions
diff --git a/chrome/browser/chromeos/upgrade_detector_chromeos.cc b/chrome/browser/chromeos/upgrade_detector_chromeos.cc
new file mode 100644
index 0000000..45ad77a
--- /dev/null
+++ b/chrome/browser/chromeos/upgrade_detector_chromeos.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/upgrade_detector_chromeos.h"
+
+#include "base/memory/singleton.h"
+#include "chrome/browser/chromeos/cros/cros_library.h"
+
+namespace {
+
+// How long to wait (each cycle) before checking which severity level we should
+// be at. Once we reach the highest severity, the timer will stop.
+const int kNotifyCycleTimeMs = 20 * 60 * 1000; // 20 minutes.
+
+} // namespace
+
+UpgradeDetectorChromeos::UpgradeDetectorChromeos() {
+ chromeos::CrosLibrary::Get()->GetUpdateLibrary()->AddObserver(this);
+}
+
+UpgradeDetectorChromeos::~UpgradeDetectorChromeos() {
+ chromeos::CrosLibrary::Get()->GetUpdateLibrary()->RemoveObserver(this);
+}
+
+void UpgradeDetectorChromeos::UpdateStatusChanged(
+ chromeos::UpdateLibrary* library) {
+ if (library->status().status != chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT)
+ return;
+
+ NotifyUpgradeDetected();
+
+ // ChromeOS shows upgrade arrow once the upgrade becomes available.
+ NotifyOnUpgrade();
+
+ // Setup timer to to move along the upgrade advisory system.
+ upgrade_notification_timer_.Start(
+ base::TimeDelta::FromMilliseconds(kNotifyCycleTimeMs),
+ this, &UpgradeDetectorChromeos::NotifyOnUpgrade);
+}
+
+void UpgradeDetectorChromeos::NotifyOnUpgrade() {
+ base::TimeDelta delta = base::Time::Now() - upgrade_detected_time();
+ int64 time_passed = delta.InDays();
+
+ const int kSevereThreshold = 7;
+ const int kHighThreshold = 4;
+ const int kElevatedThreshold = 2;
+ const int kLowThreshold = 0;
+
+ // These if statements must be sorted (highest interval first).
+ if (time_passed >= kSevereThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_SEVERE);
+
+ // We can't get any higher, baby.
+ upgrade_notification_timer_.Stop();
+ } else if (time_passed >= kHighThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
+ } else if (time_passed >= kElevatedThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
+ } else if (time_passed >= kLowThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
+ } else {
+ return; // Not ready to recommend upgrade.
+ }
+
+ NotifyUpgradeRecommended();
+}
+
+// static
+UpgradeDetectorChromeos* UpgradeDetectorChromeos::GetInstance() {
+ return Singleton<UpgradeDetectorChromeos>::get();
+}
+
+// static
+UpgradeDetector* UpgradeDetector::GetInstance() {
+ return UpgradeDetectorChromeos::GetInstance();
+}
diff --git a/chrome/browser/chromeos/upgrade_detector_chromeos.h b/chrome/browser/chromeos/upgrade_detector_chromeos.h
new file mode 100644
index 0000000..09f2f70
--- /dev/null
+++ b/chrome/browser/chromeos/upgrade_detector_chromeos.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_UPGRADE_DETECTOR_CHROMEOS_H_
+#define CHROME_BROWSER_CHROMEOS_UPGRADE_DETECTOR_CHROMEOS_H_
+#pragma once
+
+#include "base/timer.h"
+#include "chrome/browser/chromeos/cros/update_library.h"
+#include "chrome/browser/upgrade_detector.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+class UpgradeDetectorChromeos : public UpgradeDetector,
+ public chromeos::UpdateLibrary::Observer {
+ public:
+ virtual ~UpgradeDetectorChromeos();
+
+ static UpgradeDetectorChromeos* GetInstance();
+
+ private:
+ friend struct DefaultSingletonTraits<UpgradeDetectorChromeos>;
+
+ UpgradeDetectorChromeos();
+
+ // chromeos::UpdateLibrary::Observer implementation.
+ virtual void UpdateStatusChanged(chromeos::UpdateLibrary* library);
+
+ // 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();
+
+ // After we detect an upgrade we start a recurring timer to see if enough time
+ // has passed and we should start notifying the user.
+ base::RepeatingTimer<UpgradeDetectorChromeos> upgrade_notification_timer_;
+};
+
+#endif // CHROME_BROWSER_CHROMEOS_UPGRADE_DETECTOR_CHROMEOS_H_
diff --git a/chrome/browser/upgrade_detector.cc b/chrome/browser/upgrade_detector.cc
index a0d8f0c..e0fb2d5 100644
--- a/chrome/browser/upgrade_detector.cc
+++ b/chrome/browser/upgrade_detector.cc
@@ -4,158 +4,11 @@
#include "chrome/browser/upgrade_detector.h"
-#include <string>
-
-#include "base/command_line.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/singleton.h"
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
-#include "base/task.h"
-#include "base/time.h"
-#include "base/utf_string_conversions.h"
-#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_version_info.h"
#include "chrome/common/pref_names.h"
-#include "chrome/installer/util/browser_distribution.h"
-#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "grit/theme_resources.h"
-#include "ui/base/resource/resource_bundle.h"
-
-#if defined(OS_WIN)
-#include "chrome/installer/util/install_util.h"
-#elif defined(OS_MACOSX)
-#include "chrome/browser/cocoa/keystone_glue.h"
-#elif defined(OS_POSIX)
-#include "base/process_util.h"
-#include "base/version.h"
-#endif
-
-#if defined(OS_CHROMEOS)
-#include "chrome/browser/chromeos/cros/cros_library.h"
-#include "chrome/browser/chromeos/cros/update_library.h"
-#endif
-
-namespace {
-
-// How long (in milliseconds) to wait (each cycle) before checking whether
-// Chrome's been upgraded behind our back.
-const int kCheckForUpgradeMs = 2 * 60 * 60 * 1000; // 2 hours.
-
-// How long to wait (each cycle) before checking which severity level we should
-// be at. Once we reach the highest severity, the timer will stop.
-const int kNotifyCycleTimeMs = 20 * 60 * 1000; // 20 minutes.
-
-// Same as kNotifyCycleTimeMs but only used during testing.
-const int kNotifyCycleTimeForTestingMs = 500; // Half a second.
-
-std::string CmdLineInterval() {
- const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
- return cmd_line.GetSwitchValueASCII(switches::kCheckForUpdateIntervalSec);
-}
-
-// How often to check for an upgrade.
-int GetCheckForUpgradeEveryMs() {
- // Check for a value passed via the command line.
- int interval_ms;
- std::string interval = CmdLineInterval();
- if (!interval.empty() && base::StringToInt(interval, &interval_ms))
- return interval_ms * 1000; // Command line value is in seconds.
-
- return kCheckForUpgradeMs;
-}
-
-// 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,
- bool* is_unstable_channel)
- : upgrade_detected_task_(upgrade_detected_task),
- is_unstable_channel_(is_unstable_channel) {
- }
-
- virtual ~DetectUpgradeTask() {
- if (upgrade_detected_task_) {
- // This has to get deleted on the same thread it was created.
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- new DeleteTask<Task>(upgrade_detected_task_));
- }
- }
-
- virtual void Run() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- scoped_ptr<Version> installed_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.
- // TODO(tommi): Check if using the default distribution is always the right
- // thing to do.
- BrowserDistribution* dist = BrowserDistribution::GetDistribution();
- installed_version.reset(InstallUtil::GetChromeVersion(dist, false));
- if (!installed_version.get()) {
- // User level Chrome is not installed, check system level.
- installed_version.reset(InstallUtil::GetChromeVersion(dist, true));
- }
-#elif defined(OS_MACOSX)
- installed_version.reset(
- Version::GetVersionFromString(UTF16ToASCII(
- keystone_glue::CurrentlyInstalledVersion())));
-#elif defined(OS_POSIX)
- // POSIX but not Mac OS X: Linux, etc.
- 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;
- }
-
- installed_version.reset(Version::GetVersionFromString(reply));
-#endif
-
- platform_util::Channel channel = platform_util::GetChannel();
- *is_unstable_channel_ = channel == platform_util::CHANNEL_DEV ||
- channel == platform_util::CHANNEL_CANARY;
-
- // Get the version of the currently *running* instance of Chrome.
- chrome::VersionInfo version_info;
- if (!version_info.is_valid()) {
- NOTREACHED() << "Failed to get current file version";
- return;
- }
- scoped_ptr<Version> running_version(
- Version::GetVersionFromString(version_info.Version()));
- if (running_version.get() == NULL) {
- NOTREACHED() << "Failed to parse version info";
- return;
- }
-
- // |installed_version| may be NULL when the user downgrades on Linux (by
- // switching from dev to beta channel, for example). The user needs a
- // restart in this case as well. See http://crbug.com/46547
- if (!installed_version.get() ||
- (installed_version->CompareTo(*running_version) > 0)) {
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- upgrade_detected_task_);
- upgrade_detected_task_ = NULL;
- }
- }
-
- private:
- Task* upgrade_detected_task_;
- bool* is_unstable_channel_;
-};
-
-} // namespace
// static
void UpgradeDetector::RegisterPrefs(PrefService* prefs) {
@@ -179,129 +32,23 @@ int UpgradeDetector::GetIconResourceID(UpgradeNotificationIconType type) {
}
UpgradeDetector::UpgradeDetector()
- : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
- is_unstable_channel_(false),
- upgrade_notification_stage_(UPGRADE_ANNOYANCE_NONE),
+ : upgrade_notification_stage_(UPGRADE_ANNOYANCE_NONE),
notify_upgrade_(false) {
- CommandLine command_line(*CommandLine::ForCurrentProcess());
- if (command_line.HasSwitch(switches::kDisableBackgroundNetworking))
- return;
- // Windows: only enable upgrade notifications for official builds.
- // Mac: only enable them if the updater (Keystone) is present.
- // Linux (and other POSIX): always enable regardless of branding.
-#if (defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)) || defined(OS_POSIX)
-#if defined(OS_MACOSX)
- if (keystone_glue::KeystoneEnabled())
-#endif
- {
- detect_upgrade_timer_.Start(
- base::TimeDelta::FromMilliseconds(GetCheckForUpgradeEveryMs()),
- this, &UpgradeDetector::CheckForUpgrade);
- }
-#endif
}
UpgradeDetector::~UpgradeDetector() {
}
-// static
-UpgradeDetector* UpgradeDetector::GetInstance() {
- return Singleton<UpgradeDetector>::get();
-}
-
-void UpgradeDetector::CheckForUpgrade() {
-#if defined(OS_CHROMEOS)
- // For ChromeOS, check update library status to detect upgrade.
- if (chromeos::CrosLibrary::Get()->GetUpdateLibrary()->status().status ==
- chromeos::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
- UpgradeDetected();
- }
-#else
- method_factory_.RevokeAll();
- Task* callback_task =
- method_factory_.NewRunnableMethod(&UpgradeDetector::UpgradeDetected);
- // We use FILE as the thread to run the upgrade detection code on all
- // platforms. For Linux, this is because we don't want to block the UI thread
- // while launching a background process and reading its output; on the Mac and
- // on Windows checking for an upgrade requires reading a file.
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- new DetectUpgradeTask(callback_task,
- &is_unstable_channel_));
-#endif
-}
-
-void UpgradeDetector::UpgradeDetected() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- // Stop the recurring timer (that is checking for changes).
- detect_upgrade_timer_.Stop();
-
+void UpgradeDetector::NotifyUpgradeDetected() {
upgrade_detected_time_ = base::Time::Now();
NotificationService::current()->Notify(
NotificationType::UPGRADE_DETECTED,
Source<UpgradeDetector>(this),
NotificationService::NoDetails());
-
- // Start the repeating timer for notifying the user after a certain period.
- // The called function will eventually figure out that enough time has passed
- // and stop the timer.
- int cycle_time = CmdLineInterval().empty() ? kNotifyCycleTimeMs :
- kNotifyCycleTimeForTestingMs;
- upgrade_notification_timer_.Start(
- base::TimeDelta::FromMilliseconds(cycle_time),
- this, &UpgradeDetector::NotifyOnUpgrade);
}
-void UpgradeDetector::NotifyOnUpgrade() {
- base::TimeDelta delta = base::Time::Now() - upgrade_detected_time_;
- std::string interval = CmdLineInterval();
-
- // A command line interval implies testing, which we'll make more convenient
- // by switching to seconds of waiting instead of days between flipping
- // severity. This works in conjunction with the similar interval.empty()
- // check below.
- int64 time_passed = interval.empty() ? delta.InHours() : delta.InSeconds();
-
- if (is_unstable_channel_) {
- // There's only one threat level for unstable channels like dev and
- // canary, and it hits after one hour. During testing, it hits after one
- // minute.
- const int kUnstableThreshold = 1;
-
- if (time_passed >= kUnstableThreshold) {
- upgrade_notification_stage_ = UPGRADE_ANNOYANCE_LOW;
-
- // That's as high as it goes.
- upgrade_notification_timer_.Stop();
- } else {
- return; // Not ready to recommend upgrade.
- }
- } else {
- const int kMultiplier = interval.empty() ? 24 : 1;
- // 14 days when not testing, otherwise 14 seconds.
- const int kSevereThreshold = 14 * kMultiplier;
- const int kHighThreshold = 7 * kMultiplier;
- const int kElevatedThreshold = 4 * kMultiplier;
- const int kLowThreshold = 2 * kMultiplier;
-
- // These if statements must be sorted (highest interval first).
- if (time_passed >= kSevereThreshold) {
- upgrade_notification_stage_ = UPGRADE_ANNOYANCE_SEVERE;
-
- // We can't get any higher, baby.
- upgrade_notification_timer_.Stop();
- } else if (time_passed >= kHighThreshold) {
- upgrade_notification_stage_ = UPGRADE_ANNOYANCE_HIGH;
- } else if (time_passed >= kElevatedThreshold) {
- upgrade_notification_stage_ = UPGRADE_ANNOYANCE_ELEVATED;
- } else if (time_passed >= kLowThreshold) {
- upgrade_notification_stage_ = UPGRADE_ANNOYANCE_LOW;
- } else {
- return; // Not ready to recommend upgrade.
- }
- }
-
+void UpgradeDetector::NotifyUpgradeRecommended() {
notify_upgrade_ = true;
NotificationService::current()->Notify(
diff --git a/chrome/browser/upgrade_detector.h b/chrome/browser/upgrade_detector.h
index 00227d5..31ac709 100644
--- a/chrome/browser/upgrade_detector.h
+++ b/chrome/browser/upgrade_detector.h
@@ -9,7 +9,6 @@
#include "base/timer.h"
#include "ui/gfx/image.h"
-template <typename T> struct DefaultSingletonTraits;
class PrefService;
///////////////////////////////////////////////////////////////////////////////
@@ -39,10 +38,10 @@ class UpgradeDetector {
UPGRADE_ICON_TYPE_MENU_ICON, // For showing in the wrench menu.
};
- // Returns the singleton instance.
+ // Returns the singleton implementation instance.
static UpgradeDetector* GetInstance();
- ~UpgradeDetector();
+ virtual ~UpgradeDetector();
static void RegisterPrefs(PrefService* prefs);
@@ -55,41 +54,28 @@ class UpgradeDetector {
// within the wrench menu.
int GetIconResourceID(UpgradeNotificationIconType type);
- private:
- friend struct DefaultSingletonTraits<UpgradeDetector>;
-
+ protected:
UpgradeDetector();
- // 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();
+ // Sends out UPGRADE_DETECTED notification and record upgrade_detected_time_.
+ void NotifyUpgradeDetected();
- // 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();
+ // Sends out UPGRADE_RECOMMENDED notification and set notify_upgrade_.
+ void NotifyUpgradeRecommended();
- // We periodically check to see if Chrome has been upgraded.
- base::RepeatingTimer<UpgradeDetector> detect_upgrade_timer_;
+ // Accessors.
+ const base::Time& upgrade_detected_time() const {
+ return upgrade_detected_time_;
+ }
- // After we detect an upgrade we start a recurring timer to see if enough time
- // has passed and we should start notifying the user.
- base::RepeatingTimer<UpgradeDetector> upgrade_notification_timer_;
-
- // 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<UpgradeDetector> method_factory_;
+ void set_upgrade_notification_stage(UpgradeNotificationAnnoyanceLevel stage) {
+ upgrade_notification_stage_ = stage;
+ }
+ private:
// When the upgrade was detected.
base::Time upgrade_detected_time_;
- // True if this build is a dev or canary channel build.
- bool is_unstable_channel_;
-
// The stage at which the annoyance level for upgrade notifications is at.
UpgradeNotificationAnnoyanceLevel upgrade_notification_stage_;
diff --git a/chrome/browser/upgrade_detector_impl.cc b/chrome/browser/upgrade_detector_impl.cc
new file mode 100644
index 0000000..23f747a
--- /dev/null
+++ b/chrome/browser/upgrade_detector_impl.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/upgrade_detector_impl.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "content/browser/browser_thread.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_WIN)
+#include "chrome/installer/util/install_util.h"
+#elif defined(OS_MACOSX)
+#include "chrome/browser/cocoa/keystone_glue.h"
+#elif defined(OS_POSIX)
+#include "base/process_util.h"
+#include "base/version.h"
+#endif
+
+namespace {
+
+// How long (in milliseconds) to wait (each cycle) before checking whether
+// Chrome's been upgraded behind our back.
+const int kCheckForUpgradeMs = 2 * 60 * 60 * 1000; // 2 hours.
+
+// How long to wait (each cycle) before checking which severity level we should
+// be at. Once we reach the highest severity, the timer will stop.
+const int kNotifyCycleTimeMs = 20 * 60 * 1000; // 20 minutes.
+
+// Same as kNotifyCycleTimeMs but only used during testing.
+const int kNotifyCycleTimeForTestingMs = 500; // Half a second.
+
+std::string CmdLineInterval() {
+ const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
+ return cmd_line.GetSwitchValueASCII(switches::kCheckForUpdateIntervalSec);
+}
+
+// How often to check for an upgrade.
+int GetCheckForUpgradeEveryMs() {
+ // Check for a value passed via the command line.
+ int interval_ms;
+ std::string interval = CmdLineInterval();
+ if (!interval.empty() && base::StringToInt(interval, &interval_ms))
+ return interval_ms * 1000; // Command line value is in seconds.
+
+ return kCheckForUpgradeMs;
+}
+
+// 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,
+ bool* is_unstable_channel)
+ : upgrade_detected_task_(upgrade_detected_task),
+ is_unstable_channel_(is_unstable_channel) {
+ }
+
+ virtual ~DetectUpgradeTask() {
+ if (upgrade_detected_task_) {
+ // This has to get deleted on the same thread it was created.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ new DeleteTask<Task>(upgrade_detected_task_));
+ }
+ }
+
+ virtual void Run() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ scoped_ptr<Version> installed_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.
+ // TODO(tommi): Check if using the default distribution is always the right
+ // thing to do.
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ installed_version.reset(InstallUtil::GetChromeVersion(dist, false));
+ if (!installed_version.get()) {
+ // User level Chrome is not installed, check system level.
+ installed_version.reset(InstallUtil::GetChromeVersion(dist, true));
+ }
+#elif defined(OS_MACOSX)
+ installed_version.reset(
+ Version::GetVersionFromString(UTF16ToASCII(
+ keystone_glue::CurrentlyInstalledVersion())));
+#elif defined(OS_POSIX)
+ // POSIX but not Mac OS X: Linux, etc.
+ 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;
+ }
+
+ installed_version.reset(Version::GetVersionFromString(reply));
+#endif
+
+ platform_util::Channel channel = platform_util::GetChannel();
+ *is_unstable_channel_ = channel == platform_util::CHANNEL_DEV ||
+ channel == platform_util::CHANNEL_CANARY;
+
+ // Get the version of the currently *running* instance of Chrome.
+ chrome::VersionInfo version_info;
+ if (!version_info.is_valid()) {
+ NOTREACHED() << "Failed to get current file version";
+ return;
+ }
+ scoped_ptr<Version> running_version(
+ Version::GetVersionFromString(version_info.Version()));
+ if (running_version.get() == NULL) {
+ NOTREACHED() << "Failed to parse version info";
+ return;
+ }
+
+ // |installed_version| may be NULL when the user downgrades on Linux (by
+ // switching from dev to beta channel, for example). The user needs a
+ // restart in this case as well. See http://crbug.com/46547
+ if (!installed_version.get() ||
+ (installed_version->CompareTo(*running_version) > 0)) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ upgrade_detected_task_);
+ upgrade_detected_task_ = NULL;
+ }
+ }
+
+ private:
+ Task* upgrade_detected_task_;
+ bool* is_unstable_channel_;
+};
+
+} // namespace
+
+UpgradeDetectorImpl::UpgradeDetectorImpl()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ is_unstable_channel_(false) {
+ CommandLine command_line(*CommandLine::ForCurrentProcess());
+ if (command_line.HasSwitch(switches::kDisableBackgroundNetworking))
+ return;
+ // Windows: only enable upgrade notifications for official builds.
+ // Mac: only enable them if the updater (Keystone) is present.
+ // Linux (and other POSIX): always enable regardless of branding.
+#if (defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)) || defined(OS_POSIX)
+#if defined(OS_MACOSX)
+ if (keystone_glue::KeystoneEnabled())
+#endif
+ {
+ detect_upgrade_timer_.Start(
+ base::TimeDelta::FromMilliseconds(GetCheckForUpgradeEveryMs()),
+ this, &UpgradeDetectorImpl::CheckForUpgrade);
+ }
+#endif
+}
+
+UpgradeDetectorImpl::~UpgradeDetectorImpl() {
+}
+
+void UpgradeDetectorImpl::CheckForUpgrade() {
+ method_factory_.RevokeAll();
+ Task* callback_task =
+ method_factory_.NewRunnableMethod(&UpgradeDetectorImpl::UpgradeDetected);
+ // We use FILE as the thread to run the upgrade detection code on all
+ // platforms. For Linux, this is because we don't want to block the UI thread
+ // while launching a background process and reading its output; on the Mac and
+ // on Windows checking for an upgrade requires reading a file.
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ new DetectUpgradeTask(callback_task,
+ &is_unstable_channel_));
+}
+
+void UpgradeDetectorImpl::UpgradeDetected() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Stop the recurring timer (that is checking for changes).
+ detect_upgrade_timer_.Stop();
+
+ NotifyUpgradeDetected();
+
+ // Start the repeating timer for notifying the user after a certain period.
+ // The called function will eventually figure out that enough time has passed
+ // and stop the timer.
+ int cycle_time = CmdLineInterval().empty() ? kNotifyCycleTimeMs :
+ kNotifyCycleTimeForTestingMs;
+ upgrade_notification_timer_.Start(
+ base::TimeDelta::FromMilliseconds(cycle_time),
+ this, &UpgradeDetectorImpl::NotifyOnUpgrade);
+}
+
+void UpgradeDetectorImpl::NotifyOnUpgrade() {
+ base::TimeDelta delta = base::Time::Now() - upgrade_detected_time();
+ std::string interval = CmdLineInterval();
+
+ // A command line interval implies testing, which we'll make more convenient
+ // by switching to seconds of waiting instead of days between flipping
+ // severity. This works in conjunction with the similar interval.empty()
+ // check below.
+ int64 time_passed = interval.empty() ? delta.InHours() : delta.InSeconds();
+
+ if (is_unstable_channel_) {
+ // There's only one threat level for unstable channels like dev and
+ // canary, and it hits after one hour. During testing, it hits after one
+ // minute.
+ const int kUnstableThreshold = 1;
+
+ if (time_passed >= kUnstableThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
+
+ // That's as high as it goes.
+ upgrade_notification_timer_.Stop();
+ } else {
+ return; // Not ready to recommend upgrade.
+ }
+ } else {
+ const int kMultiplier = interval.empty() ? 24 : 1;
+ // 14 days when not testing, otherwise 14 seconds.
+ const int kSevereThreshold = 14 * kMultiplier;
+ const int kHighThreshold = 7 * kMultiplier;
+ const int kElevatedThreshold = 4 * kMultiplier;
+ const int kLowThreshold = 2 * kMultiplier;
+
+ // These if statements must be sorted (highest interval first).
+ if (time_passed >= kSevereThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_SEVERE);
+
+ // We can't get any higher, baby.
+ upgrade_notification_timer_.Stop();
+ } else if (time_passed >= kHighThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
+ } else if (time_passed >= kElevatedThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
+ } else if (time_passed >= kLowThreshold) {
+ set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
+ } else {
+ return; // Not ready to recommend upgrade.
+ }
+ }
+
+ NotifyUpgradeRecommended();
+}
+
+// static
+UpgradeDetectorImpl* UpgradeDetectorImpl::GetInstance() {
+ return Singleton<UpgradeDetectorImpl>::get();
+}
+
+// static
+UpgradeDetector* UpgradeDetector::GetInstance() {
+ return UpgradeDetectorImpl::GetInstance();
+}
diff --git a/chrome/browser/upgrade_detector_impl.h b/chrome/browser/upgrade_detector_impl.h
new file mode 100644
index 0000000..ee6e3fa
--- /dev/null
+++ b/chrome/browser/upgrade_detector_impl.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UPGRADE_DETECTOR_IMPL_H_
+#define CHROME_BROWSER_UPGRADE_DETECTOR_IMPL_H_
+#pragma once
+
+#include "base/timer.h"
+#include "chrome/browser/upgrade_detector.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+class UpgradeDetectorImpl : public UpgradeDetector {
+ public:
+ virtual ~UpgradeDetectorImpl();
+
+ // Returns the singleton instance.
+ static UpgradeDetectorImpl* GetInstance();
+
+ private:
+ friend struct DefaultSingletonTraits<UpgradeDetectorImpl>;
+
+ UpgradeDetectorImpl();
+
+ // 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.
+ void NotifyOnUpgrade();
+
+ // We periodically check to see if Chrome has been upgraded.
+ base::RepeatingTimer<UpgradeDetectorImpl> detect_upgrade_timer_;
+
+ // After we detect an upgrade we start a recurring timer to see if enough time
+ // has passed and we should start notifying the user.
+ base::RepeatingTimer<UpgradeDetectorImpl> upgrade_notification_timer_;
+
+ // 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<UpgradeDetectorImpl> method_factory_;
+
+ // True if this build is a dev or canary channel build.
+ bool is_unstable_channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(UpgradeDetectorImpl);
+};
+
+
+#endif // CHROME_BROWSER_UPGRADE_DETECTOR_IMPL_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index e1fde1a..15793e6 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -721,6 +721,8 @@
'browser/chromeos/tab_closeable_state_watcher.h',
'browser/chromeos/update_observer.cc',
'browser/chromeos/update_observer.h',
+ 'browser/chromeos/upgrade_detector_chromeos.cc',
+ 'browser/chromeos/upgrade_detector_chromeos.h',
'browser/chromeos/user_cros_settings_provider.cc',
'browser/chromeos/user_cros_settings_provider.h',
'browser/chromeos/version_loader.cc',
@@ -3511,6 +3513,8 @@
'browser/ui/window_snapshot/window_snapshot_x.cc',
'browser/upgrade_detector.cc',
'browser/upgrade_detector.h',
+ 'browser/upgrade_detector_impl.cc',
+ 'browser/upgrade_detector_impl.h',
'browser/user_style_sheet_watcher.cc',
'browser/user_style_sheet_watcher.h',
'browser/visitedlink/visitedlink_event_listener.cc',
@@ -3627,6 +3631,8 @@
'browser/password_manager/native_backend_kwallet_x.cc',
'browser/password_manager/native_backend_kwallet_x.h',
'browser/platform_util_linux.cc',
+ 'browser/upgrade_detector_impl.cc',
+ 'browser/upgrade_detector_impl.h',
],
}],
['use_cups==1', {