Add error messages to JSONReader and friends. This required a bit of refactoring to do cleanly. This CL doesn't actually use this capability anywhere except for unit tests. I will add that in a future CL.

Review URL: http://codereview.chromium.org/13169

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6459 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: 88e7284573598db75013dbcbd9debb311b1e255c
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 @@
 
 }  // 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::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()))
+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 */
+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())) {
+    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 @@
             // 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 @@
         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 @@
             // 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 @@
       // 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 @@
         case '"':
           break;
         default:
+          SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
           return kInvalidToken;
       }
     } else if ('"' == c) {
@@ -582,3 +640,25 @@
   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 @@
     }
   };
 
-  // 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 @@
   // 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 @@
 
   // 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 @@
 
   // 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 @@
   // 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 @@
 #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 @@
 
   // 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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
 
   // 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 @@
 
   // 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 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 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 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 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 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 @@
   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 @@
   // 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_