Stop doing unnecessary UTF-8 to UTF-16 conversions in JSONWriter.

The JSONReader only accepts UTF-8 input strings and converts \uXXXX sequences
back into UTF-8. However, the JSONWriter converts all non-ASCII characters to
UTF-16 escape sequences. This round-tripping is sub-optimal, as noted in a
TODO from r54359.

One reason for this may be that JsonDoubleQuote(), used by JSONWriter, does not
handle UTF-8 bytes correctly, interpreting them as code points and writing them
out as \u00XX sequences. If this were read back through a RFC-compliant JSON
parser, the result would be an invalid encoding error. JsonDoubleQuote() does
handle UTF-16 correctly, though.

This rewrites the base/json/string_escape.h API and fixes the above UTF-8 issue
by dividing callers up into three groups:

1. Those that pass valid UTF-8 to be escaped. Prior to this change, very few
   callers used this variant. Those that did were likely using ASCII, otherwise
   the output would be mangled due to the above issue. Now, valid UTF-8 will be
   passed through to the output unescaped. Invalid UTF-8 sequences are replaced
   with U+FFFD.

2. Those that pass valid UTF-16 to be escaped. This function now validates that
   the input is valid UTF-16, and then converts it to unescaped UTF-8 sequences
   for the output.

3. Those that pass arbitrary byte arrays as std::string and expect a non-RFC-
   compliant encoding of the binary data using \uXXXX escapes. This behavior is
   now in the EscapeBytesAsInvalidJSONString() function. It is only used by
   callers who want a "debug string" but do not expect to actually parse the
   output as valid JSON, since it is not.

Additionally, this removes the JSONWriter::OPTIONS_DO_NOT_ESCAPE flag, since
the writer can now handle UTF-8 appropriately.

BUG=15466

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=239800
Reverted: https://src.chromium.org/viewvc/chrome?view=rev&revision=240082

R=asanka@chromium.org, bauerb@chromium.org, mark@chromium.org, thakis@chromium.org, zea@chromium.org

Review URL: https://codereview.chromium.org/100823007

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


CrOS-Libchrome-Original-Commit: bbe1571f0e657d3ba18c05835f06c297b863cc09
diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc
index 6a9cc6a..d600663 100644
--- a/base/json/json_writer.cc
+++ b/base/json/json_writer.cc
@@ -21,28 +21,24 @@
 static const char kPrettyPrintLineEnding[] = "\n";
 #endif
 
-/* static */
-const char* JSONWriter::kEmptyArray = "[]";
-
-/* static */
+// static
 void JSONWriter::Write(const Value* const node, std::string* json) {
   WriteWithOptions(node, 0, json);
 }
 
-/* static */
+// static
 void JSONWriter::WriteWithOptions(const Value* const node, int options,
                                   std::string* json) {
   json->clear();
   // Is there a better way to estimate the size of the output?
   json->reserve(1024);
 
-  bool escape = !(options & OPTIONS_DO_NOT_ESCAPE);
   bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES);
   bool omit_double_type_preservation =
       !!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION);
   bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT);
 
-  JSONWriter writer(escape, omit_binary_values, omit_double_type_preservation,
+  JSONWriter writer(omit_binary_values, omit_double_type_preservation,
                     pretty_print, json);
   writer.BuildJSONString(node, 0);
 
@@ -50,11 +46,10 @@
     json->append(kPrettyPrintLineEnding);
 }
 
-JSONWriter::JSONWriter(bool escape, bool omit_binary_values,
+JSONWriter::JSONWriter(bool omit_binary_values,
                        bool omit_double_type_preservation, bool pretty_print,
                        std::string* json)
-    : escape_(escape),
-      omit_binary_values_(omit_binary_values),
+    : omit_binary_values_(omit_binary_values),
       omit_double_type_preservation_(omit_double_type_preservation),
       pretty_print_(pretty_print),
       json_string_(json) {
@@ -123,11 +118,7 @@
         std::string value;
         bool result = node->GetAsString(&value);
         DCHECK(result);
-        if (escape_) {
-          JsonDoubleQuote(UTF8ToUTF16(value), true, json_string_);
-        } else {
-          JsonDoubleQuote(value, true, json_string_);
-        }
+        EscapeJSONString(value, true, json_string_);
         break;
       }
 
@@ -169,7 +160,7 @@
           json_string_->append(kPrettyPrintLineEnding);
 
         const DictionaryValue* dict =
-          static_cast<const DictionaryValue*>(node);
+            static_cast<const DictionaryValue*>(node);
         bool first_entry = true;
         for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
              itr.Advance(), first_entry = false) {
@@ -186,7 +177,8 @@
 
           if (pretty_print_)
             IndentLine(depth + 1);
-          AppendQuotedString(itr.key());
+
+          EscapeJSONString(itr.key(), true, json_string_);
           if (pretty_print_) {
             json_string_->append(": ");
           } else {
@@ -218,12 +210,6 @@
   }
 }
 
-void JSONWriter::AppendQuotedString(const std::string& str) {
-  // TODO(viettrungluu): |str| is UTF-8, not ASCII, so to properly escape it we
-  // have to convert it to UTF-16. This round-trip is suboptimal.
-  JsonDoubleQuote(UTF8ToUTF16(str), true, json_string_);
-}
-
 void JSONWriter::IndentLine(int depth) {
   // It may be faster to keep an indent string so we don't have to keep
   // reallocating.