summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/json_reader.cc126
-rw-r--r--base/json_reader.h57
-rw-r--r--base/json_reader_unittest.cc161
-rw-r--r--base/values.h6
-rw-r--r--chrome/browser/autocomplete/search_provider.cc3
-rw-r--r--chrome/browser/bookmarks/bookmark_storage.cc2
-rw-r--r--chrome/browser/extensions/extensions_service.cc2
-rw-r--r--chrome/browser/page_state.cc2
-rw-r--r--chrome/common/json_value_serializer.cc12
-rw-r--r--chrome/common/json_value_serializer.h12
-rw-r--r--chrome/common/json_value_serializer_perftest.cc4
-rw-r--r--chrome/common/json_value_serializer_unittest.cc18
-rw-r--r--chrome/common/pref_service.cc4
-rw-r--r--chrome/common/pref_service_uitest.cc2
-rw-r--r--chrome/common/pref_service_unittest.cc7
-rw-r--r--chrome/installer/util/master_preferences.cc2
-rw-r--r--chrome/test/automation/tab_proxy.cc2
-rw-r--r--chrome/test/ui/ui_test.cc2
18 files changed, 313 insertions, 111 deletions
diff --git a/base/json_reader.cc b/base/json_reader.cc
index c0e019b..2cea239 100644
--- a/base/json_reader.cc
+++ b/base/json_reader.cc
@@ -71,66 +71,116 @@ bool ReadHexDigits(JSONReader::Token& token, int digits) {
} // anonymous namespace
+const char* JSONReader::kBadRootElementType =
+ "Root value must be an array or object.";
+const char* JSONReader::kInvalidEscape =
+ "Invalid escape sequence.";
+const char* JSONReader::kSyntaxError =
+ "Syntax error.";
+const char* JSONReader::kTrailingComma =
+ "Trailing comma not allowed.";
+const char* JSONReader::kTooMuchNesting =
+ "Too much nesting.";
+const char* JSONReader::kUnexpectedDataAfterRoot =
+ "Unexpected data after root element.";
+const char* JSONReader::kUnsupportedEncoding =
+ "Unsupported encoding. JSON must be UTF-8.";
+const char* JSONReader::kUnquotedDictionaryKey =
+ "Dictionary keys must be quoted.";
+
/* static */
bool JSONReader::Read(const std::string& json,
Value** root,
bool allow_trailing_comma) {
- return JsonToValue(json, root, true, allow_trailing_comma);
+ return ReadAndReturnError(json, root, allow_trailing_comma, NULL);
+}
+
+/* static */
+bool JSONReader::ReadAndReturnError(const std::string& json,
+ Value** root,
+ bool allow_trailing_comma,
+ std::string *error_message_out) {
+ JSONReader reader = JSONReader();
+ if (reader.JsonToValue(json, root, true, allow_trailing_comma)) {
+ return true;
+ } else {
+ if (error_message_out)
+ *error_message_out = reader.error_message();
+ return false;
+ }
}
/* static */
-bool JSONReader::JsonToValue(const std::string& json,
- Value** root,
- bool check_root,
- bool allow_trailing_comma) {
+std::string JSONReader::FormatErrorMessage(int line, int column,
+ const char* description) {
+ return StringPrintf("Line: %i, column: %i, %s",
+ line, column, description);
+}
+
+JSONReader::JSONReader()
+ : start_pos_(NULL), json_pos_(NULL), stack_depth_(0),
+ allow_trailing_comma_(false) {}
+
+bool JSONReader::JsonToValue(const std::string& json, Value** root,
+ bool check_root, bool allow_trailing_comma) {
// The input must be in UTF-8.
- if (!IsStringUTF8(json.c_str()))
+ if (!IsStringUTF8(json.c_str())) {
+ error_message_ = kUnsupportedEncoding;
return false;
+ }
+
// The conversion from UTF8 to wstring removes null bytes for us
// (a good thing).
std::wstring json_wide(UTF8ToWide(json));
- const wchar_t* json_cstr = json_wide.c_str();
+ start_pos_ = json_wide.c_str();
// When the input JSON string starts with a UTF-8 Byte-Order-Mark
// (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a Unicode
// BOM (U+FEFF). To avoid the JSONReader::BuildValue() function from
// mis-treating a Unicode BOM as an invalid character and returning false,
// skip a converted Unicode BOM if it exists.
- if (!json_wide.empty() && json_cstr[0] == 0xFEFF) {
- ++json_cstr;
+ if (!json_wide.empty() && start_pos_[0] == 0xFEFF) {
+ ++start_pos_;
}
- JSONReader reader(json_cstr, allow_trailing_comma);
+ json_pos_ = start_pos_;
+ allow_trailing_comma_ = allow_trailing_comma;
+ stack_depth_ = 0;
+ error_message_.clear();
Value* temp_root = NULL;
- bool success = reader.BuildValue(&temp_root, check_root);
// Only modify root_ if we have valid JSON and nothing else.
- if (success && reader.ParseToken().type == Token::END_OF_INPUT) {
- *root = temp_root;
- return true;
+ if (BuildValue(&temp_root, check_root)) {
+ if (ParseToken().type == Token::END_OF_INPUT) {
+ *root = temp_root;
+ return true;
+ } else {
+ SetErrorMessage(kUnexpectedDataAfterRoot, json_pos_);
+ }
}
+ // Default to calling errors "syntax errors".
+ if (error_message_.empty())
+ SetErrorMessage(kSyntaxError, json_pos_);
+
if (temp_root)
delete temp_root;
return false;
}
-JSONReader::JSONReader(const wchar_t* json_start_pos,
- bool allow_trailing_comma)
- : json_pos_(json_start_pos),
- stack_depth_(0),
- allow_trailing_comma_(allow_trailing_comma) {}
-
bool JSONReader::BuildValue(Value** node, bool is_root) {
++stack_depth_;
- if (stack_depth_ > kStackLimit)
+ if (stack_depth_ > kStackLimit) {
+ SetErrorMessage(kTooMuchNesting, json_pos_);
return false;
+ }
Token token = ParseToken();
// The root token must be an array or an object.
if (is_root && token.type != Token::OBJECT_BEGIN &&
token.type != Token::ARRAY_BEGIN) {
+ SetErrorMessage(kBadRootElementType, json_pos_);
return false;
}
@@ -184,6 +234,7 @@ bool JSONReader::BuildValue(Value** node, bool is_root) {
// consumers need the parsing leniency, so handle accordingly.
if (token.type == Token::ARRAY_END) {
if (!allow_trailing_comma_) {
+ SetErrorMessage(kTrailingComma, json_pos_);
delete array;
return false;
}
@@ -212,6 +263,7 @@ bool JSONReader::BuildValue(Value** node, bool is_root) {
DictionaryValue* dict = new DictionaryValue;
while (token.type != Token::OBJECT_END) {
if (token.type != Token::STRING) {
+ SetErrorMessage(kUnquotedDictionaryKey, json_pos_);
delete dict;
return false;
}
@@ -252,6 +304,7 @@ bool JSONReader::BuildValue(Value** node, bool is_root) {
// consumers need the parsing leniency, so handle accordingly.
if (token.type == Token::OBJECT_END) {
if (!allow_trailing_comma_) {
+ SetErrorMessage(kTrailingComma, json_pos_);
delete dict;
return false;
}
@@ -347,12 +400,16 @@ JSONReader::Token JSONReader::ParseStringToken() {
// Make sure the escaped char is valid.
switch (c) {
case 'x':
- if (!ReadHexDigits(token, 2))
+ if (!ReadHexDigits(token, 2)) {
+ SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
return kInvalidToken;
+ }
break;
case 'u':
- if (!ReadHexDigits(token, 4))
+ if (!ReadHexDigits(token, 4)) {
+ SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
return kInvalidToken;
+ }
break;
case '\\':
case '/':
@@ -365,6 +422,7 @@ JSONReader::Token JSONReader::ParseStringToken() {
case '"':
break;
default:
+ SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
return kInvalidToken;
}
} else if ('"' == c) {
@@ -582,3 +640,25 @@ bool JSONReader::EatComment() {
return true;
}
+void JSONReader::SetErrorMessage(const char* description,
+ const wchar_t* error_pos) {
+ int line_number = 1;
+ int column_number = 1;
+
+ // Figure out the line and column the error occured at.
+ for (const wchar_t* pos = start_pos_; pos != error_pos; ++pos) {
+ if (*pos == '\0') {
+ NOTREACHED();
+ return;
+ }
+
+ if (*pos == '\n') {
+ ++line_number;
+ column_number = 1;
+ } else {
+ ++column_number;
+ }
+ }
+
+ error_message_ = FormatErrorMessage(line_number, column_number, description);
+}
diff --git a/base/json_reader.h b/base/json_reader.h
index 5e08bf0..2c9f27a 100644
--- a/base/json_reader.h
+++ b/base/json_reader.h
@@ -21,11 +21,12 @@
// character, the function skips a Unicode BOM at the beginning of the
// Unicode string (converted from the input UTF-8 string) before parsing it.
//
-// TODO(tc): It would be nice to give back an error string when we fail to
-// parse JSON.
// TODO(tc): Add a parsing option to to relax object keys being wrapped in
// double quotes
// TODO(tc): Add an option to disable comment stripping
+// TODO(aa): Consider making the constructor public and the static Read() method
+// only a convenience for the common uses with more complex configuration going
+// on the instance.
#ifndef BASE_JSON_READER_H_
#define BASE_JSON_READER_H_
@@ -74,25 +75,51 @@ class JSONReader {
}
};
- // Reads and parses |json| and populates |root|. If |json| is not a properly
- // formed JSON string, returns false and leaves root unaltered. If
- // allow_trailing_comma is true, we will ignore trailing commas in objects
- // and arrays even though this goes against the RFC.
+ // Error messages that can be returned.
+ static const char* kBadRootElementType;
+ static const char* kInvalidEscape;
+ static const char* kSyntaxError;
+ static const char* kTrailingComma;
+ static const char* kTooMuchNesting;
+ static const char* kUnexpectedDataAfterRoot;
+ static const char* kUnsupportedEncoding;
+ static const char* kUnquotedDictionaryKey;
+
+ // Reads and parses |json| and populates |root|. If |json| is not a properly
+ // formed JSON string, returns false, leaves root unaltered and sets
+ // error_message if it was non-null. If allow_trailing_comma is true, we will
+ // ignore trailing commas in objects and arrays even though this goes against
+ // the RFC.
static bool Read(const std::string& json,
Value** root,
bool allow_trailing_comma);
+ // Reads and parses |json| like Read(). |error_message_out| is optional. If
+ // specified and false is returned, error_message_out will be populated with
+ // a string describing the error. Otherwise, error_message_out is unmodified.
+ static bool ReadAndReturnError(const std::string& json,
+ Value** root,
+ bool allow_trailing_comma,
+ std::string *error_message_out);
+
private:
- JSONReader(const wchar_t* json_start_pos, bool allow_trailing_comma);
+ static std::string FormatErrorMessage(int line, int column,
+ const char* description);
+
+ JSONReader();
DISALLOW_EVIL_CONSTRUCTORS(JSONReader);
FRIEND_TEST(JSONReaderTest, Reading);
+ FRIEND_TEST(JSONReaderTest, ErrorMessages);
+
+ // Returns the error message if the last call to JsonToValue() failed. If the
+ // last call did not fail, returns a valid empty string.
+ std::string error_message() { return error_message_; }
// Pass through method from JSONReader::Read. We have this so unittests can
// disable the root check.
- static bool JsonToValue(const std::string& json, Value** root,
- bool check_root,
- bool allow_trailing_comma);
+ bool JsonToValue(const std::string& json, Value** root, bool check_root,
+ bool allow_trailing_comma);
// Recursively build Value. Returns false if we don't have a valid JSON
// string. If |is_root| is true, we verify that the root element is either
@@ -135,6 +162,13 @@ class JSONReader {
// Checks if json_pos_ matches str.
bool NextStringMatch(const std::wstring& str);
+ // Creates the error message that will be returned to the caller. The current
+ // line and column are determined and added into the final message.
+ void SetErrorMessage(const char* description, const wchar_t* error_pos);
+
+ // Pointer to the starting position in the input string.
+ const wchar_t* start_pos_;
+
// Pointer to the current position in the input string.
const wchar_t* json_pos_;
@@ -143,6 +177,9 @@ class JSONReader {
// A parser flag that allows trailing commas in objects and arrays.
bool allow_trailing_comma_;
+
+ // Contains the error message for the last call to JsonToValue(), if any.
+ std::string error_message_;
};
#endif // BASE_JSON_READER_H_
diff --git a/base/json_reader_unittest.cc b/base/json_reader_unittest.cc
index ebc7062..7a2c16e 100644
--- a/base/json_reader_unittest.cc
+++ b/base/json_reader_unittest.cc
@@ -10,26 +10,26 @@
TEST(JSONReaderTest, Reading) {
// some whitespace checking
Value* root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue(" null ", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue(" null ", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_NULL));
delete root;
// Invalid JSON string
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("nu", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("nu", &root, false, false));
ASSERT_FALSE(root);
// Simple bool
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("true ", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("true ", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
delete root;
// Test number formats
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("43", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("43", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
int int_val = 0;
@@ -39,19 +39,19 @@ TEST(JSONReaderTest, Reading) {
// According to RFC4627, oct, hex, and leading zeros are invalid JSON.
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("043", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("043", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("0x43", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("0x43", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("00", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("00", &root, false, false));
ASSERT_FALSE(root);
// Test 0 (which needs to be special cased because of the leading zero
// clause).
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("0", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("0", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
int_val = 1;
@@ -62,7 +62,7 @@ TEST(JSONReaderTest, Reading) {
// Numbers that overflow ints should succeed, being internally promoted to
// storage as doubles
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("2147483648", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("2147483648", &root, false, false));
ASSERT_TRUE(root);
double real_val;
#ifdef ARCH_CPU_32_BITS
@@ -78,7 +78,7 @@ TEST(JSONReaderTest, Reading) {
#endif
delete root;
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("-2147483649", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("-2147483649", &root, false, false));
ASSERT_TRUE(root);
#ifdef ARCH_CPU_32_BITS
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
@@ -95,7 +95,7 @@ TEST(JSONReaderTest, Reading) {
// Parse a double
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("43.1", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("43.1", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
@@ -104,7 +104,7 @@ TEST(JSONReaderTest, Reading) {
delete root;
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("4.3e-1", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("4.3e-1", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
@@ -113,7 +113,7 @@ TEST(JSONReaderTest, Reading) {
delete root;
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("2.1e0", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("2.1e0", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
@@ -122,7 +122,7 @@ TEST(JSONReaderTest, Reading) {
delete root;
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("2.1e+0001", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("2.1e+0001", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
@@ -131,7 +131,7 @@ TEST(JSONReaderTest, Reading) {
delete root;
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("0.01", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("0.01", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
@@ -140,7 +140,7 @@ TEST(JSONReaderTest, Reading) {
delete root;
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("1.00", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("1.00", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
real_val = 0.0;
@@ -150,57 +150,57 @@ TEST(JSONReaderTest, Reading) {
// Fractional parts must have a digit before and after the decimal point.
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1.", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1.", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue(".1", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue(".1", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1.e10", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1.e10", &root, false, false));
ASSERT_FALSE(root);
// Exponent must have a digit following the 'e'.
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1e", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1e", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1E", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1E", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1e1.", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1e1.", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1e1.0", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1e1.0", &root, false, false));
ASSERT_FALSE(root);
// INF/-INF/NaN are not valid
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("1e1000", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("1e1000", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("-1e1000", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("-1e1000", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("NaN", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("NaN", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("nan", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("nan", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("inf", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("inf", &root, false, false));
ASSERT_FALSE(root);
// Invalid number formats
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("4.3.1", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("4.3.1", &root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("4e3.1", &root, false, false));
+ ASSERT_FALSE(JSONReader().JsonToValue("4e3.1", &root, false, false));
ASSERT_FALSE(root);
// Test string parser
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("\"hello world\"", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("\"hello world\"", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
std::wstring str_val;
@@ -210,7 +210,7 @@ TEST(JSONReaderTest, Reading) {
// Empty string
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("\"\"", &root, false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("\"\"", &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
@@ -220,8 +220,8 @@ TEST(JSONReaderTest, Reading) {
// Test basic string escapes
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"", &root,
- false, false));
+ ASSERT_TRUE(JSONReader().JsonToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"",
+ &root, false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
str_val.clear();
@@ -231,7 +231,7 @@ TEST(JSONReaderTest, Reading) {
// Test hex and unicode escapes including the null character.
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("\"\\x41\\x00\\u1234\"", &root, false,
+ ASSERT_TRUE(JSONReader().JsonToValue("\"\\x41\\x00\\u1234\"", &root, false,
false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
@@ -242,27 +242,27 @@ TEST(JSONReaderTest, Reading) {
// Test invalid strings
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("\"no closing quote", &root, false,
+ ASSERT_FALSE(JSONReader().JsonToValue("\"no closing quote", &root, false,
false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("\"\\z invalid escape char\"", &root,
+ ASSERT_FALSE(JSONReader().JsonToValue("\"\\z invalid escape char\"", &root,
false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("\"\\xAQ invalid hex code\"", &root,
+ ASSERT_FALSE(JSONReader().JsonToValue("\"\\xAQ invalid hex code\"", &root,
false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("not enough hex chars\\x1\"", &root,
+ ASSERT_FALSE(JSONReader().JsonToValue("not enough hex chars\\x1\"", &root,
false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("\"not enough escape chars\\u123\"",
+ ASSERT_FALSE(JSONReader().JsonToValue("\"not enough escape chars\\u123\"",
&root, false, false));
ASSERT_FALSE(root);
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("\"extra backslash at end of input\\\"",
+ ASSERT_FALSE(JSONReader().JsonToValue("\"extra backslash at end of input\\\"",
&root, false, false));
ASSERT_FALSE(root);
@@ -478,7 +478,7 @@ TEST(JSONReaderTest, Reading) {
// Test utf8 encoded input
root = NULL;
- ASSERT_TRUE(JSONReader::JsonToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"", &root,
+ ASSERT_TRUE(JSONReader().JsonToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"", &root,
false, false));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
@@ -489,9 +489,9 @@ TEST(JSONReaderTest, Reading) {
// Test invalid utf8 encoded input
root = NULL;
- ASSERT_FALSE(JSONReader::JsonToValue("\"345\xb0\xa1\xb0\xa2\"", &root,
+ ASSERT_FALSE(JSONReader().JsonToValue("\"345\xb0\xa1\xb0\xa2\"", &root,
false, false));
- ASSERT_FALSE(JSONReader::JsonToValue("\"123\xc0\x81\"", &root,
+ ASSERT_FALSE(JSONReader().JsonToValue("\"123\xc0\x81\"", &root,
false, false));
// Test invalid root objects.
@@ -501,3 +501,76 @@ TEST(JSONReaderTest, Reading) {
ASSERT_FALSE(JSONReader::Read("10", &root, false));
ASSERT_FALSE(JSONReader::Read("\"root\"", &root, false));
}
+
+TEST(JSONReaderTest, ErrorMessages) {
+ // Error strings should not be modified in case of success.
+ std::string error_message;
+ Value* root = NULL;
+ ASSERT_TRUE(JSONReader::ReadAndReturnError("[42]", &root, false,
+ &error_message));
+ ASSERT_TRUE(error_message.empty());
+
+ // Test line and column counting
+ const char* big_json = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]";
+ // error here --------------------------------^
+ ASSERT_FALSE(JSONReader::ReadAndReturnError(big_json, &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(5, 9, JSONReader::kSyntaxError),
+ error_message);
+
+ // Test each of the error conditions
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("{},{}", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 3,
+ JSONReader::kUnexpectedDataAfterRoot), error_message);
+
+ std::string nested_json;
+ for (int i = 0; i < 101; ++i) {
+ nested_json.insert(nested_json.begin(), '[');
+ nested_json.append(1, ']');
+ }
+ ASSERT_FALSE(JSONReader::ReadAndReturnError(nested_json, &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 101, JSONReader::kTooMuchNesting),
+ error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("42", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 1,
+ JSONReader::kBadRootElementType), error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("[1,]", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 4, JSONReader::kTrailingComma),
+ error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("{foo:\"bar\"}", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 2,
+ JSONReader::kUnquotedDictionaryKey), error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}", &root,
+ false, &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 14, JSONReader::kTrailingComma),
+ error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("[nu]", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 2, JSONReader::kSyntaxError),
+ error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
+ error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
+ error_message);
+
+ ASSERT_FALSE(JSONReader::ReadAndReturnError("[\"xxx\\q\"]", &root, false,
+ &error_message));
+ ASSERT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
+ error_message);
+}
diff --git a/base/values.h b/base/values.h
index 951776a..4786ff6 100644
--- a/base/values.h
+++ b/base/values.h
@@ -352,8 +352,10 @@ class ValueSerializer {
// The method should return true if and only if the root parameter is set
// to a complete Value representation of the serialized form. If the
// return value is true, the caller takes ownership of the objects pointed
- // to by root. If the return value is false, root should be unchanged.
- virtual bool Deserialize(Value** root) = 0;
+ // to by root. If the return value is false, root should be unchanged and if
+ // error_message is non-null, it should be filled with a message describing
+ // the error.
+ virtual bool Deserialize(Value** root, std::string* error_message) = 0;
};
#endif // BASE_VALUES_H_
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc
index 47ccdd8..0bad51c 100644
--- a/chrome/browser/autocomplete/search_provider.cc
+++ b/chrome/browser/autocomplete/search_provider.cc
@@ -133,7 +133,8 @@ void SearchProvider::OnURLFetchComplete(const URLFetcher* source,
deserializer.set_allow_trailing_comma(true);
Value* root_val = NULL;
have_suggest_results_ = status.is_success() && (response_code == 200) &&
- deserializer.Deserialize(&root_val) && ParseSuggestResults(root_val);
+ deserializer.Deserialize(&root_val, NULL) &&
+ ParseSuggestResults(root_val);
delete root_val;
ConvertResultsToAutocompleteMatches();
listener_->OnProviderUpdate(!suggest_results_.empty());
diff --git a/chrome/browser/bookmarks/bookmark_storage.cc b/chrome/browser/bookmarks/bookmark_storage.cc
index 591f190..e6e9a50 100644
--- a/chrome/browser/bookmarks/bookmark_storage.cc
+++ b/chrome/browser/bookmarks/bookmark_storage.cc
@@ -167,7 +167,7 @@ void BookmarkStorageBackend::Read(scoped_refptr<BookmarkStorage> service,
Value* root = NULL;
if (bookmark_file_exists) {
JSONFileValueSerializer serializer(path);
- serializer.Deserialize(&root);
+ serializer.Deserialize(&root, NULL);
}
// BookmarkStorage takes ownership of root.
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 67d508e..32cc861 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -86,7 +86,7 @@ bool ExtensionsServiceBackend::LoadExtensionsFromDirectory(
JSONFileValueSerializer serializer(manifest_path.ToWStringHack());
Value* root = NULL;
- if (!serializer.Deserialize(&root)) {
+ if (!serializer.Deserialize(&root, NULL)) {
ReportExtensionLoadError(frontend.get(),
Extension::kInvalidManifestError);
continue;
diff --git a/chrome/browser/page_state.cc b/chrome/browser/page_state.cc
index e0c350f..f47a4cf 100644
--- a/chrome/browser/page_state.cc
+++ b/chrome/browser/page_state.cc
@@ -42,7 +42,7 @@ void PageState::InitWithBytes(const std::string& bytes) {
JSONStringValueSerializer serializer(bytes);
Value* root = NULL;
- if (!serializer.Deserialize(&root))
+ if (!serializer.Deserialize(&root, NULL))
NOTREACHED();
if (root != NULL && root->GetType() == Value::TYPE_DICTIONARY) {
diff --git a/chrome/common/json_value_serializer.cc b/chrome/common/json_value_serializer.cc
index b07fb29..82938ec 100644
--- a/chrome/common/json_value_serializer.cc
+++ b/chrome/common/json_value_serializer.cc
@@ -21,11 +21,14 @@ bool JSONStringValueSerializer::Serialize(const Value& root) {
return true;
}
-bool JSONStringValueSerializer::Deserialize(Value** root) {
+bool JSONStringValueSerializer::Deserialize(Value** root,
+ std::string* error_message) {
if (!json_string_)
return false;
- return JSONReader::Read(*json_string_, root, allow_trailing_comma_);
+ return JSONReader::ReadAndReturnError(*json_string_, root,
+ allow_trailing_comma_,
+ error_message);
}
/******* File Serializer *******/
@@ -47,12 +50,13 @@ bool JSONFileValueSerializer::Serialize(const Value& root) {
return true;
}
-bool JSONFileValueSerializer::Deserialize(Value** root) {
+bool JSONFileValueSerializer::Deserialize(Value** root,
+ std::string* error_message) {
std::string json_string;
if (!file_util::ReadFileToString(json_file_path_, &json_string)) {
return false;
}
JSONStringValueSerializer serializer(json_string);
- return serializer.Deserialize(root);
+ return serializer.Deserialize(root, error_message);
}
diff --git a/chrome/common/json_value_serializer.h b/chrome/common/json_value_serializer.h
index a86369d..2e581ee 100644
--- a/chrome/common/json_value_serializer.h
+++ b/chrome/common/json_value_serializer.h
@@ -41,8 +41,10 @@ class JSONStringValueSerializer : public ValueSerializer {
// in to the constructor into a structure of Value objects. If the return
// value is true, the |root| parameter will be set to point to a new Value
// object that corresponds to the values represented in the string. The
- // caller takes ownership of the returned Value objects.
- bool Deserialize(Value** root);
+ // caller takes ownership of the returned Value objects. Otherwise, the root
+ // value will be changed and if |error_message| is non-null, it will contain
+ // a string describing the error.
+ bool Deserialize(Value** root, std::string* error_message);
void set_pretty_print(bool new_value) { pretty_print_ = new_value; }
bool pretty_print() { return pretty_print_; }
@@ -86,8 +88,10 @@ class JSONFileValueSerializer : public ValueSerializer {
// in to the constructor into a structure of Value objects. If the return
// value is true, the |root| parameter will be set to point to a new Value
// object that corresponds to the values represented in the file. The
- // caller takes ownership of the returned Value objects.
- bool Deserialize(Value** root);
+ // caller takes ownership of the returned Value objects. Otherwise, the root
+ // value will be changed and if |error_message| is non-null, it will contain
+ // a string describing the error.
+ bool Deserialize(Value** root, std::string* error_message);
private:
std::wstring json_file_path_;
diff --git a/chrome/common/json_value_serializer_perftest.cc b/chrome/common/json_value_serializer_perftest.cc
index ddc34db..6570d34 100644
--- a/chrome/common/json_value_serializer_perftest.cc
+++ b/chrome/common/json_value_serializer_perftest.cc
@@ -54,7 +54,7 @@ TEST_F(JSONValueSerializerTests, Reading) {
for (size_t j = 0; j < test_cases_.size(); ++j) {
Value* root = NULL;
JSONStringValueSerializer reader(test_cases_[j]);
- ASSERT_TRUE(reader.Deserialize(&root));
+ ASSERT_TRUE(reader.Deserialize(&root, NULL));
delete root;
}
}
@@ -69,7 +69,7 @@ TEST_F(JSONValueSerializerTests, CompactWriting) {
for (size_t i = 0; i < test_cases_.size(); ++i) {
Value* root = NULL;
JSONStringValueSerializer reader(test_cases_[i]);
- ASSERT_TRUE(reader.Deserialize(&root));
+ ASSERT_TRUE(reader.Deserialize(&root, NULL));
test_cases.push_back(root);
}
diff --git a/chrome/common/json_value_serializer_unittest.cc b/chrome/common/json_value_serializer_unittest.cc
index 3997205..3dd175b 100644
--- a/chrome/common/json_value_serializer_unittest.cc
+++ b/chrome/common/json_value_serializer_unittest.cc
@@ -18,7 +18,7 @@ TEST(JSONValueSerializerTest, Roundtrip) {
"{\"bool\":true,\"int\":42,\"list\":[1,2],\"null\":null,\"real\":3.14}";
Value* root = NULL;
JSONStringValueSerializer serializer(original_serialization);
- ASSERT_TRUE(serializer.Deserialize(&root));
+ ASSERT_TRUE(serializer.Deserialize(&root, NULL));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
@@ -121,7 +121,7 @@ TEST(JSONValueSerializerTest, UnicodeStrings) {
// escaped ascii text -> json
Value* deserial_root = NULL;
JSONStringValueSerializer deserializer(expected);
- ASSERT_TRUE(deserializer.Deserialize(&deserial_root));
+ ASSERT_TRUE(deserializer.Deserialize(&deserial_root, NULL));
DictionaryValue* dict_root = static_cast<DictionaryValue*>(deserial_root);
std::wstring web_value;
ASSERT_TRUE(dict_root->GetString(L"web", &web_value));
@@ -145,7 +145,7 @@ TEST(JSONValueSerializerTest, HexStrings) {
// escaped ascii text -> json
Value* deserial_root = NULL;
JSONStringValueSerializer deserializer(expected);
- ASSERT_TRUE(deserializer.Deserialize(&deserial_root));
+ ASSERT_TRUE(deserializer.Deserialize(&deserial_root, NULL));
DictionaryValue* dict_root = static_cast<DictionaryValue*>(deserial_root);
std::wstring test_value;
ASSERT_TRUE(dict_root->GetString(L"test", &test_value));
@@ -156,7 +156,7 @@ TEST(JSONValueSerializerTest, HexStrings) {
deserial_root = NULL;
std::string escaped_chars = "{\"test\":\"\\x67\\x6f\"}";
JSONStringValueSerializer deserializer2(escaped_chars);
- ASSERT_TRUE(deserializer2.Deserialize(&deserial_root));
+ ASSERT_TRUE(deserializer2.Deserialize(&deserial_root, NULL));
dict_root = static_cast<DictionaryValue*>(deserial_root);
ASSERT_TRUE(dict_root->GetString(L"test", &test_value));
ASSERT_EQ(std::wstring(L"go"), test_value);
@@ -172,8 +172,8 @@ TEST(JSONValueSerializerTest, AllowTrailingComma) {
JSONStringValueSerializer serializer(test_with_commas);
serializer.set_allow_trailing_comma(true);
JSONStringValueSerializer serializer_expected(test_no_commas);
- ASSERT_TRUE(serializer.Deserialize(&root));
- ASSERT_TRUE(serializer_expected.Deserialize(&root_expected));
+ ASSERT_TRUE(serializer.Deserialize(&root, NULL));
+ ASSERT_TRUE(serializer_expected.Deserialize(&root_expected, NULL));
ASSERT_TRUE(root->Equals(root_expected));
delete root;
@@ -262,7 +262,7 @@ TEST_F(JSONFileValueSerializerTest, Roundtrip) {
JSONFileValueSerializer deserializer(original_file_path);
Value* root;
- ASSERT_TRUE(deserializer.Deserialize(&root));
+ ASSERT_TRUE(deserializer.Deserialize(&root, NULL));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
@@ -312,7 +312,7 @@ TEST_F(JSONFileValueSerializerTest, RoundtripNested) {
JSONFileValueSerializer deserializer(original_file_path);
Value* root;
- ASSERT_TRUE(deserializer.Deserialize(&root));
+ ASSERT_TRUE(deserializer.Deserialize(&root, NULL));
// Now try writing.
std::wstring written_file_path = test_dir_;
@@ -338,7 +338,7 @@ TEST_F(JSONFileValueSerializerTest, NoWhitespace) {
ASSERT_TRUE(file_util::PathExists(source_file_path));
JSONFileValueSerializer serializer(source_file_path);
Value* root;
- ASSERT_TRUE(serializer.Deserialize(&root));
+ ASSERT_TRUE(serializer.Deserialize(&root, NULL));
ASSERT_TRUE(root);
delete root;
}
diff --git a/chrome/common/pref_service.cc b/chrome/common/pref_service.cc
index a942e32..8f3322a 100644
--- a/chrome/common/pref_service.cc
+++ b/chrome/common/pref_service.cc
@@ -137,7 +137,7 @@ bool PrefService::LoadPersistentPrefs(const std::wstring& file_path) {
JSONFileValueSerializer serializer(file_path);
Value* root = NULL;
- if (serializer.Deserialize(&root)) {
+ if (serializer.Deserialize(&root, NULL)) {
// Preferences should always have a dictionary root.
if (!root->IsType(Value::TYPE_DICTIONARY)) {
delete root;
@@ -156,7 +156,7 @@ void PrefService::ReloadPersistentPrefs() {
JSONFileValueSerializer serializer(pref_filename_);
Value* root;
- if (serializer.Deserialize(&root)) {
+ if (serializer.Deserialize(&root, NULL)) {
// Preferences should always have a dictionary root.
if (!root->IsType(Value::TYPE_DICTIONARY)) {
delete root;
diff --git a/chrome/common/pref_service_uitest.cc b/chrome/common/pref_service_uitest.cc
index 9c46d9d..b9f0774 100644
--- a/chrome/common/pref_service_uitest.cc
+++ b/chrome/common/pref_service_uitest.cc
@@ -83,7 +83,7 @@ TEST_F(PreferenceServiceTest, PreservedWindowPlacementIsLoaded) {
JSONFileValueSerializer deserializer(tmp_pref_file_);
Value* root = NULL;
- ASSERT_TRUE(deserializer.Deserialize(&root));
+ ASSERT_TRUE(deserializer.Deserialize(&root, NULL));
ASSERT_TRUE(root);
ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
diff --git a/chrome/common/pref_service_unittest.cc b/chrome/common/pref_service_unittest.cc
index 37d04de..6c63756 100644
--- a/chrome/common/pref_service_unittest.cc
+++ b/chrome/common/pref_service_unittest.cc
@@ -155,19 +155,20 @@ TEST_F(PrefServiceTest, Overlay) {
Value* transient_value;
{
JSONStringValueSerializer serializer(transient);
- ASSERT_TRUE(serializer.Deserialize(&transient_value));
+ ASSERT_TRUE(serializer.Deserialize(&transient_value, NULL));
}
prefs.transient()->Set(transient_string, transient_value);
Value* both_transient_value;
{
JSONStringValueSerializer serializer(transient);
- ASSERT_TRUE(serializer.Deserialize(&both_transient_value));
+ ASSERT_TRUE(serializer.Deserialize(&both_transient_value, NULL));
}
prefs.transient()->Set(L"both", both_transient_value);
// Register test prefs
- const wchar_t* kTypes[] = { L"neither.", L"persistent.", L"transient.", L"both." };
+ const wchar_t* kTypes[] =
+ { L"neither.", L"persistent.", L"transient.", L"both." };
for (size_t i = 0; i < arraysize(kTypes); ++i) {
wchar_t temp[1024];
wcscpy_s(temp, kTypes[i]);
diff --git a/chrome/installer/util/master_preferences.cc b/chrome/installer/util/master_preferences.cc
index ee09b48..34d10a9 100644
--- a/chrome/installer/util/master_preferences.cc
+++ b/chrome/installer/util/master_preferences.cc
@@ -13,7 +13,7 @@ namespace {
DictionaryValue* ReadJSONPrefs(const std::string& data) {
JSONStringValueSerializer json(data);
Value* root;
- if (!json.Deserialize(&root))
+ if (!json.Deserialize(&root, NULL))
return NULL;
if (!root->IsType(Value::TYPE_DICTIONARY)) {
delete root;
diff --git a/chrome/test/automation/tab_proxy.cc b/chrome/test/automation/tab_proxy.cc
index 0b87689..91cdd1b 100644
--- a/chrome/test/automation/tab_proxy.cc
+++ b/chrome/test/automation/tab_proxy.cc
@@ -555,7 +555,7 @@ bool TabProxy::ExecuteAndExtractValue(const std::wstring& frame_xpath,
json.append("]");
JSONStringValueSerializer deserializer(json);
- succeeded = deserializer.Deserialize(value);
+ succeeded = deserializer.Deserialize(value, NULL);
delete response;
return succeeded;
diff --git a/chrome/test/ui/ui_test.cc b/chrome/test/ui/ui_test.cc
index 415533e..5b549a0 100644
--- a/chrome/test/ui/ui_test.cc
+++ b/chrome/test/ui/ui_test.cc
@@ -463,7 +463,7 @@ static DictionaryValue* LoadDictionaryValueFromPath(const std::wstring& path) {
JSONFileValueSerializer serializer(path);
Value* root_value = NULL;
- if (serializer.Deserialize(&root_value) &&
+ if (serializer.Deserialize(&root_value, NULL) &&
root_value->GetType() != Value::TYPE_DICTIONARY) {
delete root_value;
return NULL;