Index: extensions/browser/manifest_highlighter.cc |
diff --git a/extensions/browser/manifest_highlighter.cc b/extensions/browser/manifest_highlighter.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..06a7936008e7a94e7310ebf55be79b01be338c63 |
--- /dev/null |
+++ b/extensions/browser/manifest_highlighter.cc |
@@ -0,0 +1,160 @@ |
+// 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 { |
+ |
+size_t FindNextQuote(const std::string& string, size_t index) { |
+ bool found = false; |
+ while (!found && index < string.size()) { |
+ if (string[index] == '\\') |
+ index += 2; // if we find an escaped character, skip it. |
+ else if (string[index] == '"') |
+ found = true; |
+ else |
+ ++index; |
+ } |
+ return index < string.size() ? index : std::string::npos; |
Yoyo Zhou
2013/08/19 21:48:51
Should this test use found?
Devlin
2013/08/20 20:38:22
Done.
|
+} |
+ |
+} // 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 = FindNextQuote(manifest_, start_ + 1); |
+ 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(&start_); |
+ } else { |
+ CommentSafeIncrement(&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(&local_start); |
+ else |
+ CommentSafeIncrement(&local_start); |
+ } |
+} |
+ |
+void ManifestHighlighter::ChunkIncrement(size_t* index) { |
+ char c = manifest_[*index]; |
+ std::stack<char> stack; |
+ do { |
+ if (c == '"') |
+ *index = FindNextQuote(manifest_, *index + 1); |
+ else if (c == '[') |
+ stack.push(']'); |
+ else if (c == '{') |
+ stack.push('}'); |
+ else if (!stack.empty() && c == stack.top()) |
+ stack.pop(); |
+ CommentSafeIncrement(index); |
+ c = manifest_[*index]; |
+ } while (!stack.empty() && *index < end_); |
+} |
+ |
+void ManifestHighlighter::CommentSafeIncrement(size_t* index) { |
Yoyo Zhou
2013/08/19 21:48:51
nit: seems like this and FindNextQuote are in the
Devlin
2013/08/20 20:38:22
Done.
|
+ size_t i = *index; |
+ if (manifest_[i] == '/' && i + 1 < manifest_.size()) { |
+ // Eat a single-line comment. |
+ if (manifest_[i + 1] == '/') { |
+ i += 2; // Eat the '//'. |
+ while (i < manifest_.size() && |
+ manifest_[i] != '\n' && manifest_[i] != '\r') { |
+ ++i; |
+ } |
+ } else if (manifest_[i + 1] == '*') { // Eat a multi-line comment. |
+ i += 3; // Advance to the first possible comment end. |
+ while (i < manifest_.size() && |
+ !(manifest_[i - 1] == '*' && manifest_[i] == '/')) { |
+ ++i; |
+ } |
+ } |
+ } |
+ *index = i + 1; |
+} |
+ |
+} // namespace extensions |