#include "extensions/renderer/extension_throttle_manager.h"
#include <utility>
#include "base/containers/cxx20_erase.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "extensions/common/constants.h"
#include "extensions/renderer/extension_url_loader_throttle.h"
#include "net/base/url_util.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
namespace extensions {
const unsigned int ExtensionThrottleManager::kMaximumNumberOfEntries = 1500;
const unsigned int ExtensionThrottleManager::kRequestsBetweenCollecting = 200;
ExtensionThrottleManager::ExtensionThrottleManager()
: requests_since_last_gc_(0) {
url_id_replacements_.ClearPassword();
url_id_replacements_.ClearUsername();
url_id_replacements_.ClearQuery();
url_id_replacements_.ClearRef();
}
ExtensionThrottleManager::~ExtensionThrottleManager() {
base::AutoLock auto_lock(lock_);
url_entries_.clear();
}
std::unique_ptr<blink::URLLoaderThrottle>
ExtensionThrottleManager::MaybeCreateURLLoaderThrottle(
const blink::WebURLRequest& request) {
if (request.SiteForCookies().scheme() != extensions::kExtensionScheme
#if defined(OHOS_ARKWEB_EXTENSIONS)
&& request.SiteForCookies().scheme() != extensions::kArkwebExtensionScheme
#endif
)
return nullptr;
return std::make_unique<ExtensionURLLoaderThrottle>(this);
}
ExtensionThrottleEntry* ExtensionThrottleManager::RegisterRequestUrl(
const GURL& url) {
std::string url_id = GetIdFromUrl(url);
GarbageCollectEntriesIfNecessary();
std::unique_ptr<ExtensionThrottleEntry>& entry = url_entries_[url_id];
if (entry && entry->IsEntryOutdated())
entry.reset();
if (!entry) {
if (backoff_policy_for_tests_) {
entry = std::make_unique<ExtensionThrottleEntry>(
url_id, backoff_policy_for_tests_.get());
} else {
entry = std::make_unique<ExtensionThrottleEntry>(url_id);
}
if (net::IsLocalhost(url)) {
entry->DisableBackoffThrottling();
}
}
return entry.get();
}
bool ExtensionThrottleManager::ShouldRejectRequest(const GURL& request_url) {
base::AutoLock auto_lock(lock_);
return RegisterRequestUrl(request_url)->ShouldRejectRequest();
}
bool ExtensionThrottleManager::ShouldRejectRedirect(
const GURL& request_url,
const net::RedirectInfo& redirect_info) {
{
base::AutoLock auto_lock(lock_);
auto it = url_entries_.find(GetIdFromUrl(request_url));
if (it != url_entries_.end())
it->second->UpdateWithResponse(redirect_info.status_code);
}
return ShouldRejectRequest(redirect_info.new_url);
}
void ExtensionThrottleManager::WillProcessResponse(
const GURL& response_url,
const network::mojom::URLResponseHead& response_head) {
if (response_head.network_accessed) {
base::AutoLock auto_lock(lock_);
auto it = url_entries_.find(GetIdFromUrl(response_url));
if (it != url_entries_.end())
it->second->UpdateWithResponse(response_head.headers->response_code());
}
}
void ExtensionThrottleManager::SetBackoffPolicyForTests(
std::unique_ptr<net::BackoffEntry::Policy> policy) {
base::AutoLock auto_lock(lock_);
backoff_policy_for_tests_ = std::move(policy);
}
void ExtensionThrottleManager::OverrideEntryForTests(
const GURL& url,
std::unique_ptr<ExtensionThrottleEntry> entry) {
base::AutoLock auto_lock(lock_);
std::string url_id = GetIdFromUrl(url);
GarbageCollectEntriesIfNecessary();
url_entries_[url_id] = std::move(entry);
}
void ExtensionThrottleManager::EraseEntryForTests(const GURL& url) {
base::AutoLock auto_lock(lock_);
std::string url_id = GetIdFromUrl(url);
url_entries_.erase(url_id);
}
void ExtensionThrottleManager::SetOnline(bool is_online) {
base::AutoLock auto_lock(lock_);
url_entries_.clear();
requests_since_last_gc_ = 0;
}
std::string ExtensionThrottleManager::GetIdFromUrl(const GURL& url) const {
if (!url.is_valid())
return url.possibly_invalid_spec();
GURL id = url.ReplaceComponents(url_id_replacements_);
return base::ToLowerASCII(id.spec());
}
void ExtensionThrottleManager::GarbageCollectEntriesIfNecessary() {
requests_since_last_gc_++;
if (requests_since_last_gc_ < kRequestsBetweenCollecting)
return;
requests_since_last_gc_ = 0;
GarbageCollectEntries();
}
void ExtensionThrottleManager::GarbageCollectEntries() {
base::EraseIf(url_entries_, [](const auto& entry) {
return entry.second->IsEntryOutdated();
});
while (url_entries_.size() > kMaximumNumberOfEntries) {
url_entries_.erase(url_entries_.begin());
}
}
}