summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-05 21:56:22 +0000
committerrdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-05 21:56:22 +0000
commitfa5fed3316ecc4f13efa1319e646cb6b974e858d (patch)
tree75c38c014130a0e917df950b3f556c68eb924127 /extensions
parent9db89a7513b69546b5d28790c1f065a22a8f07a1 (diff)
downloadchromium_src-fa5fed3316ecc4f13efa1319e646cb6b974e858d.zip
chromium_src-fa5fed3316ecc4f13efa1319e646cb6b974e858d.tar.gz
chromium_src-fa5fed3316ecc4f13efa1319e646cb6b974e858d.tar.bz2
Add Error Console UI for install warnings to the chrome:extensions page (hidden behind the error console switch).
Snazzy Images: http://imgur.com/a/7QnMo#0 (updated 9/3) BUG=21734 Review URL: https://chromiumcodereview.appspot.com/22938005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221527 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r--extensions/browser/extension_error.cc60
-rw-r--r--extensions/browser/extension_error.h23
-rw-r--r--extensions/browser/manifest_highlighter.cc166
-rw-r--r--extensions/browser/manifest_highlighter.h74
-rw-r--r--extensions/browser/manifest_highlighter_unittest.cc98
5 files changed, 401 insertions, 20 deletions
diff --git a/extensions/browser/extension_error.cc b/extensions/browser/extension_error.cc
index b372885..272028e 100644
--- a/extensions/browser/extension_error.cc
+++ b/extensions/browser/extension_error.cc
@@ -11,30 +11,20 @@
#include "url/gurl.h"
using base::string16;
+using base::DictionaryValue;
namespace extensions {
-namespace {
-
-const char kLineNumberKey[] = "lineNumber";
-const char kColumnNumberKey[] = "columnNumber";
-const char kURLKey[] = "url";
-const char kFunctionNameKey[] = "functionName";
-const char kExecutionContextURLKey[] = "executionContextURL";
-const char kStackTraceKey[] = "stackTrace";
-
-// Try to retrieve an extension ID from a |url|. On success, returns true and
-// populates |extension_id| with the ID. On failure, returns false and leaves
-// extension_id untouched.
-bool GetExtensionIDFromGURL(const GURL& url, std::string* extension_id) {
- if (url.SchemeIs(kExtensionScheme)) {
- *extension_id = url.host();
- return true;
- }
- return false;
-}
+////////////////////////////////////////////////////////////////////////////////
+// ExtensionError
-} // namespace
+// Static JSON keys.
+const char ExtensionError::kExtensionIdKey[] = "extensionId";
+const char ExtensionError::kFromIncognitoKey[] = "fromIncognito";
+const char ExtensionError::kLevelKey[] = "level";
+const char ExtensionError::kMessageKey[] = "message";
+const char ExtensionError::kSourceKey[] = "source";
+const char ExtensionError::kTypeKey[] = "type";
ExtensionError::ExtensionError(Type type,
const std::string& extension_id,
@@ -54,6 +44,20 @@ ExtensionError::ExtensionError(Type type,
ExtensionError::~ExtensionError() {
}
+scoped_ptr<DictionaryValue> ExtensionError::ToValue() const {
+ // TODO(rdevlin.cronin): Use ValueBuilder when it's moved from
+ // chrome/common/extensions.
+ scoped_ptr<DictionaryValue> value(new DictionaryValue);
+ value->SetInteger(kTypeKey, static_cast<int>(type_));
+ value->SetString(kExtensionIdKey, extension_id_);
+ value->SetBoolean(kFromIncognitoKey, from_incognito_);
+ value->SetInteger(kLevelKey, static_cast<int>(level_));
+ value->SetString(kSourceKey, source_);
+ value->SetString(kMessageKey, message_);
+
+ return value.Pass();
+}
+
std::string ExtensionError::PrintForTest() const {
return std::string("Extension Error:") +
"\n OTR: " + std::string(from_incognito_ ? "true" : "false") +
@@ -72,6 +76,13 @@ bool ExtensionError::IsEqual(const ExtensionError* rhs) const {
IsEqualImpl(rhs);
}
+////////////////////////////////////////////////////////////////////////////////
+// ManifestError
+
+// Static JSON keys.
+const char ManifestError::kManifestKeyKey[] = "manifestKey";
+const char ManifestError::kManifestSpecificKey[] = "manifestSpecific";
+
ManifestError::ManifestError(const std::string& extension_id,
const string16& message,
const string16& manifest_key,
@@ -89,6 +100,15 @@ ManifestError::ManifestError(const std::string& extension_id,
ManifestError::~ManifestError() {
}
+scoped_ptr<DictionaryValue> ManifestError::ToValue() const {
+ scoped_ptr<DictionaryValue> value = ExtensionError::ToValue();
+ if (!manifest_key_.empty())
+ value->SetString(kManifestKeyKey, manifest_key_);
+ if (!manifest_specific_.empty())
+ value->SetString(kManifestSpecificKey, manifest_specific_);
+ return value.Pass();
+}
+
std::string ManifestError::PrintForTest() const {
return ExtensionError::PrintForTest() +
"\n Type: ManifestError";
diff --git a/extensions/browser/extension_error.h b/extensions/browser/extension_error.h
index ceb1504..7e02bb3 100644
--- a/extensions/browser/extension_error.h
+++ b/extensions/browser/extension_error.h
@@ -10,10 +10,15 @@
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
#include "extensions/common/stack_frame.h"
#include "url/gurl.h"
+namespace base {
+class DictionaryValue;
+}
+
namespace extensions {
class ExtensionError {
@@ -25,6 +30,9 @@ class ExtensionError {
virtual ~ExtensionError();
+ // Serializes the ExtensionError into JSON format.
+ virtual scoped_ptr<base::DictionaryValue> ToValue() const;
+
virtual std::string PrintForTest() const;
// Return true if this error and |rhs| are considered equal, and should be
@@ -40,6 +48,14 @@ class ExtensionError {
size_t occurrences() const { return occurrences_; }
void set_occurrences(size_t occurrences) { occurrences_ = occurrences; }
+ // Keys used for retrieving JSON values.
+ static const char kExtensionIdKey[];
+ static const char kFromIncognitoKey[];
+ static const char kLevelKey[];
+ static const char kMessageKey[];
+ static const char kSourceKey[];
+ static const char kTypeKey[];
+
protected:
ExtensionError(Type type,
const std::string& extension_id,
@@ -78,10 +94,17 @@ class ManifestError : public ExtensionError {
const base::string16& manifest_specific);
virtual ~ManifestError();
+ virtual scoped_ptr<base::DictionaryValue> ToValue() const OVERRIDE;
+
virtual std::string PrintForTest() const OVERRIDE;
const base::string16& manifest_key() const { return manifest_key_; }
const base::string16& manifest_specific() const { return manifest_specific_; }
+
+ // Keys used for retrieving JSON values.
+ static const char kManifestKeyKey[];
+ static const char kManifestSpecificKey[];
+
private:
virtual bool IsEqualImpl(const ExtensionError* rhs) const OVERRIDE;
diff --git a/extensions/browser/manifest_highlighter.cc b/extensions/browser/manifest_highlighter.cc
new file mode 100644
index 0000000..5afdc46
--- /dev/null
+++ b/extensions/browser/manifest_highlighter.cc
@@ -0,0 +1,166 @@
+// Copyright 2013 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 <stack>
+
+#include "extensions/browser/manifest_highlighter.h"
+
+namespace extensions {
+
+namespace {
+
+// Increment |index| to the position of the next quote ('"') in |str|, skipping
+// over any escaped quotes. If no next quote is found, |index| is set to
+// std::string::npos. Assumes |index| currently points to a quote.
+void QuoteIncrement(const std::string& str, size_t* index) {
+ size_t i = *index + 1; // Skip over the first quote.
+ bool found = false;
+ while (!found && i < str.size()) {
+ if (str[i] == '\\')
+ i += 2; // if we find an escaped character, skip it.
+ else if (str[i] == '"')
+ found = true;
+ else
+ ++i;
+ }
+ *index = found ? i : std::string::npos;
+}
+
+// Increment |index| by one if the next character is not a comment. Increment
+// index until the end of the comment if it is a comment.
+void CommentSafeIncrement(const std::string& str, size_t* index) {
+ size_t i = *index;
+ if (str[i] == '/' && i + 1 < str.size()) {
+ // Eat a single-line comment.
+ if (str[i + 1] == '/') {
+ i += 2; // Eat the '//'.
+ while (i < str.size() && str[i] != '\n' && str[i] != '\r')
+ ++i;
+ } else if (str[i + 1] == '*') { // Eat a multi-line comment.
+ i += 3; // Advance to the first possible comment end.
+ while (i < str.size() && !(str[i - 1] == '*' && str[i] == '/'))
+ ++i;
+ }
+ }
+ *index = i + 1;
+}
+
+// Increment index until the end of the current "chunk"; a "chunk" is a JSON-
+// style list, object, or string literal, without exceeding |end|. Assumes
+// |index| currently points to a chunk's starting character ('{', '[', or '"').
+void ChunkIncrement(const std::string& str, size_t* index, size_t end) {
+ char c = str[*index];
+ std::stack<char> stack;
+ do {
+ if (c == '"')
+ QuoteIncrement(str, index);
+ else if (c == '[')
+ stack.push(']');
+ else if (c == '{')
+ stack.push('}');
+ else if (!stack.empty() && c == stack.top())
+ stack.pop();
+ CommentSafeIncrement(str, index);
+ c = str[*index];
+ } while (!stack.empty() && *index < end);
+}
+
+} // namespace
+
+ManifestHighlighter::ManifestHighlighter(const std::string& manifest,
+ const std::string& key,
+ const std::string& specific)
+ : manifest_(manifest),
+ start_(manifest_.find('{') + 1),
+ end_(manifest_.rfind('}')) {
+ Parse(key, specific);
+}
+
+ManifestHighlighter::~ManifestHighlighter() {
+}
+
+std::string ManifestHighlighter::GetBeforeFeature() const {
+ return manifest_.substr(0, start_);
+}
+
+std::string ManifestHighlighter::GetFeature() const {
+ return manifest_.substr(start_, end_ - start_);
+}
+
+std::string ManifestHighlighter::GetAfterFeature() const {
+ return manifest_.substr(end_);
+}
+
+void ManifestHighlighter::Parse(const std::string& key,
+ const std::string& specific) {
+ // First, try to find the bounds of the full key.
+ if (FindBounds(key, true) /* enforce at top level */ ) {
+ // If we succeed, and we have a specific location, find the bounds of the
+ // specific.
+ if (!specific.empty())
+ FindBounds(specific, false /* don't enforce at top level */ );
+
+ // We may have found trailing whitespace. Don't use base::TrimWhitespace,
+ // because we want to keep any whitespace we find - just not highlight it.
+ size_t trim = manifest_.find_last_not_of(" \t\n\r", end_ - 1);
+ if (trim < end_ && trim > start_)
+ end_ = trim + 1;
+ } else {
+ // If we fail, then we set start to end so that the highlighted portion is
+ // empty.
+ start_ = end_;
+ }
+}
+
+bool ManifestHighlighter::FindBounds(const std::string& feature,
+ bool enforce_at_top_level) {
+ char c = '\0';
+ while (start_ < end_) {
+ c = manifest_[start_];
+ if (c == '"') {
+ // The feature may be quoted.
+ size_t quote_end = start_;
+ QuoteIncrement(manifest_, &quote_end);
+ if (manifest_.substr(start_ + 1, quote_end - 1 - start_) == feature) {
+ FindBoundsEnd(feature, quote_end + 1);
+ return true;
+ } else {
+ // If it's not the feature, then we can skip the quoted section.
+ start_ = quote_end + 1;
+ }
+ } else if (manifest_.substr(start_, feature.size()) == feature) {
+ FindBoundsEnd(feature, start_ + feature.size() + 1);
+ return true;
+ } else if (enforce_at_top_level && (c == '{' || c == '[')) {
+ // If we don't have to be at the top level, then we can skip any chunks
+ // we find.
+ ChunkIncrement(manifest_, &start_, end_);
+ } else {
+ CommentSafeIncrement(manifest_, &start_);
+ }
+ }
+ return false;
+}
+
+void ManifestHighlighter::FindBoundsEnd(const std::string& feature,
+ size_t local_start) {
+ char c = '\0';
+ while (local_start < end_) {
+ c = manifest_[local_start];
+ // We're done when we find a terminating character (i.e., either a comma or
+ // an ending bracket.
+ if (c == ',' || c == '}' || c == ']') {
+ end_ = local_start;
+ return;
+ }
+ // We can skip any chunks we find, since we are looking for the end of the
+ // current feature, and don't want to go any deeper.
+ if (c == '"' || c == '{' || c == '[')
+ ChunkIncrement(manifest_, &local_start, end_);
+ else
+ CommentSafeIncrement(manifest_, &local_start);
+ }
+}
+
+} // namespace extensions
diff --git a/extensions/browser/manifest_highlighter.h b/extensions/browser/manifest_highlighter.h
new file mode 100644
index 0000000..bb20fef
--- /dev/null
+++ b/extensions/browser/manifest_highlighter.h
@@ -0,0 +1,74 @@
+// Copyright 2013 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_MANIFEST_HIGHLIGHTER_H_
+#define EXTENSIONS_BROWSER_MANIFEST_HIGHLIGHTER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace extensions {
+
+// Use the ManifestHighlighter class to find the bounds of a feature in the
+// manifest. The manifest is parsed for the feature upon construction of the
+// object.
+// A feature can be at any level in the hierarchy. The "start" of a feature is
+// the first character of the feature name, or the beginning quote of the name,
+// if present. The "end" of a feature is wherever the next item at the same
+// level starts.
+// For instance, the bounds for the 'permissions' feature at the top level could
+// be '"permissions": { "tabs", "history", "downloads" }', but the feature for
+// 'tabs' within 'permissions' would just be '"tabs"'.
+// We can't use the JSONParser to do this, because we want to display the actual
+// manifest, and once we parse it into Values, we lose any formatting the user
+// may have had.
+// If a feature cannot be found, the feature will have zero-length.
+class ManifestHighlighter {
+ public:
+ ManifestHighlighter(const std::string& manifest,
+ const std::string& key,
+ const std::string& specific /* optional */);
+ ~ManifestHighlighter();
+
+ // Get the portion of the manifest which should not be highlighted and is
+ // before the feature.
+ std::string GetBeforeFeature() const;
+
+ // Get the feature portion of the manifest, which should be highlighted.
+ std::string GetFeature() const;
+
+ // Get the portion of the manifest which should not be highlighted and is
+ // after the feature.
+ std::string GetAfterFeature() const;
+
+ private:
+ // Called from the constructor; determine the start and end bounds of a
+ // feature, using both the key and specific information.
+ void Parse(const std::string& key, const std::string& specific);
+
+ // Find the bounds of any feature, either a full key or a specific item within
+ // the key. |enforce_at_top_level| means that the feature we find must be at
+ // the same level as |start_| (i.e., ignore nested elements).
+ // Returns true on success.
+ bool FindBounds(const std::string& feature, bool enforce_at_top_level);
+
+ // Finds the end of the feature.
+ void FindBoundsEnd(const std::string& feature, size_t local_start);
+
+ // The manifest we are parsing.
+ std::string manifest_;
+
+ // The start of the feature.
+ size_t start_;
+
+ // The end of the feature.
+ size_t end_;
+
+ DISALLOW_COPY_AND_ASSIGN(ManifestHighlighter);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_MANIFEST_HIGHLIGHTER_H_
diff --git a/extensions/browser/manifest_highlighter_unittest.cc b/extensions/browser/manifest_highlighter_unittest.cc
new file mode 100644
index 0000000..ab6e780
--- /dev/null
+++ b/extensions/browser/manifest_highlighter_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2013 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/manifest_highlighter.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+const char kManifest[] =
+"{\n"
+" \"name\": \"Content Scripts\",\n"
+" \"version\": \"2.0\",\n"
+" // this is a comment with the word permissions.\n"
+" /* This is a multine\n"
+" comment with the word permissions\n"
+" that shouldn't be highlighted */\n"
+" \"permissions\": [\n"
+" /* This is a tricky comment because it has brackets }]*/\n"
+" \"tabs\"\n"
+" ],\n"
+" \"content_scripts\": [\n"
+" {\n"
+" \"matches\": [\"*://aaronboodman.com/*\", \"*://rdcronin.com/*\"],\n"
+" \"js\": [\"myscript.js\"]\n"
+" }\n"
+" ],\n"
+" \"test_key\": {\n"
+" \"escaped_quoted\\\"\",\n"
+" \"/*foo*/\"\n"
+" },\n"
+" \"manifest_version\": 2,\n"
+" \"international_key\": \"還是不要\"\n"
+"}";
+
+} // namespace
+
+TEST(ManifestHighlighterUnitTest, ManifestHighlighterUnitTest) {
+ // Get a full key.
+ const char kPermissionsFeature[] =
+ "\"permissions\": [\n"
+ " /* This is a tricky comment because it has brackets }]*/\n"
+ " \"tabs\"\n"
+ " ]";
+ ManifestHighlighter permissions(kManifest, "permissions", EmptyString());
+ EXPECT_EQ(kPermissionsFeature, permissions.GetFeature());
+
+ // Get a specific portion of a key.
+ const char kTabsFeature[] = "\"tabs\"";
+ ManifestHighlighter tabs(kManifest, "permissions", "tabs");
+ EXPECT_EQ(kTabsFeature, tabs.GetFeature());
+
+ // Get a single-character, non-quoted entity of a key.
+ const char kManifestVersionFeature[] = "2";
+ ManifestHighlighter version(kManifest, "manifest_version", "2");
+ EXPECT_EQ(kManifestVersionFeature, version.GetFeature());
+
+ // Get a compound portion of a key, including quoted '//' (which shouldn't be
+ // mistaken for comments).
+ const char kMatchesFeature[] =
+ "\"matches\": [\"*://aaronboodman.com/*\", \"*://rdcronin.com/*\"]";
+ ManifestHighlighter matches(kManifest, "content_scripts", "matches");
+ EXPECT_EQ(kMatchesFeature, matches.GetFeature());
+
+ // If a feature isn't present, we should get an empty string.
+ ManifestHighlighter not_present(kManifest, "a_fake_feature", EmptyString());
+ EXPECT_EQ(EmptyString(), not_present.GetFeature());
+
+ // If we request a specific portion of a key which is not found, we should
+ // get an empty string.
+ ManifestHighlighter specific_portion_not_present(
+ kManifest, "permissions", "a_fake_feature");
+ EXPECT_EQ(EmptyString(), specific_portion_not_present.GetFeature());
+
+ const char kEscapedQuotedFeature[] = "\"escaped_quoted\\\"\"";
+ ManifestHighlighter escaped_quoted(
+ kManifest, "test_key", "escaped_quoted\\\"");
+ EXPECT_EQ(kEscapedQuotedFeature, escaped_quoted.GetFeature());
+
+ const char kFeatureWithComment[] = "\"/*foo*/\"";
+ ManifestHighlighter feature_with_comment(kManifest, "test_key", "/*foo*/");
+ EXPECT_EQ(kFeatureWithComment, feature_with_comment.GetFeature());
+
+ // Check with non-ascii characters.
+ const char kInternationalFeature[] = "\"international_key\": \"還是不要\"";
+ ManifestHighlighter international_feature(
+ kManifest, "international_key", EmptyString());
+ EXPECT_EQ(kInternationalFeature, international_feature.GetFeature());
+}
+
+} // namespace extensions