blob: 8a8c1188ea83fa95923b8ac5770d9df6ab7d094c [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/containers/contains.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/extensions/corrupted_extension_reinstaller_factory.h"
#include "chrome/browser/extensions/external_provider_manager.h"
#include "content/public/browser/browser_context.h"
#include "extensions/buildflags/buildflags.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
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
// static
CorruptedExtensionReinstaller* CorruptedExtensionReinstaller::Get(
content::BrowserContext* context) {
return CorruptedExtensionReinstallerFactory::GetInstance()
->GetForBrowserContext(context);
}
CorruptedExtensionReinstaller::CorruptedExtensionReinstaller(
content::BrowserContext* context)
: context_(context), backoff_entry_(&kCorruptedReinstallBackoffPolicy) {}
CorruptedExtensionReinstaller::~CorruptedExtensionReinstaller() = default;
// 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,
std::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() {
// Cancels already-scheduled attempts, if any, for a smoother shutdown, by
// invalidating weak pointers stored in postponed tasks.
weak_factory_.InvalidateWeakPtrs();
}
void CorruptedExtensionReinstaller::Fire() {
scheduled_fire_pending_ = false;
// If there's nothing to repair, then bail out.
if (!HasAnyReinstallForCorruption())
return;
ExternalProviderManager::Get(context_)->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