blob: 31ccd6a9c3d76913df047f6f05a4f84c9aeb49ae [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/api/declarative_net_request/global_rules_tracker.h"
#include "extensions/browser/api/declarative_net_request/utils.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/api/declarative_net_request.h"
#include "extensions/common/extension.h"
namespace extensions {
namespace declarative_net_request {
namespace {
namespace dnr_api = api::declarative_net_request;
// Returns the total allocated global rule count, as maintained in extension
// prefs from the set of installed extensions in the registry.
size_t CalculateAllocatedGlobalRuleCount(
const ExtensionPrefs* extension_prefs,
const ExtensionRegistry* extension_registry) {
std::unique_ptr<ExtensionSet> installed_extensions =
extension_registry->GenerateInstalledExtensionsSet();
// For each extension, fetch its allocated rules count and add it to
// |allocated_global_rule_count_|.
size_t allocated_rule_count;
size_t allocated_global_rule_count = 0;
for (const auto& extension : *installed_extensions) {
if (extension_prefs->GetDNRAllocatedGlobalRuleCount(
extension->id(), &allocated_rule_count)) {
allocated_global_rule_count += allocated_rule_count;
}
}
return allocated_global_rule_count;
}
// Returns the total allocated global rule count, as maintained in extension
// prefs from the set of installed extensions from prefs. This should be called
// only if the extension registry has not been populated yet (e.g. when a
// browser session has just started).
size_t CalculateInitialAllocatedGlobalRuleCount(
const ExtensionPrefs* extension_prefs) {
std::unique_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
extension_prefs->GetInstalledExtensionsInfo());
// For each extension, fetch its allocated rules count and add it to
// |allocated_global_rule_count_|.
size_t allocated_rule_count = 0;
size_t allocated_global_rule_count = 0;
for (const auto& info : *extensions_info) {
// Skip extensions that were loaded from the command-line because we don't
// want those to persist across browser restart.
if (info->extension_location == mojom::ManifestLocation::kCommandLine)
continue;
if (extension_prefs->GetDNRAllocatedGlobalRuleCount(
info->extension_id, &allocated_rule_count)) {
allocated_global_rule_count += allocated_rule_count;
}
}
return allocated_global_rule_count;
}
} // namespace
GlobalRulesTracker::GlobalRulesTracker(ExtensionPrefs* extension_prefs,
ExtensionRegistry* extension_registry)
: allocated_global_rule_count_(
CalculateInitialAllocatedGlobalRuleCount(extension_prefs)),
extension_prefs_(extension_prefs),
extension_registry_(extension_registry) {}
GlobalRulesTracker::~GlobalRulesTracker() = default;
size_t GlobalRulesTracker::GetAllocatedGlobalRuleCountForTesting() const {
DCHECK_EQ(
allocated_global_rule_count_,
CalculateAllocatedGlobalRuleCount(extension_prefs_, extension_registry_));
return allocated_global_rule_count_;
}
bool GlobalRulesTracker::OnExtensionRuleCountUpdated(
const ExtensionId& extension_id,
size_t new_rule_count) {
// Each extension ruleset is allowed to have up to
// |GetMaximumRulesPerRuleset()| rules during indexing.
DCHECK_LE(new_rule_count, static_cast<size_t>(GetMaximumRulesPerRuleset()) *
dnr_api::MAX_NUMBER_OF_STATIC_RULESETS);
bool keep_excess_allocation =
extension_prefs_->GetDNRKeepExcessAllocation(extension_id);
if (new_rule_count <=
static_cast<size_t>(GetStaticGuaranteedMinimumRuleCount())) {
if (!keep_excess_allocation)
ClearExtensionAllocation(extension_id);
return true;
}
size_t old_allocated_rule_count = GetAllocationInPrefs(extension_id);
DCHECK_GE(allocated_global_rule_count_, old_allocated_rule_count);
size_t new_allocated_rule_count =
new_rule_count - GetStaticGuaranteedMinimumRuleCount();
if (new_allocated_rule_count == old_allocated_rule_count)
return true;
if (keep_excess_allocation &&
old_allocated_rule_count > new_allocated_rule_count) {
// Retain the extension's current excess allocation and allow the update.
return true;
}
size_t new_global_rule_count =
(allocated_global_rule_count_ - old_allocated_rule_count) +
new_allocated_rule_count;
// If updating this extension's rule count would cause the global rule count
// to be exceeded, don't commit the update and return false.
if (new_global_rule_count > static_cast<size_t>(GetGlobalStaticRuleLimit()))
return false;
if (keep_excess_allocation) {
DCHECK_GT(new_allocated_rule_count, old_allocated_rule_count);
// The extension is now using more than it's pre-update rule allocation.
// Remove it's ability to keep the excess allocation.
extension_prefs_->SetDNRKeepExcessAllocation(extension_id, false);
}
allocated_global_rule_count_ = new_global_rule_count;
extension_prefs_->SetDNRAllocatedGlobalRuleCount(extension_id,
new_allocated_rule_count);
return true;
}
size_t GlobalRulesTracker::GetUnallocatedRuleCount() const {
return GetGlobalStaticRuleLimit() - allocated_global_rule_count_;
}
size_t GlobalRulesTracker::GetAvailableAllocation(
const ExtensionId& extension_id) const {
return GetUnallocatedRuleCount() + GetAllocationInPrefs(extension_id);
}
void GlobalRulesTracker::ClearExtensionAllocation(
const ExtensionId& extension_id) {
size_t allocated_rule_count = 0;
if (!extension_prefs_->GetDNRAllocatedGlobalRuleCount(
extension_id, &allocated_rule_count)) {
return;
}
DCHECK_GE(allocated_global_rule_count_, allocated_rule_count);
allocated_global_rule_count_ -= allocated_rule_count;
extension_prefs_->SetDNRAllocatedGlobalRuleCount(extension_id,
0 /* rule_count */);
}
size_t GlobalRulesTracker::GetAllocationInPrefs(
const ExtensionId& extension_id) const {
size_t allocated_rule_count = 0;
extension_prefs_->GetDNRAllocatedGlobalRuleCount(extension_id,
&allocated_rule_count);
return allocated_rule_count;
}
} // namespace declarative_net_request
} // namespace extensions