| // 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 |