blob: d731f4e20556233f07da6240916ed2514db3d454 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/error_map.h"
#include <utility>
#include "base/lazy_instance.h"
#include "base/macros.h"
namespace extensions {
namespace {
// The maximum number of errors to be stored per extension.
const size_t kMaxErrorsPerExtension = 100;
base::LazyInstance<ErrorList>::DestructorAtExit g_empty_error_list =
LAZY_INSTANCE_INITIALIZER;
// An incrementing counter for the next error id. Overflowing this is very
// unlikely, since the number of errors per extension is capped at 100.
int g_next_error_id = 1;
} // namespace
////////////////////////////////////////////////////////////////////////////////
// ErrorMap::Filter
ErrorMap::Filter::Filter(const std::string& restrict_to_extension_id,
int restrict_to_type,
const std::set<int>& restrict_to_ids,
bool restrict_to_incognito)
: restrict_to_extension_id(restrict_to_extension_id),
restrict_to_type(restrict_to_type),
restrict_to_ids(restrict_to_ids),
restrict_to_incognito(restrict_to_incognito) {
}
ErrorMap::Filter::Filter(const Filter& other) = default;
ErrorMap::Filter::~Filter() {
}
ErrorMap::Filter ErrorMap::Filter::ErrorsForExtension(
const std::string& extension_id) {
return Filter(extension_id, -1, std::set<int>(), false);
}
ErrorMap::Filter ErrorMap::Filter::ErrorsForExtensionWithType(
const std::string& extension_id,
ExtensionError::Type type) {
return Filter(extension_id, type, std::set<int>(), false);
}
ErrorMap::Filter ErrorMap::Filter::ErrorsForExtensionWithIds(
const std::string& extension_id,
const std::set<int>& ids) {
return Filter(extension_id, -1, ids, false);
}
ErrorMap::Filter ErrorMap::Filter::ErrorsForExtensionWithTypeAndIds(
const std::string& extension_id,
ExtensionError::Type type,
const std::set<int>& ids) {
return Filter(extension_id, type, ids, false);
}
ErrorMap::Filter ErrorMap::Filter::IncognitoErrors() {
return Filter(std::string(), -1, std::set<int>(), true);
}
bool ErrorMap::Filter::Matches(const ExtensionError* error) const {
if (restrict_to_type != -1 && restrict_to_type != error->type())
return false;
if (restrict_to_incognito && !error->from_incognito())
return false;
if (!restrict_to_extension_id.empty() &&
error->extension_id() != restrict_to_extension_id)
return false;
if (!restrict_to_ids.empty() && restrict_to_ids.count(error->id()) == 0)
return false;
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ErrorMap::ExtensionEntry
class ErrorMap::ExtensionEntry {
public:
ExtensionEntry();
~ExtensionEntry();
// Delete any errors in the entry that match the given ids and type, if
// provided.
// Returns true if any errors were deleted.
bool DeleteErrors(const ErrorMap::Filter& filter);
// Delete all errors in the entry.
void DeleteAllErrors();
// Add the error to the list, and return a weak reference.
const ExtensionError* AddError(std::unique_ptr<ExtensionError> error);
const ErrorList* list() const { return &list_; }
private:
// The list of all errors associated with the extension. The errors are
// owned by the Entry (in turn owned by the ErrorMap) and are deleted upon
// destruction.
ErrorList list_;
DISALLOW_COPY_AND_ASSIGN(ExtensionEntry);
};
ErrorMap::ExtensionEntry::ExtensionEntry() {
}
ErrorMap::ExtensionEntry::~ExtensionEntry() {
DeleteAllErrors();
}
bool ErrorMap::ExtensionEntry::DeleteErrors(const Filter& filter) {
bool deleted = false;
for (auto iter = list_.begin(); iter != list_.end();) {
if (filter.Matches(iter->get())) {
iter = list_.erase(iter);
deleted = true;
} else {
++iter;
}
}
return deleted;
}
void ErrorMap::ExtensionEntry::DeleteAllErrors() {
list_.clear();
}
const ExtensionError* ErrorMap::ExtensionEntry::AddError(
std::unique_ptr<ExtensionError> error) {
for (auto iter = list_.begin(); iter != list_.end(); ++iter) {
// If we find a duplicate error, remove the old error and add the new one,
// incrementing the occurrence count of the error. We use the new error
// for runtime errors, so we can link to the latest context, inspectable
// view, etc.
if (error->IsEqual(iter->get())) {
error->set_occurrences((*iter)->occurrences() + 1);
error->set_id((*iter)->id());
list_.erase(iter);
break;
}
}
// If there are too many errors for an extension already, limit ourselves to
// the most recent ones.
if (list_.size() >= kMaxErrorsPerExtension)
list_.pop_front();
if (error->id() == 0)
error->set_id(g_next_error_id++);
list_.push_back(std::move(error));
return list_.back().get();
}
////////////////////////////////////////////////////////////////////////////////
// ErrorMap
ErrorMap::ErrorMap() {
}
ErrorMap::~ErrorMap() {
RemoveAllErrors();
}
const ErrorList& ErrorMap::GetErrorsForExtension(
const std::string& extension_id) const {
auto iter = map_.find(extension_id);
return iter != map_.end() ? *iter->second->list() : g_empty_error_list.Get();
}
const ExtensionError* ErrorMap::AddError(
std::unique_ptr<ExtensionError> error) {
std::unique_ptr<ExtensionEntry>& entry = map_[error->extension_id()];
if (!entry)
entry = std::make_unique<ExtensionEntry>();
return entry->AddError(std::move(error));
}
void ErrorMap::RemoveErrors(const Filter& filter,
std::set<std::string>* affected_ids) {
if (!filter.restrict_to_extension_id.empty()) {
auto iter = map_.find(filter.restrict_to_extension_id);
if (iter != map_.end()) {
if (iter->second->DeleteErrors(filter) && affected_ids)
affected_ids->insert(filter.restrict_to_extension_id);
}
} else {
for (auto& key_val : map_) {
if (key_val.second->DeleteErrors(filter) && affected_ids)
affected_ids->insert(key_val.first);
}
}
}
void ErrorMap::RemoveAllErrors() {
map_.clear();
}
} // namespace extensions