diff options
-rw-r--r-- | base/json_reader.cc | 126 | ||||
-rw-r--r-- | base/json_reader.h | 57 | ||||
-rw-r--r-- | base/json_reader_unittest.cc | 161 | ||||
-rw-r--r-- | base/values.h | 6 | ||||
-rw-r--r-- | chrome/browser/autocomplete/search_provider.cc | 3 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_storage.cc | 2 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 2 | ||||
-rw-r--r-- | chrome/browser/page_state.cc | 2 | ||||
-rw-r--r-- | chrome/common/json_value_serializer.cc | 12 | ||||
-rw-r--r-- | chrome/common/json_value_serializer.h | 12 | ||||
-rw-r--r-- | chrome/common/json_value_serializer_perftest.cc | 4 | ||||
-rw-r--r-- | chrome/common/json_value_serializer_unittest.cc | 18 | ||||
-rw-r--r-- | chrome/common/pref_service.cc | 4 | ||||
-rw-r--r-- | chrome/common/pref_service_uitest.cc | 2 | ||||
-rw-r--r-- | chrome/common/pref_service_unittest.cc | 7 | ||||
-rw-r--r-- | chrome/installer/util/master_preferences.cc | 2 | ||||
-rw-r--r-- | chrome/test/automation/tab_proxy.cc | 2 | ||||
-rw-r--r-- | chrome/test/ui/ui_test.cc | 2 |
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; |