Reland 0a1b4fb27a194a6c4ec3fa22040a4e8152cd45b
(https://chromium-review.googlesource.com/c/chromium/src/+/1344880)
Relative to the original CL, this changes the setter for skipping
scheduled updates into a global value that can be set by browser tests
prior to constructing and kicking off the extension updater. This is
the pattern used by content verification and other extension-behavior
customization.
It's my hope that taking this approach will eliminate a lot of long-
standing flakiness in several of the tests, so if this sticks I can
follow it up with a CL re-enabling several tests. (Or another person
who has a better relationship with extensions can.)
OCL description:
> Simplify extension update check delay after browser start.
>
> Bug: 907219
> Change-Id: I067fcb333b43f60441e1789cac8caf5b996b7e5d
> Reviewed-on: https://chromium-review.googlesource.com/c/1344880
> Commit-Queue: Joshua Pawlicki <[email protected]>
> Reviewed-by: Gabriel Charette <[email protected]>
> Reviewed-by: Sergey Poromov <[email protected]>
> Reviewed-by: Brian White <[email protected]>
> Reviewed-by: Devlin <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#620845}
Bug: 907219, 920040
Change-Id: I35ff0311f84294eca6cd1b23a3fd612d53ea42db
Reviewed-on: https://chromium-review.googlesource.com/c/1412796
Auto-Submit: Joshua Pawlicki <[email protected]>
Reviewed-by: Devlin <[email protected]>
Reviewed-by: Brian White <[email protected]>
Reviewed-by: Sergey Poromov <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Commit-Queue: Joshua Pawlicki <[email protected]>
Cr-Commit-Position: refs/heads/master@{#624302}
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h
index 1a8cdad..4c8544c9 100644
--- a/chrome/browser/extensions/extension_browsertest.h
+++ b/chrome/browser/extensions/extension_browsertest.h
@@ -15,6 +15,7 @@
#include "build/build_config.h"
#include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
#include "chrome/browser/extensions/install_verifier.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
@@ -418,6 +419,8 @@
std::unique_ptr<ScopedInstallVerifierBypassForTest>
ignore_install_verification_;
+ ExtensionUpdater::ScopedSkipScheduledCheckForTest skip_scheduled_check_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionBrowserTest);
};
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
index 3d91582..00aef112 100644
--- a/chrome/browser/extensions/updater/extension_updater.cc
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -52,20 +52,14 @@
namespace {
-// Wait at least 60 seconds after browser startup before we do any checks. If
-// you change this value, make sure to update comments where it is used.
-const int kStartupWaitSeconds = 60;
-
-// The minimum number of seconds there should be for the delay passed to
-// ScheduleNextCheck.
-const int kScheduleNextCheckMinGapSecs = 1;
-
// For sanity checking on update frequency - enforced in release mode only.
#if defined(NDEBUG)
const int kMinUpdateFrequencySeconds = 30;
#endif
const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
+bool g_skip_scheduled_checks_for_tests = false;
+
// When we've computed a days value, we want to make sure we don't send a
// negative value (due to the system clock being set backwards, etc.), since -1
// is a special sentinel value that means "never pinged", and other negative
@@ -146,7 +140,7 @@
service_(service),
downloader_factory_(downloader_factory),
update_service_(nullptr),
- frequency_seconds_(frequency_seconds),
+ frequency_(base::TimeDelta::FromSeconds(frequency_seconds)),
will_check_soon_(false),
extension_prefs_(extension_prefs),
prefs_(prefs),
@@ -155,13 +149,13 @@
crx_install_is_running_(false),
extension_cache_(cache),
weak_ptr_factory_(this) {
- DCHECK_GE(frequency_seconds_, 5);
- DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
+ DCHECK_LE(frequency_seconds, kMaxUpdateFrequencySeconds);
#if defined(NDEBUG)
// In Release mode we enforce that update checks don't happen too often.
- frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
+ frequency_seconds = std::max(frequency_seconds, kMinUpdateFrequencySeconds);
#endif
- frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
+ frequency_seconds = std::min(frequency_seconds, kMaxUpdateFrequencySeconds);
+ frequency_ = base::TimeDelta::FromSeconds(frequency_seconds);
}
ExtensionUpdater::~ExtensionUpdater() {
@@ -177,38 +171,6 @@
}
}
-// The overall goal here is to balance keeping clients up to date while
-// avoiding a thundering herd against update servers.
-base::TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
- DCHECK(alive_);
- // If someone's testing with a quick frequency, just allow it.
- if (frequency_seconds_ < kStartupWaitSeconds)
- return base::TimeDelta::FromSeconds(frequency_seconds_);
-
- // If we've never scheduled a check before, start at a random time up to
- // frequency_seconds_ away.
- if (!prefs_->HasPrefPath(pref_names::kNextUpdateCheck))
- return base::TimeDelta::FromSeconds(
- RandInt(kStartupWaitSeconds, frequency_seconds_));
-
- // Read the persisted next check time, and use that if it isn't in the past
- // or too far in the future (this can happen with system clock changes).
- base::Time saved_next = base::Time::FromInternalValue(
- prefs_->GetInt64(pref_names::kNextUpdateCheck));
- base::Time now = base::Time::Now();
- base::Time earliest =
- now + base::TimeDelta::FromSeconds(kScheduleNextCheckMinGapSecs);
- base::Time latest = now + base::TimeDelta::FromSeconds(frequency_seconds_);
- if (saved_next > earliest && saved_next < latest) {
- return saved_next - now;
- }
-
- // In most cases we'll get here because the persisted next check time passed
- // while we weren't running, so pick something soon.
- return base::TimeDelta::FromSeconds(
- RandInt(kStartupWaitSeconds, kStartupWaitSeconds * 5));
-}
-
void ExtensionUpdater::Start() {
DCHECK(!alive_);
// If these are NULL, then that means we've been called after Stop()
@@ -219,8 +181,11 @@
DCHECK(profile_);
DCHECK(!weak_ptr_factory_.HasWeakPtrs());
alive_ = true;
- // Make sure our prefs are registered, then schedule the first check.
- ScheduleNextCheck(DetermineFirstCheckDelay());
+ // Check soon, and set up the first delayed check.
+ if (!g_skip_scheduled_checks_for_tests) {
+ CheckSoon();
+ ScheduleNextCheck();
+ }
}
void ExtensionUpdater::Stop() {
@@ -230,55 +195,29 @@
extension_prefs_ = NULL;
prefs_ = NULL;
profile_ = NULL;
- timer_.Stop();
will_check_soon_ = false;
downloader_.reset();
update_service_ = nullptr;
}
-void ExtensionUpdater::ScheduleNextCheck(const base::TimeDelta& target_delay) {
+void ExtensionUpdater::ScheduleNextCheck() {
DCHECK(alive_);
- DCHECK(!timer_.IsRunning());
- DCHECK(target_delay >=
- base::TimeDelta::FromSeconds(kScheduleNextCheckMinGapSecs));
-
- // Add +/- 10% random jitter.
- double delay_ms = target_delay.InMillisecondsF();
- double jitter_factor = (RandDouble() * .2) - 0.1;
- delay_ms += delay_ms * jitter_factor;
- base::TimeDelta actual_delay =
- base::TimeDelta::FromMilliseconds(static_cast<int64_t>(delay_ms));
-
- // Save the time of next check.
- base::Time next = base::Time::Now() + actual_delay;
- prefs_->SetInt64(pref_names::kNextUpdateCheck, next.ToInternalValue());
-
- timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
+ // Jitter the frequency by +/- 20%.
+ const double jitter_factor = RandDouble() * 0.4 + 0.8;
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
+ static_cast<int64_t>(frequency_.InMilliseconds() * jitter_factor));
+ base::PostDelayedTaskWithTraits(
+ FROM_HERE, {base::TaskPriority::BEST_EFFORT, BrowserThread::UI},
+ base::BindOnce(&ExtensionUpdater::NextCheck,
+ weak_ptr_factory_.GetWeakPtr()),
+ delay);
}
-void ExtensionUpdater::TimerFired() {
- DCHECK(alive_);
+void ExtensionUpdater::NextCheck() {
+ if (!alive_)
+ return;
CheckNow(CheckParams());
-
- // If the user has overridden the update frequency, don't bother reporting
- // this.
- if (frequency_seconds_ == kDefaultUpdateFrequencySeconds) {
- base::Time last = base::Time::FromInternalValue(
- prefs_->GetInt64(pref_names::kLastUpdateCheck));
- if (last.ToInternalValue() != 0) {
- // Use counts rather than time so we can use minutes rather than millis.
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "Extensions.UpdateCheckGap", (base::Time::Now() - last).InMinutes(),
- base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
- base::TimeDelta::FromDays(40).InMinutes(),
- 50); // 50 buckets seems to be the default.
- }
- }
-
- // Save the last check time, and schedule the next check.
- int64_t now = base::Time::Now().ToInternalValue();
- prefs_->SetInt64(pref_names::kLastUpdateCheck, now);
- ScheduleNextCheck(base::TimeDelta::FromSeconds(frequency_seconds_));
+ ScheduleNextCheck();
}
void ExtensionUpdater::CheckSoon() {
@@ -286,7 +225,7 @@
if (will_check_soon_)
return;
if (base::PostTaskWithTraits(
- FROM_HERE, {BrowserThread::UI},
+ FROM_HERE, {base::TaskPriority::BEST_EFFORT, BrowserThread::UI},
base::BindOnce(&ExtensionUpdater::DoCheckSoon,
weak_ptr_factory_.GetWeakPtr()))) {
will_check_soon_ = true;
@@ -304,10 +243,6 @@
extension_cache_ = extension_cache;
}
-void ExtensionUpdater::StopTimerForTesting() {
- timer_.Stop();
-}
-
void ExtensionUpdater::DoCheckSoon() {
DCHECK(will_check_soon_);
CheckNow(CheckParams());
@@ -706,4 +641,15 @@
requests_in_progress_.erase(request_id);
}
+ExtensionUpdater::ScopedSkipScheduledCheckForTest::
+ ScopedSkipScheduledCheckForTest() {
+ DCHECK(!g_skip_scheduled_checks_for_tests);
+ g_skip_scheduled_checks_for_tests = true;
+}
+
+ExtensionUpdater::ScopedSkipScheduledCheckForTest::
+ ~ScopedSkipScheduledCheckForTest() {
+ g_skip_scheduled_checks_for_tests = false;
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
index 656484e..3e4f1b56 100644
--- a/chrome/browser/extensions/updater/extension_updater.h
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -18,8 +18,6 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "extensions/browser/extension_registry_observer.h"
@@ -78,9 +76,9 @@
// right away.
bool install_immediately;
- // An extension update check can be originated by a user or by a timer.
- // When the value of |fetch_priority| is FOREGROUND, the update request was
- // initiated by a user.
+ // An extension update check can be originated by a user or by a scheduled
+ // task. When the value of |fetch_priority| is FOREGROUND, the update
+ // request was initiated by a user.
ManifestFetchData::FetchPriority fetch_priority;
// Callback to call when the update check is complete. Can be null, if
@@ -88,6 +86,18 @@
FinishedCallback callback;
};
+ // A class for use in tests to skip scheduled update checks for extensions
+ // during the lifetime of an instance of it. Only one instance should be alive
+ // at any given time.
+ class ScopedSkipScheduledCheckForTest {
+ public:
+ ScopedSkipScheduledCheckForTest();
+ ~ScopedSkipScheduledCheckForTest();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedSkipScheduledCheckForTest);
+ };
+
// Holds a pointer to the passed |service|, using it for querying installed
// extensions and installing updated ones. The |frequency_seconds| parameter
// controls how often update checks are scheduled.
@@ -128,9 +138,6 @@
// Overrides the extension cache with |extension_cache| for testing.
void SetExtensionCacheForTesting(ExtensionCache* extension_cache);
- // Stop the timer to prevent scheduled updates for testing.
- void StopTimerForTesting();
-
private:
friend class ExtensionUpdaterTest;
friend class ExtensionUpdaterFileHandler;
@@ -170,14 +177,9 @@
// |downloader|.
void EnsureDownloaderCreated();
- // Computes when to schedule the first update check.
- base::TimeDelta DetermineFirstCheckDelay();
-
- // Sets the timer to call TimerFired after roughly |target_delay| from now.
- // To help spread load evenly on servers, this method adds some random
- // jitter. It also saves the scheduled time so it can be reloaded on
- // browser restart.
- void ScheduleNextCheck(const base::TimeDelta& target_delay);
+ // Schedules a task to call NextCheck after |frequency_| delay, plus
+ // or minus 0 to 20% (to help spread load evenly on servers).
+ void ScheduleNextCheck();
// Add fetch records for extensions that are installed to the downloader,
// ignoring |pending_ids| so the extension isn't fetched again.
@@ -187,8 +189,8 @@
ManifestFetchData::FetchPriority fetch_priority,
ExtensionUpdateCheckParams* update_check_params);
- // BaseTimer::ReceiverMethod callback.
- void TimerFired();
+ // Conduct a check as scheduled by ScheduleNextCheck.
+ void NextCheck();
// Posted by CheckSoon().
void DoCheckSoon();
@@ -254,8 +256,8 @@
// shutdown.
UpdateService* update_service_;
- base::OneShotTimer timer_;
- int frequency_seconds_;
+ bool do_scheduled_checks_;
+ base::TimeDelta frequency_;
bool will_check_soon_;
ExtensionPrefs* extension_prefs_;
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
index 6013e41..2d8d76f 100644
--- a/chrome/browser/extensions/updater/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -685,11 +685,7 @@
base::RunLoop().RunUntilIdle();
}
- void SimulateTimerFired(ExtensionUpdater* updater) {
- EXPECT_TRUE(updater->timer_.IsRunning());
- updater->timer_.Stop();
- updater->TimerFired();
- }
+ void SimulateTimerFired(ExtensionUpdater* updater) { updater->NextCheck(); }
// Adds a Result with the given data to results.
void AddParseResult(const std::string& id,
@@ -1436,6 +1432,7 @@
}
void TestSingleExtensionDownloading(bool pending, bool retry, bool fail) {
+ ExtensionUpdater::ScopedSkipScheduledCheckForTest skip_scheduled_checks;
std::unique_ptr<ServiceForDownloadTests> service =
std::make_unique<ServiceForDownloadTests>(
prefs_.get(), test_shared_url_loader_factory_);
@@ -2589,6 +2586,8 @@
service.GetDownloaderFactory());
EXPECT_FALSE(updater.WillCheckSoon());
updater.Start();
+ EXPECT_TRUE(updater.WillCheckSoon());
+ RunUntilIdle();
EXPECT_FALSE(updater.WillCheckSoon());
updater.CheckSoon();
EXPECT_TRUE(updater.WillCheckSoon());
@@ -2648,23 +2647,6 @@
RunUntilIdle();
}
-// Tests that we don't get a DCHECK failure when the next check time saved in
-// prefs happens to be within one second of startup.
-TEST_F(ExtensionUpdaterTest, TestPersistedNextCheckTime) {
- base::Time next_check_time =
- base::Time::Now() + base::TimeDelta::FromMilliseconds(500);
- prefs_->pref_service()->SetInt64(pref_names::kNextUpdateCheck,
- next_check_time.ToInternalValue());
- ServiceForManifestTests service(prefs_.get(),
- test_shared_url_loader_factory_);
- ExtensionUpdater updater(&service, service.extension_prefs(),
- service.pref_service(), service.profile(),
- kDefaultUpdateFrequencySeconds, nullptr,
- service.GetDownloaderFactory());
- updater.Start();
- updater.Stop();
-}
-
TEST_F(ExtensionUpdaterTest, TestManifestFetchDataAddExtension) {
TestManifestAddExtension(ManifestFetchData::FetchPriority::BACKGROUND,
ManifestFetchData::FetchPriority::BACKGROUND,
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 6340ee5f..8d1ccf3 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1114,6 +1114,8 @@
MockConfigurationPolicyProvider provider_;
std::unique_ptr<extensions::ExtensionCacheFake> test_extension_cache_;
extensions::ScopedIgnoreContentVerifierForTest ignore_content_verifier_;
+ extensions::ExtensionUpdater::ScopedSkipScheduledCheckForTest
+ skip_scheduled_extension_checks_;
};
// A subclass of PolicyTest that runs each test with the old interstitial code
@@ -2810,9 +2812,6 @@
extensions::ExtensionPrefs* extension_prefs =
extensions::ExtensionPrefs::Get(browser()->profile());
- // Explicitly stop the timer to avoid all scheduled extension auto-updates.
- service->updater()->StopTimerForTesting();
-
// Install the extension.
EXPECT_TRUE(InstallExtension(kGoodV1CrxName));
EXPECT_TRUE(registry->enabled_extensions().Contains(kGoodCrxId));
@@ -2883,9 +2882,6 @@
extensions::ExtensionPrefs* extension_prefs =
extensions::ExtensionPrefs::Get(browser()->profile());
- // Explicitly stop the timer to avoid all scheduled extension auto-updates.
- service->updater()->StopTimerForTesting();
-
// Set the policy to require an even higher minimum version this time.
{
extensions::ExtensionManagementPolicyUpdater management_policy(&provider_);
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 554195b..326be03 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -384,6 +384,10 @@
// perhaps be kept around longer than the others.
const char kHttpServerProperties[] = "net.http_server_properties";
+// Deprecated 1/2019.
+const char kNextUpdateCheck[] = "extensions.autoupdate.next_check";
+const char kLastUpdateCheck[] = "extensions.autoupdate.last_check";
+
// Register prefs used only for migration (clearing or moving to a new key).
void RegisterProfilePrefsForMigration(
user_prefs::PrefRegistrySyncable* registry) {
@@ -411,6 +415,8 @@
registry->RegisterIntegerPref(kNuxOnboardGroup, 0);
registry->RegisterDictionaryPref(kHttpServerProperties,
PrefRegistry::LOSSY_PREF);
+ registry->RegisterIntegerPref(kLastUpdateCheck, 0);
+ registry->RegisterIntegerPref(kNextUpdateCheck, 0);
}
} // namespace
@@ -891,4 +897,8 @@
// Added 12/2018.
profile_prefs->ClearPref(prefs::kDataSaverPromptsShown);
#endif
+
+ // Added 1/2019.
+ profile_prefs->ClearPref(kLastUpdateCheck);
+ profile_prefs->ClearPref(kNextUpdateCheck);
}