detect preferences errors

BUG=38352
TEST=none

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

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


CrOS-Libchrome-Original-Commit: ba3996771f6d19298ee655992b2f6291a314ea76
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc
index bdc682b..cf9ce02 100644
--- a/base/json/json_reader.cc
+++ b/base/json/json_reader.cc
@@ -95,40 +95,79 @@
 /* static */
 Value* JSONReader::Read(const std::string& json,
                         bool allow_trailing_comma) {
-  return ReadAndReturnError(json, allow_trailing_comma, NULL);
+  return ReadAndReturnError(json, allow_trailing_comma, NULL, NULL);
 }
 
 /* static */
 Value* JSONReader::ReadAndReturnError(const std::string& json,
                                       bool allow_trailing_comma,
-                                      std::string *error_message_out) {
+                                      int* error_code_out,
+                                      std::string* error_msg_out) {
   JSONReader reader = JSONReader();
   Value* root = reader.JsonToValue(json, true, allow_trailing_comma);
   if (root)
     return root;
 
-  if (error_message_out)
-    *error_message_out = reader.error_message();
+  if (error_code_out)
+    *error_code_out = reader.error_code();
+  if (error_msg_out)
+    *error_msg_out = reader.GetErrorMessage();
 
   return NULL;
 }
 
 /* static */
 std::string JSONReader::FormatErrorMessage(int line, int column,
-                                           const char* description) {
-  return StringPrintf("Line: %i, column: %i, %s",
-                      line, column, description);
+                                           const std::string& description) {
+  if (line || column) {
+    return StringPrintf("Line: %i, column: %i, %s",
+                        line, column, description.c_str());
+  }
+  return description;
+}
+
+/* static */
+std::string JSONReader::ErrorCodeToString(JsonParseError error_code) {
+  switch (error_code) {
+    case JSON_NO_ERROR:
+      return std::string();
+    case JSON_BAD_ROOT_ELEMENT_TYPE:
+      return kBadRootElementType;
+    case JSON_INVALID_ESCAPE:
+      return kInvalidEscape;
+    case JSON_SYNTAX_ERROR:
+      return kSyntaxError;
+    case JSON_TRAILING_COMMA:
+      return kTrailingComma;
+    case JSON_TOO_MUCH_NESTING:
+      return kTooMuchNesting;
+    case JSON_UNEXPECTED_DATA_AFTER_ROOT:
+      return kUnexpectedDataAfterRoot;
+    case JSON_UNSUPPORTED_ENCODING:
+      return kUnsupportedEncoding;
+    case JSON_UNQUOTED_DICTIONARY_KEY:
+      return kUnquotedDictionaryKey;
+    default:
+      NOTREACHED();
+      return std::string();
+  }
+}
+
+std::string JSONReader::GetErrorMessage() const {
+  return FormatErrorMessage(error_line_, error_col_,
+                            ErrorCodeToString(error_code_));
 }
 
 JSONReader::JSONReader()
     : start_pos_(NULL), json_pos_(NULL), stack_depth_(0),
-      allow_trailing_comma_(false) {}
+      allow_trailing_comma_(false),
+      error_code_(JSON_NO_ERROR), error_line_(0), error_col_(0) {}
 
 Value* JSONReader::JsonToValue(const std::string& json, bool check_root,
                                bool allow_trailing_comma) {
   // The input must be in UTF-8.
   if (!IsStringUTF8(json.c_str())) {
-    error_message_ = kUnsupportedEncoding;
+    error_code_ = JSON_UNSUPPORTED_ENCODING;
     return NULL;
   }
 
@@ -149,20 +188,20 @@
   json_pos_ = start_pos_;
   allow_trailing_comma_ = allow_trailing_comma;
   stack_depth_ = 0;
-  error_message_.clear();
+  error_code_ = JSON_NO_ERROR;
 
   scoped_ptr<Value> root(BuildValue(check_root));
   if (root.get()) {
     if (ParseToken().type == Token::END_OF_INPUT) {
       return root.release();
     } else {
-      SetErrorMessage(kUnexpectedDataAfterRoot, json_pos_);
+      SetErrorCode(JSON_UNEXPECTED_DATA_AFTER_ROOT, json_pos_);
     }
   }
 
   // Default to calling errors "syntax errors".
-  if (error_message_.empty())
-    SetErrorMessage(kSyntaxError, json_pos_);
+  if (error_code_ == 0)
+    SetErrorCode(JSON_SYNTAX_ERROR, json_pos_);
 
   return NULL;
 }
@@ -170,7 +209,7 @@
 Value* JSONReader::BuildValue(bool is_root) {
   ++stack_depth_;
   if (stack_depth_ > kStackLimit) {
-    SetErrorMessage(kTooMuchNesting, json_pos_);
+    SetErrorCode(JSON_TOO_MUCH_NESTING, json_pos_);
     return NULL;
   }
 
@@ -178,7 +217,7 @@
   // 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_);
+    SetErrorCode(JSON_BAD_ROOT_ELEMENT_TYPE, json_pos_);
     return NULL;
   }
 
@@ -234,7 +273,7 @@
             // consumers need the parsing leniency, so handle accordingly.
             if (token.type == Token::ARRAY_END) {
               if (!allow_trailing_comma_) {
-                SetErrorMessage(kTrailingComma, json_pos_);
+                SetErrorCode(JSON_TRAILING_COMMA, json_pos_);
                 return NULL;
               }
               // Trailing comma OK, stop parsing the Array.
@@ -259,7 +298,7 @@
         node.reset(new DictionaryValue);
         while (token.type != Token::OBJECT_END) {
           if (token.type != Token::STRING) {
-            SetErrorMessage(kUnquotedDictionaryKey, json_pos_);
+            SetErrorCode(JSON_UNQUOTED_DICTIONARY_KEY, json_pos_);
             return NULL;
           }
           scoped_ptr<Value> dict_key_value(DecodeString(token));
@@ -294,7 +333,7 @@
             // consumers need the parsing leniency, so handle accordingly.
             if (token.type == Token::OBJECT_END) {
               if (!allow_trailing_comma_) {
-                SetErrorMessage(kTrailingComma, json_pos_);
+                SetErrorCode(JSON_TRAILING_COMMA, json_pos_);
                 return NULL;
               }
               // Trailing comma OK, stop parsing the Object.
@@ -384,13 +423,13 @@
       switch (c) {
         case 'x':
           if (!ReadHexDigits(token, 2)) {
-            SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
+            SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length);
             return kInvalidToken;
           }
           break;
         case 'u':
           if (!ReadHexDigits(token, 4)) {
-            SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
+            SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length);
             return kInvalidToken;
           }
           break;
@@ -405,7 +444,7 @@
         case '"':
           break;
         default:
-          SetErrorMessage(kInvalidEscape, json_pos_ + token.length);
+          SetErrorCode(JSON_INVALID_ESCAPE, json_pos_ + token.length);
           return kInvalidToken;
       }
     } else if ('"' == c) {
@@ -617,8 +656,8 @@
   return true;
 }
 
-void JSONReader::SetErrorMessage(const char* description,
-                                 const wchar_t* error_pos) {
+void JSONReader::SetErrorCode(JsonParseError error,
+                              const wchar_t* error_pos) {
   int line_number = 1;
   int column_number = 1;
 
@@ -637,7 +676,9 @@
     }
   }
 
-  error_message_ = FormatErrorMessage(line_number, column_number, description);
+  error_line_ = line_number;
+  error_col_ = column_number;
+  error_code_ = error;
 }
 
 }  // namespace base
diff --git a/base/json/json_reader.h b/base/json/json_reader.h
index 47598f4..aa0c2a7 100644
--- a/base/json/json_reader.h
+++ b/base/json/json_reader.h
@@ -82,7 +82,20 @@
     }
   };
 
-  // Error messages that can be returned.
+  // Error codes during parsing.
+  enum JsonParseError {
+    JSON_NO_ERROR = 0,
+    JSON_BAD_ROOT_ELEMENT_TYPE,
+    JSON_INVALID_ESCAPE,
+    JSON_SYNTAX_ERROR,
+    JSON_TRAILING_COMMA,
+    JSON_TOO_MUCH_NESTING,
+    JSON_UNEXPECTED_DATA_AFTER_ROOT,
+    JSON_UNSUPPORTED_ENCODING,
+    JSON_UNQUOTED_DICTIONARY_KEY,
+  };
+
+  // String versions of parse error codes.
   static const char* kBadRootElementType;
   static const char* kInvalidEscape;
   static const char* kSyntaxError;
@@ -100,17 +113,26 @@
   // objects and arrays even though this goes against the RFC.
   static Value* Read(const std::string& json, bool allow_trailing_comma);
 
-  // Reads and parses |json| like Read(). |error_message_out| is optional. If
-  // specified and NULL is returned, |error_message_out| will be populated with
-  // a string describing the error. Otherwise, |error_message_out| is
-  // unmodified.
+  // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out|
+  // are optional. If specified and NULL is returned, they will be populated
+  // an error code and a formatted error message (including error location if
+  // appropriate). Otherwise, they will be unmodified.
   static Value* ReadAndReturnError(const std::string& json,
                                    bool allow_trailing_comma,
-                                   std::string* error_message_out);
+                                   int* error_code_out,
+                                   std::string* error_msg_out);
 
-  // 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_; }
+  // Converts a JSON parse error code into a human readable message.
+  // Returns an empty string if error_code is JSON_NO_ERROR.
+  static std::string ErrorCodeToString(JsonParseError error_code);
+
+  // Returns the error code if the last call to JsonToValue() failed.
+  // Returns JSON_NO_ERROR otherwise.
+  JsonParseError error_code() const { return error_code_; }
+
+  // Converts error_code_ to a human-readable string, including line and column
+  // numbers if appropriate.
+  std::string GetErrorMessage() const;
 
   // Reads and parses |json|, returning a Value. The caller owns the returned
   // instance. If |json| is not a properly formed JSON string, returns NULL and
@@ -124,7 +146,7 @@
 
  private:
   static std::string FormatErrorMessage(int line, int column,
-                                        const char* description);
+                                        const std::string& description);
 
   DISALLOW_COPY_AND_ASSIGN(JSONReader);
 
@@ -170,9 +192,9 @@
   // 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
+  // Sets the error code 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);
+  void SetErrorCode(const JsonParseError error, const wchar_t* error_pos);
 
   // Pointer to the starting position in the input string.
   const wchar_t* start_pos_;
@@ -186,8 +208,10 @@
   // 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_;
+  // Contains the error code for the last call to JsonToValue(), if any.
+  JsonParseError error_code_;
+  int error_line_;
+  int error_col_;
 };
 
 }  // namespace base
diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc
index 17dea56..71649df 100644
--- a/base/json/json_reader_unittest.cc
+++ b/base/json/json_reader_unittest.cc
@@ -468,23 +468,30 @@
 TEST(JSONReaderTest, ErrorMessages) {
   // Error strings should not be modified in case of success.
   std::string error_message;
+  int error_code = 0;
   scoped_ptr<Value> root;
-  root.reset(JSONReader::ReadAndReturnError("[42]", false, &error_message));
+  root.reset(JSONReader::ReadAndReturnError("[42]", false,
+                                            &error_code, &error_message));
   EXPECT_TRUE(error_message.empty());
+  EXPECT_EQ(0, error_code);
 
   // Test line and column counting
   const char* big_json = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]";
   // error here --------------------------------^
-  root.reset(JSONReader::ReadAndReturnError(big_json, false, &error_message));
+  root.reset(JSONReader::ReadAndReturnError(big_json, false,
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(5, 9, JSONReader::kSyntaxError),
             error_message);
+  EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code);
 
   // Test each of the error conditions
-  root.reset(JSONReader::ReadAndReturnError("{},{}", false, &error_message));
+  root.reset(JSONReader::ReadAndReturnError("{},{}", false,
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 3,
       JSONReader::kUnexpectedDataAfterRoot), error_message);
+  EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, error_code);
 
   std::string nested_json;
   for (int i = 0; i < 101; ++i) {
@@ -492,56 +499,66 @@
     nested_json.append(1, ']');
   }
   root.reset(JSONReader::ReadAndReturnError(nested_json, false,
-                                            &error_message));
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 101, JSONReader::kTooMuchNesting),
             error_message);
+  EXPECT_EQ(JSONReader::JSON_TOO_MUCH_NESTING, error_code);
 
-  root.reset(JSONReader::ReadAndReturnError("42", false, &error_message));
+  root.reset(JSONReader::ReadAndReturnError("42", false,
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 1,
       JSONReader::kBadRootElementType), error_message);
+  EXPECT_EQ(JSONReader::JSON_BAD_ROOT_ELEMENT_TYPE, error_code);
 
-  root.reset(JSONReader::ReadAndReturnError("[1,]", false, &error_message));
+  root.reset(JSONReader::ReadAndReturnError("[1,]", false,
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 4, JSONReader::kTrailingComma),
             error_message);
+  EXPECT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code);
 
   root.reset(JSONReader::ReadAndReturnError("{foo:\"bar\"}", false,
-                                            &error_message));
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 2,
       JSONReader::kUnquotedDictionaryKey), error_message);
+  EXPECT_EQ(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, error_code);
 
   root.reset(JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}", false,
-                                            &error_message));
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 14, JSONReader::kTrailingComma),
             error_message);
 
-  root.reset(JSONReader::ReadAndReturnError("[nu]", false, &error_message));
+  root.reset(JSONReader::ReadAndReturnError("[nu]", false,
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 2, JSONReader::kSyntaxError),
             error_message);
+  EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code);
 
   root.reset(JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", false,
-                                            &error_message));
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
             error_message);
+  EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code);
 
   root.reset(JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", false,
-                                            &error_message));
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
             error_message);
+  EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code);
 
   root.reset(JSONReader::ReadAndReturnError("[\"xxx\\q\"]", false,
-                                            &error_message));
+                                            &error_code, &error_message));
   EXPECT_FALSE(root.get());
   EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape),
             error_message);
-
+  EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code);
 }
 
 }  // namespace base
diff --git a/base/values.h b/base/values.h
index ac6307a..44af9c1 100644
--- a/base/values.h
+++ b/base/values.h
@@ -431,9 +431,11 @@
 
   // This method deserializes the subclass-specific format into a Value object.
   // If the return value is non-NULL, the caller takes ownership of returned
-  // Value. If the return value is NULL, and if error_message is non-NULL,
-  // error_message should be filled with a message describing the error.
-  virtual Value* Deserialize(std::string* error_message) = 0;
+  // Value. If the return value is NULL, and if error_code is non-NULL,
+  // error_code will be set with the underlying error.
+  // If |error_message| is non-null, it will be filled in with a formatted
+  // error message including the location of the error if appropriate.
+  virtual Value* Deserialize(int* error_code, std::string* error_str) = 0;
 };
 
 #endif  // BASE_VALUES_H_