diff options
author | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-24 01:06:40 +0000 |
---|---|---|
committer | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-24 01:06:40 +0000 |
commit | 71c10c5ae000e72154770f4e467928a15a05297a (patch) | |
tree | 7c7178d027efdb8d79457bc39d9fd5d4d5df00eb /extensions | |
parent | 54ac189c804de1d3c907aa386f4c7b26f87b7203 (diff) | |
download | chromium_src-71c10c5ae000e72154770f4e467928a15a05297a.zip chromium_src-71c10c5ae000e72154770f4e467928a15a05297a.tar.gz chromium_src-71c10c5ae000e72154770f4e467928a15a05297a.tar.bz2 |
Allow Extension-specific toggling for error reporting
Allow error reporting to be toggled on or off for specific extensions.
Allow type-specific enabling for extension errors (e.g., enabling only manifest or runtime errors).
Create an ErrorMap object for error storage management.
BUG=21734
TBR=finnur@chromium.org (c/b/ui/webui/extensions)
Review URL: https://codereview.chromium.org/58903008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246756 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/browser/error_map.cc | 138 | ||||
-rw-r--r-- | extensions/browser/error_map.h | 84 | ||||
-rw-r--r-- | extensions/browser/error_map_unittest.cc | 147 | ||||
-rw-r--r-- | extensions/browser/extension_error_test_util.cc | 64 | ||||
-rw-r--r-- | extensions/browser/extension_error_test_util.h | 35 | ||||
-rw-r--r-- | extensions/extensions.gyp | 2 |
6 files changed, 470 insertions, 0 deletions
diff --git a/extensions/browser/error_map.cc b/extensions/browser/error_map.cc new file mode 100644 index 0000000..dfcef9f --- /dev/null +++ b/extensions/browser/error_map.cc @@ -0,0 +1,138 @@ +// 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 "base/lazy_instance.h" +#include "base/stl_util.h" +#include "extensions/common/extension.h" + +namespace extensions { +namespace { + +// The maximum number of errors to be stored per extension. +const size_t kMaxErrorsPerExtension = 100; + +base::LazyInstance<ErrorList> g_empty_error_list = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ErrorMap::ExtensionEntry +ErrorMap::ExtensionEntry::ExtensionEntry() { +} + +ErrorMap::ExtensionEntry::~ExtensionEntry() { + DeleteAllErrors(); +} + +void ErrorMap::ExtensionEntry::DeleteAllErrors() { + STLDeleteContainerPointers(list_.begin(), list_.end()); + list_.clear(); +} + +void ErrorMap::ExtensionEntry::DeleteIncognitoErrors() { + ErrorList::iterator iter = list_.begin(); + while (iter != list_.end()) { + if ((*iter)->from_incognito()) { + delete *iter; + iter = list_.erase(iter); + } else { + ++iter; + } + } +} + +void ErrorMap::ExtensionEntry::DeleteErrorsOfType(ExtensionError::Type type) { + ErrorList::iterator iter = list_.begin(); + while (iter != list_.end()) { + if ((*iter)->type() == type) { + delete *iter; + iter = list_.erase(iter); + } else { + ++iter; + } + } +} + +const ExtensionError* ErrorMap::ExtensionEntry::AddError( + scoped_ptr<ExtensionError> error) { + for (ErrorList::iterator 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)) { + error->set_occurrences((*iter)->occurrences() + 1); + delete *iter; + 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) { + delete list_.front(); + list_.pop_front(); + } + + list_.push_back(error.release()); + return list_.back(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ErrorMap +ErrorMap::ErrorMap() { +} + +ErrorMap::~ErrorMap() { + RemoveAllErrors(); +} + +const ErrorList& ErrorMap::GetErrorsForExtension( + const std::string& extension_id) const { + EntryMap::const_iterator iter = map_.find(extension_id); + return iter != map_.end() ? *iter->second->list() : g_empty_error_list.Get(); +} + +const ExtensionError* ErrorMap::AddError(scoped_ptr<ExtensionError> error) { + EntryMap::iterator iter = map_.find(error->extension_id()); + if (iter == map_.end()) { + iter = map_.insert(std::pair<std::string, ExtensionEntry*>( + error->extension_id(), new ExtensionEntry)).first; + } + return iter->second->AddError(error.Pass()); +} + +void ErrorMap::Remove(const std::string& extension_id) { + EntryMap::iterator iter = map_.find(extension_id); + if (iter == map_.end()) + return; + + delete iter->second; + map_.erase(iter); +} + +void ErrorMap::RemoveErrorsForExtensionOfType(const std::string& extension_id, + ExtensionError::Type type) { + EntryMap::iterator iter = map_.find(extension_id); + if (iter != map_.end()) + iter->second->DeleteErrorsOfType(type); +} + +void ErrorMap::RemoveIncognitoErrors() { + for (EntryMap::iterator iter = map_.begin(); iter != map_.end(); ++iter) + iter->second->DeleteIncognitoErrors(); +} + +void ErrorMap::RemoveAllErrors() { + for (EntryMap::iterator iter = map_.begin(); iter != map_.end(); ++iter) { + iter->second->DeleteAllErrors(); + delete iter->second; + } + map_.clear(); +} + +} // namespace extensions diff --git a/extensions/browser/error_map.h b/extensions/browser/error_map.h new file mode 100644 index 0000000..2dcff22 --- /dev/null +++ b/extensions/browser/error_map.h @@ -0,0 +1,84 @@ +// 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. + +#ifndef EXTENSIONS_BROWSER_ERROR_MAP_H_ +#define EXTENSIONS_BROWSER_ERROR_MAP_H_ + +#include <deque> +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/extension_error.h" + +namespace extensions { + +typedef std::deque<const ExtensionError*> ErrorList; + +// An ErrorMap is responsible for storing Extension-related errors, keyed by +// Extension ID. The errors are owned by the ErrorMap, and are deleted upon +// destruction. +class ErrorMap { + public: + explicit ErrorMap(); + ~ErrorMap(); + + // Return the list of all errors associated with the given extension. + const ErrorList& GetErrorsForExtension(const std::string& extension_id) const; + + // Add the |error| to the ErrorMap. + const ExtensionError* AddError(scoped_ptr<ExtensionError> error); + + // Remove an extension from the ErrorMap, deleting all associated errors. + void Remove(const std::string& extension_id); + // Remove all errors of a given type for an extension. + void RemoveErrorsForExtensionOfType(const std::string& extension_id, + ExtensionError::Type type); + // Remove all incognito errors for all extensions. + void RemoveIncognitoErrors(); + // Remove all errors for all extensions, and clear the map. + void RemoveAllErrors(); + + size_t size() const { return map_.size(); } + + private: + // An Entry is created for each Extension ID, and stores the errors related to + // that Extension. + class ExtensionEntry { + public: + explicit ExtensionEntry(); + ~ExtensionEntry(); + + // Delete all errors associated with this extension. + void DeleteAllErrors(); + // Delete all incognito errors associated with this extension. + void DeleteIncognitoErrors(); + // Delete all errors of the given |type| associated with this extension. + void DeleteErrorsOfType(ExtensionError::Type type); + + // Add the error to the list, and return a weak reference. + const ExtensionError* AddError(scoped_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); + }; + typedef std::map<std::string, ExtensionEntry*> EntryMap; + + // The mapping between Extension IDs and their corresponding Entries. + EntryMap map_; + + DISALLOW_COPY_AND_ASSIGN(ErrorMap); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_ERROR_MAP_H_ diff --git a/extensions/browser/error_map_unittest.cc b/extensions/browser/error_map_unittest.cc new file mode 100644 index 0000000..8330c6a --- /dev/null +++ b/extensions/browser/error_map_unittest.cc @@ -0,0 +1,147 @@ +// 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "extensions/browser/extension_error.h" +#include "extensions/browser/extension_error_test_util.h" +#include "extensions/common/constants.h" +#include "extensions/common/id_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +using error_test_util::CreateNewRuntimeError; + +class ErrorMapUnitTest : public testing::Test { + public: + ErrorMapUnitTest() { } + virtual ~ErrorMapUnitTest() { } + + virtual void SetUp() OVERRIDE { + testing::Test::SetUp(); + } + + protected: + ErrorMap errors_; +}; + +// Test adding errors, and removing them by reference, by incognito status, +// and in bulk. +TEST_F(ErrorMapUnitTest, AddAndRemoveErrors) { + ASSERT_EQ(0u, errors_.size()); + + const size_t kNumTotalErrors = 6; + const size_t kNumNonIncognitoErrors = 3; + const std::string kId = id_util::GenerateId("id"); + // Populate with both incognito and non-incognito errors (evenly distributed). + for (size_t i = 0; i < kNumTotalErrors; ++i) { + ASSERT_TRUE(errors_.AddError( + CreateNewRuntimeError(kId, base::UintToString(i), i % 2 == 0))); + } + + // There should only be one entry in the map, since errors are stored in lists + // keyed by extension id. + ASSERT_EQ(1u, errors_.size()); + + ASSERT_EQ(kNumTotalErrors, errors_.GetErrorsForExtension(kId).size()); + + // Remove the incognito errors; three errors should remain, and all should + // be from non-incognito contexts. + errors_.RemoveIncognitoErrors(); + const ErrorList& list = errors_.GetErrorsForExtension(kId); + ASSERT_EQ(kNumNonIncognitoErrors, list.size()); + for (size_t i = 0; i < list.size(); ++i) + ASSERT_FALSE(list[i]->from_incognito()); + + // Add another error for a different extension id. + const std::string kSecondId = id_util::GenerateId("id2"); + ASSERT_TRUE(errors_.AddError(CreateNewRuntimeError(kSecondId, "foo"))); + + // There should be two entries now, one for each id, and there should be one + // error for the second extension. + ASSERT_EQ(2u, errors_.size()); + ASSERT_EQ(1u, errors_.GetErrorsForExtension(kSecondId).size()); + + // Remove all errors for the second id. + errors_.Remove(kSecondId); + ASSERT_EQ(1u, errors_.size()); + ASSERT_EQ(0u, errors_.GetErrorsForExtension(kSecondId).size()); + // First extension should be unaffected. + ASSERT_EQ(kNumNonIncognitoErrors, + errors_.GetErrorsForExtension(kId).size()); + + // Remove remaining errors. + errors_.RemoveAllErrors(); + ASSERT_EQ(0u, errors_.size()); + ASSERT_EQ(0u, errors_.GetErrorsForExtension(kId).size()); +} + +// Test that if we add enough errors, only the most recent +// kMaxErrorsPerExtension are kept. +TEST_F(ErrorMapUnitTest, ExcessiveErrorsGetCropped) { + ASSERT_EQ(0u, errors_.size()); + + // This constant matches one of the same name in error_console.cc. + const size_t kMaxErrorsPerExtension = 100; + const size_t kNumExtraErrors = 5; + const std::string kId = id_util::GenerateId("id"); + + // Add new errors, with each error's message set to its number. + for (size_t i = 0; i < kMaxErrorsPerExtension + kNumExtraErrors; ++i) { + ASSERT_TRUE(errors_.AddError( + CreateNewRuntimeError(kId, base::UintToString(i)))); + } + + ASSERT_EQ(1u, errors_.size()); + + const ErrorList& list = errors_.GetErrorsForExtension(kId); + ASSERT_EQ(kMaxErrorsPerExtension, list.size()); + + // We should have popped off errors in the order they arrived, so the + // first stored error should be the 6th reported (zero-based)... + ASSERT_EQ(base::UintToString16(kNumExtraErrors), + list.front()->message()); + // ..and the last stored should be the 105th reported. + ASSERT_EQ(base::UintToString16(kMaxErrorsPerExtension + kNumExtraErrors - 1), + list.back()->message()); +} + +// Test to ensure that the error console will not add duplicate errors, but will +// keep the latest version of an error. +TEST_F(ErrorMapUnitTest, DuplicateErrorsAreReplaced) { + ASSERT_EQ(0u, errors_.size()); + + const std::string kId = id_util::GenerateId("id"); + const size_t kNumErrors = 3u; + + // Report three errors. + for (size_t i = 0; i < kNumErrors; ++i) { + ASSERT_TRUE(errors_.AddError( + CreateNewRuntimeError(kId, base::UintToString(i)))); + } + + // Create an error identical to the second error reported, save its + // location, and add it to the error map. + scoped_ptr<ExtensionError> runtime_error2 = + CreateNewRuntimeError(kId, base::UintToString(1u)); + const ExtensionError* weak_error = runtime_error2.get(); + ASSERT_TRUE(errors_.AddError(runtime_error2.Pass())); + + // We should only have three errors stored, since two of the four reported + // were identical, and the older should have been replaced. + ASSERT_EQ(1u, errors_.size()); + const ErrorList& list = errors_.GetErrorsForExtension(kId); + ASSERT_EQ(kNumErrors, list.size()); + + // The duplicate error should be the last reported (pointer comparison)... + ASSERT_EQ(weak_error, list.back()); + // ... and should have two reported occurrences. + ASSERT_EQ(2u, list.back()->occurrences()); +} + +} // namespace extensions diff --git a/extensions/browser/extension_error_test_util.cc b/extensions/browser/extension_error_test_util.cc new file mode 100644 index 0000000..cdf4917 --- /dev/null +++ b/extensions/browser/extension_error_test_util.cc @@ -0,0 +1,64 @@ +// 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/extension_error_test_util.h" + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/common/url_constants.h" +#include "extensions/browser/extension_error.h" +#include "extensions/common/constants.h" +#include "extensions/common/stack_frame.h" +#include "url/gurl.h" + +namespace extensions { +namespace error_test_util { + +namespace { +const char kDefaultStackTrace[] = "function_name (https://url.com:1:1)"; +} + +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, + const std::string& message, + bool from_incognito) { + StackTrace stack_trace; + scoped_ptr<StackFrame> frame = + StackFrame::CreateFromText(base::UTF8ToUTF16(kDefaultStackTrace)); + CHECK(frame.get()); + stack_trace.push_back(*frame); + + base::string16 source = + base::UTF8ToUTF16(std::string(kExtensionScheme) + + content::kStandardSchemeSeparator + + extension_id); + + return scoped_ptr<ExtensionError>(new RuntimeError( + extension_id, + from_incognito, + source, + base::UTF8ToUTF16(message), + stack_trace, + GURL::EmptyGURL(), // no context url + logging::LOG_INFO, + 0, 0 /* Render [View|Process] ID */ )); +} + +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, const std::string& message) { + return CreateNewRuntimeError(extension_id, message, false); +} + +scoped_ptr<ExtensionError> CreateNewManifestError( + const std::string& extension_id, const std::string& message) { + return scoped_ptr<ExtensionError>( + new ManifestError(extension_id, + base::UTF8ToUTF16(message), + base::EmptyString16(), + base::EmptyString16())); +} + +} // namespace error_test_util +} // namespace extensions diff --git a/extensions/browser/extension_error_test_util.h b/extensions/browser/extension_error_test_util.h new file mode 100644 index 0000000..4866cef --- /dev/null +++ b/extensions/browser/extension_error_test_util.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef EXTENSIONS_BROWSER_EXTENSION_ERROR_TEST_UTIL_H_ +#define EXTENSIONS_BROWSER_EXTENSION_ERROR_TEST_UTIL_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace extensions { + +class ExtensionError; + +namespace error_test_util { + +// Create a new RuntimeError. +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, + const std::string& message, + bool from_incognito); + +// Create a new RuntimeError; incognito defaults to "false". +scoped_ptr<ExtensionError> CreateNewRuntimeError( + const std::string& extension_id, const std::string& message); + +// Create a new ManifestError. +scoped_ptr<ExtensionError> CreateNewManifestError( + const std::string& extension_id, const std::string& mnessage); + +} // namespace error_test_util +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_EXTENSION_ERROR_TEST_UTIL_H_ diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index e58aaa3..770f2d4 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -168,6 +168,8 @@ 'browser/admin_policy.cc', 'browser/admin_policy.h', 'browser/app_sorting.h', + 'browser/error_map.cc', + 'browser/error_map.h', 'browser/event_listener_map.cc', 'browser/event_listener_map.h', 'browser/event_router.cc', |