summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-03 21:08:30 +0000
committerrdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-03 21:08:30 +0000
commitb191e2d3d6f5040a766ceb4c5d136a1a5edf1ad0 (patch)
tree89690fee087dbda21d3da91acadf8fb7db31065b
parentfb53f2673d110adf08933a86d6371c07d44d9dad (diff)
downloadchromium_src-b191e2d3d6f5040a766ceb4c5d136a1a5edf1ad0.zip
chromium_src-b191e2d3d6f5040a766ceb4c5d136a1a5edf1ad0.tar.gz
chromium_src-b191e2d3d6f5040a766ceb4c5d136a1a5edf1ad0.tar.bz2
Catch ManifestErrors with ErrorConsole; Add Browsertests
Add backend support for catching manifest install warnings with the ErrorConsole. Forked from https://codereview.chromium.org/22938005 in order to reduce size and separate backend and frontend CLs. BUG=21734 Review URL: https://chromiumcodereview.appspot.com/23684015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221020 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/error_console/error_console.cc73
-rw-r--r--chrome/browser/extensions/error_console/error_console.h12
-rw-r--r--chrome/browser/extensions/error_console/error_console_browsertest.cc100
-rw-r--r--chrome/browser/extensions/extension_system.cc2
-rw-r--r--chrome/browser/extensions/test_extension_system.cc2
-rw-r--r--chrome/common/extensions/extension_manifest_constants.h2
-rw-r--r--chrome/common/extensions/permissions/permissions_data.cc7
-rw-r--r--chrome/test/data/extensions/error_console/manifest_warnings/manifest.json9
-rw-r--r--extensions/browser/extension_error.cc8
-rw-r--r--extensions/browser/extension_error.h14
-rw-r--r--extensions/common/manifest.cc4
-rw-r--r--extensions/common/manifest_constants.cc8
-rw-r--r--extensions/common/manifest_constants.h5
13 files changed, 227 insertions, 19 deletions
diff --git a/chrome/browser/extensions/error_console/error_console.cc b/chrome/browser/extensions/error_console/error_console.cc
index 2b0432f..b0193b3 100644
--- a/chrome/browser/extensions/error_console/error_console.cc
+++ b/chrome/browser/extensions/error_console/error_console.cc
@@ -5,16 +5,20 @@
#include "chrome/browser/extensions/error_console/error_console.h"
#include <list>
+#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/prefs/pref_service.h"
#include "base/stl_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_set.h"
#include "chrome/common/extensions/feature_switch.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_details.h"
@@ -42,6 +46,21 @@ void DeleteIncognitoErrorsFromList(ErrorConsole::ErrorList* list) {
}
}
+// Iterate through an error list and remove and delete all errors of a given
+// |type|.
+void DeleteErrorsOfTypeFromList(ErrorConsole::ErrorList* list,
+ ExtensionError::Type type) {
+ ErrorConsole::ErrorList::iterator iter = list->begin();
+ while (iter != list->end()) {
+ if ((*iter)->type() == type) {
+ delete *iter;
+ iter = list->erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
base::LazyInstance<ErrorConsole::ErrorList> g_empty_error_list =
LAZY_INSTANCE_INITIALIZER;
@@ -50,8 +69,9 @@ base::LazyInstance<ErrorConsole::ErrorList> g_empty_error_list =
void ErrorConsole::Observer::OnErrorConsoleDestroyed() {
}
-ErrorConsole::ErrorConsole(Profile* profile) : enabled_(false),
- profile_(profile) {
+ErrorConsole::ErrorConsole(Profile* profile,
+ ExtensionService* extension_service)
+ : enabled_(false), profile_(profile) {
// TODO(rdevlin.cronin): Remove once crbug.com/159265 is fixed.
#if !defined(ENABLE_EXTENSIONS)
return;
@@ -69,7 +89,7 @@ ErrorConsole::ErrorConsole(Profile* profile) : enabled_(false),
base::Unretained(this)));
if (profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode))
- Enable();
+ Enable(extension_service);
}
ErrorConsole::~ErrorConsole() {
@@ -141,12 +161,12 @@ void ErrorConsole::OnPrefChanged() {
profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
if (developer_mode && !enabled_)
- Enable();
+ Enable(ExtensionSystem::Get(profile_)->extension_service());
else if (!developer_mode && enabled_)
Disable();
}
-void ErrorConsole::Enable() {
+void ErrorConsole::Enable(ExtensionService* extension_service) {
enabled_ = true;
notification_registrar_.Add(
@@ -157,6 +177,19 @@ void ErrorConsole::Enable() {
this,
chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
content::Source<Profile>(profile_));
+ notification_registrar_.Add(
+ this,
+ chrome::NOTIFICATION_EXTENSION_INSTALLED,
+ content::Source<Profile>(profile_));
+
+ if (extension_service) {
+ // Get manifest errors for extensions already installed.
+ const ExtensionSet* extensions = extension_service->extensions();
+ for (ExtensionSet::const_iterator iter = extensions->begin();
+ iter != extensions->end(); ++iter) {
+ AddManifestErrorsForExtension(iter->get());
+ }
+ }
}
void ErrorConsole::Disable() {
@@ -165,6 +198,19 @@ void ErrorConsole::Disable() {
enabled_ = false;
}
+void ErrorConsole::AddManifestErrorsForExtension(const Extension* extension) {
+ const std::vector<InstallWarning>& warnings =
+ extension->install_warnings();
+ for (std::vector<InstallWarning>::const_iterator iter = warnings.begin();
+ iter != warnings.end(); ++iter) {
+ ReportError(scoped_ptr<ExtensionError>(new ManifestError(
+ extension->id(),
+ base::UTF8ToUTF16(iter->message),
+ base::UTF8ToUTF16(iter->key),
+ base::UTF8ToUTF16(iter->specific))));
+ }
+}
+
void ErrorConsole::RemoveIncognitoErrors() {
for (ErrorMap::iterator iter = errors_.begin();
iter != errors_.end(); ++iter) {
@@ -204,6 +250,23 @@ void ErrorConsole::Observe(int type,
RemoveErrorsForExtension(
content::Details<Extension>(details).ptr()->id());
break;
+ case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
+ const InstalledExtensionInfo* info =
+ content::Details<InstalledExtensionInfo>(details).ptr();
+
+ // We don't want to have manifest errors from previous installs. We want
+ // to keep runtime errors, though, because extensions are reloaded on a
+ // refresh of chrome:extensions, and we don't want to wipe our history
+ // whenever that happens.
+ ErrorMap::iterator iter = errors_.find(info->extension->id());
+ if (iter != errors_.end()) {
+ DeleteErrorsOfTypeFromList(&(iter->second),
+ ExtensionError::MANIFEST_ERROR);
+ }
+
+ AddManifestErrorsForExtension(info->extension);
+ break;
+ }
default:
NOTREACHED();
}
diff --git a/chrome/browser/extensions/error_console/error_console.h b/chrome/browser/extensions/error_console/error_console.h
index 6c8ebb5..45e9c00 100644
--- a/chrome/browser/extensions/error_console/error_console.h
+++ b/chrome/browser/extensions/error_console/error_console.h
@@ -24,10 +24,12 @@ class NotificationSource;
class RenderViewHost;
}
+class ExtensionService;
class Profile;
namespace extensions {
class ErrorConsoleUnitTest;
+class Extension;
// The ErrorConsole is a central object to which all extension errors are
// reported. This includes errors detected in extensions core, as well as
@@ -49,7 +51,7 @@ class ErrorConsole : public content::NotificationObserver {
virtual void OnErrorConsoleDestroyed();
};
- explicit ErrorConsole(Profile* profile);
+ explicit ErrorConsole(Profile* profile, ExtensionService* extension_service);
virtual ~ErrorConsole();
// Convenience method to return the ErrorConsole for a given profile.
@@ -72,8 +74,9 @@ class ErrorConsole : public content::NotificationObserver {
private:
FRIEND_TEST_ALL_PREFIXES(ErrorConsoleUnitTest, AddAndRemoveErrors);
- // Enable the error console for error collection and retention.
- void Enable();
+ // Enable the error console for error collection and retention. This involves
+ // subscribing to the appropriate notifications and fetching manifest errors.
+ void Enable(ExtensionService* extension_service);
// Disable the error console, removing the subscriptions to notifications and
// removing all current errors.
void Disable();
@@ -83,6 +86,9 @@ class ErrorConsole : public content::NotificationObserver {
// not.
void OnPrefChanged();
+ // Add manifest errors from an extension's install warnings.
+ void AddManifestErrorsForExtension(const Extension* extension);
+
// Remove all errors which happened while incognito; we have to do this once
// the incognito profile is destroyed.
void RemoveIncognitoErrors();
diff --git a/chrome/browser/extensions/error_console/error_console_browsertest.cc b/chrome/browser/extensions/error_console/error_console_browsertest.cc
index 1cdbf62..455eb5d 100644
--- a/chrome/browser/extensions/error_console/error_console_browsertest.cc
+++ b/chrome/browser/extensions/error_console/error_console_browsertest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/error_console/error_console.h"
+#include "base/files/file_path.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
@@ -18,6 +19,8 @@
#include "chrome/test/base/ui_test_utils.h"
#include "extensions/browser/extension_error.h"
#include "extensions/common/constants.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/manifest_constants.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -98,6 +101,26 @@ void CheckRuntimeError(const ExtensionError* error,
EXPECT_EQ(expected_stack_size, runtime_error->stack_trace().size());
}
+void CheckManifestError(const ExtensionError* error,
+ const std::string& id,
+ const std::string& message,
+ const std::string& manifest_key,
+ const std::string& manifest_specific) {
+ CheckError(error,
+ ExtensionError::MANIFEST_ERROR,
+ id,
+ // source is always the manifest for ManifestErrors.
+ base::FilePath(kManifestFilename).AsUTF8Unsafe(),
+ false, // manifest errors are never from incognito.
+ message);
+
+ const ManifestError* manifest_error =
+ static_cast<const ManifestError*>(error);
+ EXPECT_EQ(UTF8ToUTF16(manifest_key), manifest_error->manifest_key());
+ EXPECT_EQ(UTF8ToUTF16(manifest_specific),
+ manifest_error->manifest_specific());
+}
+
} // namespace
class ErrorConsoleBrowserTest : public ExtensionBrowserTest {
@@ -250,6 +273,83 @@ class ErrorConsoleBrowserTest : public ExtensionBrowserTest {
ErrorConsole* error_console_;
};
+// Test to ensure that we are successfully reporting manifest errors as an
+// extension is installed.
+IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest, ReportManifestErrors) {
+ const Extension* extension = NULL;
+ // We expect two errors - one for an invalid permission, and a second for
+ // an unknown key.
+ LoadExtensionAndCheckErrors("manifest_warnings",
+ ExtensionBrowserTest::kFlagIgnoreManifestWarnings,
+ 2,
+ ACTION_NONE,
+ &extension);
+
+ const ErrorConsole::ErrorList& errors =
+ error_console()->GetErrorsForExtension(extension->id());
+
+ // Unfortunately, there's not always a hard guarantee of order in parsing the
+ // manifest, so there's not a definitive order in which these errors may
+ // occur. As such, we need to determine which error corresponds to which
+ // expected error.
+ const ExtensionError* permissions_error = NULL;
+ const ExtensionError* unknown_key_error = NULL;
+ const char kFakeKey[] = "not_a_real_key";
+ for (size_t i = 0; i < errors.size(); ++i) {
+ ASSERT_EQ(ExtensionError::MANIFEST_ERROR, errors[i]->type());
+ std::string utf8_key = UTF16ToUTF8(
+ (static_cast<const ManifestError*>(errors[i]))->manifest_key());
+ if (utf8_key == manifest_keys::kPermissions)
+ permissions_error = errors[i];
+ else if (utf8_key == kFakeKey)
+ unknown_key_error = errors[i];
+ }
+ ASSERT_TRUE(permissions_error);
+ ASSERT_TRUE(unknown_key_error);
+
+ const char kFakePermission[] = "not_a_real_permission";
+ CheckManifestError(permissions_error,
+ extension->id(),
+ ErrorUtils::FormatErrorMessage(
+ manifest_errors::kPermissionUnknownOrMalformed,
+ kFakePermission),
+ manifest_keys::kPermissions,
+ kFakePermission);
+
+ CheckManifestError(unknown_key_error,
+ extension->id(),
+ ErrorUtils::FormatErrorMessage(
+ manifest_errors::kUnrecognizedManifestKey,
+ kFakeKey),
+ kFakeKey,
+ EmptyString());
+}
+
+// Test that we do not store any errors unless the Developer Mode switch is
+// toggled on the profile.
+IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest,
+ DontStoreErrorsWithoutDeveloperMode) {
+ profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
+
+ const Extension* extension = NULL;
+ // Same test as ReportManifestErrors, except we don't expect any errors since
+ // we disable Developer Mode.
+ LoadExtensionAndCheckErrors("manifest_warnings",
+ ExtensionBrowserTest::kFlagIgnoreManifestWarnings,
+ 0,
+ ACTION_NONE,
+ &extension);
+
+ // Now if we enable developer mode, the errors should be reported...
+ profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
+ EXPECT_EQ(2u, error_console()->GetErrorsForExtension(extension->id()).size());
+
+ // ... and if we disable it again, all errors which we were holding should be
+ // removed.
+ profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, false);
+ EXPECT_EQ(0u, error_console()->GetErrorsForExtension(extension->id()).size());
+}
+
// Load an extension which, upon visiting any page, first sends out a console
// log, and then crashes with a JS TypeError.
IN_PROC_BROWSER_TEST_F(ErrorConsoleBrowserTest,
diff --git a/chrome/browser/extensions/extension_system.cc b/chrome/browser/extensions/extension_system.cc
index 0b75800..801a4f7 100644
--- a/chrome/browser/extensions/extension_system.cc
+++ b/chrome/browser/extensions/extension_system.cc
@@ -221,7 +221,7 @@ void ExtensionSystemImpl::Shared::Init(bool extensions_enabled) {
new ExtensionWarningBadgeService(profile_));
extension_warning_service_->AddObserver(
extension_warning_badge_service_.get());
- error_console_.reset(new ErrorConsole(profile_));
+ error_console_.reset(new ErrorConsole(profile_, extension_service_.get()));
}
void ExtensionSystemImpl::Shared::Shutdown() {
diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc
index 48078c2..3023808 100644
--- a/chrome/browser/extensions/test_extension_system.cc
+++ b/chrome/browser/extensions/test_extension_system.cc
@@ -34,7 +34,7 @@ TestExtensionSystem::TestExtensionSystem(Profile* profile)
: profile_(profile),
value_store_(NULL),
info_map_(new ExtensionInfoMap()),
- error_console_(new ErrorConsole(profile)) {
+ error_console_(new ErrorConsole(profile, NULL)) {
}
TestExtensionSystem::~TestExtensionSystem() {
diff --git a/chrome/common/extensions/extension_manifest_constants.h b/chrome/common/extensions/extension_manifest_constants.h
index bf38691..411c52d 100644
--- a/chrome/common/extensions/extension_manifest_constants.h
+++ b/chrome/common/extensions/extension_manifest_constants.h
@@ -253,10 +253,10 @@ namespace extension_manifest_errors {
extern const char kMultipleOverrides[];
extern const char kNoPermissionForMIMETypes[];
extern const char kNoWildCardsInPaths[];
+ extern const char kOneUISurfaceOnly[];
extern const char kPermissionMustBeOptional[];
extern const char kPermissionNotAllowed[];
extern const char kPermissionNotAllowedInManifest[];
- extern const char kOneUISurfaceOnly[];
extern const char kReservedMessageFound[];
extern const char kScriptBadgeRequiresFlag[];
extern const char kScriptBadgeIconIgnored[];
diff --git a/chrome/common/extensions/permissions/permissions_data.cc b/chrome/common/extensions/permissions/permissions_data.cc
index 139e4dc..139bb2e 100644
--- a/chrome/common/extensions/permissions/permissions_data.cc
+++ b/chrome/common/extensions/permissions/permissions_data.cc
@@ -23,6 +23,7 @@
#include "extensions/common/error_utils.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_constants.h"
#include "extensions/common/switches.h"
#include "extensions/common/url_pattern_set.h"
#include "extensions/common/user_script.h"
@@ -231,9 +232,9 @@ bool ParseHelper(Extension* extension,
// It's probably an unknown API permission. Do not throw an error so
// extensions can retain backwards compatability (http://crbug.com/42742).
extension->AddInstallWarning(InstallWarning(
- base::StringPrintf(
- "Permission '%s' is unknown or URL pattern is malformed.",
- permission_str.c_str()),
+ ErrorUtils::FormatErrorMessage(
+ manifest_errors::kPermissionUnknownOrMalformed,
+ permission_str),
key,
permission_str));
}
diff --git a/chrome/test/data/extensions/error_console/manifest_warnings/manifest.json b/chrome/test/data/extensions/error_console/manifest_warnings/manifest.json
new file mode 100644
index 0000000..4dcb3ce
--- /dev/null
+++ b/chrome/test/data/extensions/error_console/manifest_warnings/manifest.json
@@ -0,0 +1,9 @@
+{
+ "name": "Error Console Manifest Warnings Test",
+ "manifest_version": 2,
+ "version": "1.1",
+ "not_a_real_key": "not_a_real_value",
+ "permissions": [
+ "tabs", "not_a_real_permission"
+ ]
+}
diff --git a/extensions/browser/extension_error.cc b/extensions/browser/extension_error.cc
index 9b1e95f..b372885 100644
--- a/extensions/browser/extension_error.cc
+++ b/extensions/browser/extension_error.cc
@@ -73,13 +73,17 @@ bool ExtensionError::IsEqual(const ExtensionError* rhs) const {
}
ManifestError::ManifestError(const std::string& extension_id,
- const string16& message)
+ const string16& message,
+ const string16& manifest_key,
+ const string16& manifest_specific)
: ExtensionError(ExtensionError::MANIFEST_ERROR,
extension_id,
false, // extensions can't be installed while incognito.
logging::LOG_WARNING, // All manifest errors are warnings.
base::FilePath(kManifestFilename).AsUTF16Unsafe(),
- message) {
+ message),
+ manifest_key_(manifest_key),
+ manifest_specific_(manifest_specific) {
}
ManifestError::~ManifestError() {
diff --git a/extensions/browser/extension_error.h b/extensions/browser/extension_error.h
index 6ee7791..ceb1504 100644
--- a/extensions/browser/extension_error.h
+++ b/extensions/browser/extension_error.h
@@ -73,13 +73,25 @@ class ExtensionError {
class ManifestError : public ExtensionError {
public:
ManifestError(const std::string& extension_id,
- const base::string16& message);
+ const base::string16& message,
+ const base::string16& manifest_key,
+ const base::string16& manifest_specific);
virtual ~ManifestError();
virtual std::string PrintForTest() const OVERRIDE;
+
+ const base::string16& manifest_key() const { return manifest_key_; }
+ const base::string16& manifest_specific() const { return manifest_specific_; }
private:
virtual bool IsEqualImpl(const ExtensionError* rhs) const OVERRIDE;
+ // If present, this indicates the feature in the manifest which caused the
+ // error.
+ base::string16 manifest_key_;
+ // If present, this is a more-specific location of the error - for instance,
+ // a specific permission which is incorrect, rather than simply "permissions".
+ base::string16 manifest_specific_;
+
DISALLOW_COPY_AND_ASSIGN(ManifestError);
};
diff --git a/extensions/common/manifest.cc b/extensions/common/manifest.cc
index eaaaf76..85b5d98 100644
--- a/extensions/common/manifest.cc
+++ b/extensions/common/manifest.cc
@@ -160,8 +160,8 @@ bool Manifest::ValidateManifest(
it.Advance()) {
if (!provider->GetFeature(it.key())) {
warnings->push_back(InstallWarning(
- base::StringPrintf("Unrecognized manifest key '%s'.",
- it.key().c_str()),
+ ErrorUtils::FormatErrorMessage(
+ manifest_errors::kUnrecognizedManifestKey, it.key()),
it.key()));
}
}
diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc
index 3ba2a63..0402c40 100644
--- a/extensions/common/manifest_constants.cc
+++ b/extensions/common/manifest_constants.cc
@@ -149,4 +149,12 @@ const char kWebURLs[] = "app.urls";
} // namespace manifest_keys
+namespace manifest_errors {
+
+const char kPermissionUnknownOrMalformed[] =
+ "Permission '*' is unknown or URL pattern is malformed.";
+const char kUnrecognizedManifestKey[] = "Unrecognized manifest key '*'.";
+
+} // namespace manifest_errors
+
} // namespace extensions
diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h
index ff4ca85..e08348e 100644
--- a/extensions/common/manifest_constants.h
+++ b/extensions/common/manifest_constants.h
@@ -153,6 +153,11 @@ namespace manifest_keys {
extern const char kWebURLs[];
} // namespace manifest_keys
+namespace manifest_errors {
+extern const char kPermissionUnknownOrMalformed[];
+extern const char kUnrecognizedManifestKey[];
+} // namespace manifest_errors
+
} // namespace extensions
#endif // EXTENSIONS_COMMON_MANIFEST_CONSTANTS_H_