blob: 50d6a2e74a843a3e2c00f010afab2de3eb366d39 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/corrupted_extension_reinstaller.h"
#include "base/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/extensions/extension_service.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/extension_system.h"
namespace extensions {
namespace {
CorruptedExtensionReinstaller::ReinstallCallback* g_reinstall_action_for_test =
nullptr;
const net::BackoffEntry::Policy kCorruptedReinstallBackoffPolicy = {
// num_errors_to_ignore
1,
// initial_delay_ms (note that we set 'always_use_initial_delay' to false
// below)
100,
// multiply_factor
2,
// jitter_factor
0.1,
// maximum_backoff_ms (30 minutes)
1000 * 60 * 30,
// entry_lifetime_ms (6 hours)
1000 * 60 * 60 * 6,
// always_use_initial_delay
false,
};
} // namespace
CorruptedExtensionReinstaller::CorruptedExtensionReinstaller(
content::BrowserContext* context)
: context_(context), backoff_entry_(&kCorruptedReinstallBackoffPolicy) {}
CorruptedExtensionReinstaller::~CorruptedExtensionReinstaller() {}
// static
void CorruptedExtensionReinstaller::set_reinstall_action_for_test(
ReinstallCallback* action) {
g_reinstall_action_for_test = action;
}
void CorruptedExtensionReinstaller::RecordPolicyReinstallReason(
PolicyReinstallReason reason_for_uma) {
base::UmaHistogramEnumeration("Extensions.CorruptPolicyExtensionDetected3",
reason_for_uma);
}
void CorruptedExtensionReinstaller::ExpectReinstallForCorruption(
const ExtensionId& id,
absl::optional<PolicyReinstallReason> reason_for_uma,
mojom::ManifestLocation manifest_location_for_uma) {
if (base::Contains(expected_reinstalls_, id))
return;
expected_reinstalls_[id] = base::TimeTicks::Now();
if (reason_for_uma)
RecordPolicyReinstallReason(*reason_for_uma);
}
void CorruptedExtensionReinstaller::MarkResolved(const ExtensionId& id) {
if (!base::Contains(expected_reinstalls_, id))
return;
base::TimeDelta latency = base::TimeTicks::Now() - expected_reinstalls_[id];
base::UmaHistogramLongTimes("Extensions.CorruptPolicyExtensionResolved",
latency);
LOG(ERROR) << "Corrupted extension " << id << " reinstalled with latency "
<< latency;
expected_reinstalls_.erase(id);
}
bool CorruptedExtensionReinstaller::IsReinstallForCorruptionExpected(
const ExtensionId& id) const {
return base::Contains(expected_reinstalls_, id);
}
bool CorruptedExtensionReinstaller::HasAnyReinstallForCorruption() const {
return !expected_reinstalls_.empty();
}
const std::map<ExtensionId, base::TimeTicks>&
CorruptedExtensionReinstaller::GetExpectedReinstalls() const {
return expected_reinstalls_;
}
void CorruptedExtensionReinstaller::NotifyExtensionDisabledDueToCorruption() {
ScheduleNextReinstallAttempt();
}
void CorruptedExtensionReinstaller::Shutdown() {
// Cancel already scheduled attempts by invalidating weak pointers stored in
// postponed tasks.
weak_factory_.InvalidateWeakPtrs();
}
void CorruptedExtensionReinstaller::Fire() {
scheduled_fire_pending_ = false;
ExtensionSystem* system = ExtensionSystem::Get(context_);
ExtensionService* service = system->extension_service();
// If there's nothing to repair, then bail out.
if (!HasAnyReinstallForCorruption())
return;
service->CheckForExternalUpdates();
ScheduleNextReinstallAttempt();
}
base::TimeDelta CorruptedExtensionReinstaller::GetNextFireDelay() {
backoff_entry_.InformOfRequest(false);
return backoff_entry_.GetTimeUntilRelease();
}
void CorruptedExtensionReinstaller::ScheduleNextReinstallAttempt() {
if (scheduled_fire_pending_)
return;
scheduled_fire_pending_ = true;
base::TimeDelta reinstall_delay = GetNextFireDelay();
base::OnceClosure callback = base::BindOnce(
&CorruptedExtensionReinstaller::Fire, weak_factory_.GetWeakPtr());
if (g_reinstall_action_for_test) {
g_reinstall_action_for_test->Run(std::move(callback), reinstall_delay);
} else {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, std::move(callback), reinstall_delay);
}
}
} // namespace extensions