Bug 1032726 part 1 - Make ConvertJSValueToByteString work with Latin1 strings. r=bz,terrence
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 02 Jul 2014 13:04:14 +0200
changeset 212696 4ffff7bdb4c8a3ddb3f4d356385350b9bd3125d4
parent 212695 e5494989d182b8d0d0222219cd4ee46904a09d16
child 212697 4dc51e7f8e8d7f6979aa7bb4f838e28180c07399
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, terrence
bugs1032726
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1032726 part 1 - Make ConvertJSValueToByteString work with Latin1 strings. r=bz,terrence
dom/bindings/BindingUtils.cpp
js/src/jsapi.cpp
js/src/jsapi.h
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2119,40 +2119,61 @@ ConvertJSValueToByteString(JSContext* cx
 
     s = JS::ToString(cx, v);
     if (!s) {
       return false;
     }
     pval.set(JS::StringValue(s));  // Root the new string.
   }
 
+  // Conversion from Javascript string to ByteString is only valid if all
+  // characters < 256. This is always the case for Latin1 strings.
   size_t length;
-  const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &length);
-  if (!chars) {
-    return false;
-  }
+  if (!JS_StringHasLatin1Chars(s)) {
+    // ThrowErrorMessage can GC, so we first scan the string for bad chars
+    // and report the error outside the AutoCheckCannotGC scope.
+    bool foundBadChar = false;
+    size_t badCharIndex;
+    jschar badChar;
+    {
+      JS::AutoCheckCannotGC nogc;
+      const jschar* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length);
+      if (!chars) {
+        return false;
+      }
 
-  // Conversion from Javascript string to ByteString is only valid if all
-  // characters < 256.
-  for (size_t i = 0; i < length; i++) {
-    if (chars[i] > 255) {
+      for (size_t i = 0; i < length; i++) {
+        if (chars[i] > 255) {
+          badCharIndex = i;
+          badChar = chars[i];
+          foundBadChar = true;
+          break;
+        }
+      }
+    }
+
+    if (foundBadChar) {
+      MOZ_ASSERT(badCharIndex < length);
+      MOZ_ASSERT(badChar > 255);
       // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
       // 20 digits, plus one more for the null terminator.
       char index[21];
       static_assert(sizeof(size_t) <= 8, "index array too small");
-      PR_snprintf(index, sizeof(index), "%d", i);
+      PR_snprintf(index, sizeof(index), "%d", badCharIndex);
       // A jschar is 16 bits long.  The biggest unsigned 16 bit
       // number (65,535) has 5 digits, plus one more for the null
       // terminator.
-      char badChar[6];
-      static_assert(sizeof(jschar) <= 2, "badChar array too small");
-      PR_snprintf(badChar, sizeof(badChar), "%d", chars[i]);
-      ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badChar);
+      char badCharArray[6];
+      static_assert(sizeof(jschar) <= 2, "badCharArray too small");
+      PR_snprintf(badCharArray, sizeof(badCharArray), "%d", badChar);
+      ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
       return false;
     }
+  } else {
+    length = JS_GetStringLength(s);
   }
 
   if (length >= UINT32_MAX) {
     return false;
   }
   result.SetCapacity(length+1);
   JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
   result.BeginWriting()[length] = '\0';
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5316,16 +5316,22 @@ JS_InternUCString(JSContext *cx, const j
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str)
 {
     return str->length();
 }
 
+JS_PUBLIC_API(bool)
+JS_StringHasLatin1Chars(JSString *str)
+{
+    return str->hasLatin1Chars();
+}
+
 JS_PUBLIC_API(const jschar *)
 JS_GetStringCharsZ(JSContext *cx, JSString *str)
 {
     size_t dummy;
     return JS_GetStringCharsZAndLength(cx, str, &dummy);
 }
 
 JS_PUBLIC_API(const jschar *)
@@ -5355,16 +5361,31 @@ JS_GetStringCharsAndLength(JSContext *cx
     JSLinearString *linear = str->ensureLinear(cx);
     if (!linear)
         return nullptr;
     *plength = linear->length();
     return linear->chars();
 }
 
 JS_PUBLIC_API(const jschar *)
+JS_GetTwoByteStringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
+                                  size_t *plength)
+{
+    JS_ASSERT(plength);
+    AssertHeapIsIdleOrStringIsFlat(cx, str);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, str);
+    JSLinearString *linear = str->ensureLinear(cx);
+    if (!linear)
+        return nullptr;
+    *plength = linear->length();
+    return linear->twoByteChars(nogc);
+}
+
+JS_PUBLIC_API(const jschar *)
 JS_GetInternedStringChars(JSString *str)
 {
     JS_ASSERT(str->isAtom());
     JSFlatString *flat = str->ensureFlat(nullptr);
     if (!flat)
         return nullptr;
     return flat->chars();
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4161,20 +4161,28 @@ JS_FileEscapedString(FILE *fp, JSString 
  * APIs without CharsZ if possible. The infallible functions also return
  * null-terminated arrays. (There is no additional cost or non-Z alternative
  * for the infallible functions, so 'Z' is left out of the identifier.)
  */
 
 extern JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str);
 
+/* Returns true iff the string's characters are stored as Latin1. */
+extern JS_PUBLIC_API(bool)
+JS_StringHasLatin1Chars(JSString *str);
+
 extern JS_PUBLIC_API(const jschar *)
 JS_GetStringCharsAndLength(JSContext *cx, JSString *str, size_t *length);
 
 extern JS_PUBLIC_API(const jschar *)
+JS_GetTwoByteStringCharsAndLength(JSContext *cx, const JS::AutoCheckCannotGC &nogc, JSString *str,
+                                  size_t *length);
+
+extern JS_PUBLIC_API(const jschar *)
 JS_GetInternedStringChars(JSString *str);
 
 extern JS_PUBLIC_API(const jschar *)
 JS_GetInternedStringCharsAndLength(JSString *str, size_t *length);
 
 extern JS_PUBLIC_API(const jschar *)
 JS_GetStringCharsZ(JSContext *cx, JSString *str);