Add proper handling of multi-line error messages.

Change-Id: I9d51b461e1f8a107c7ed6a61d698a25fb5935a07
diff --git a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
index 658fc10..3608851 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -373,21 +373,17 @@
     return (match && match[1]) || @StackTraceCreator::ANONYMOUS;
   }-*/;
 
-  /*
-   * u200b is a special character inserted by our Throwable.initializeBackingError before any
-   * newlines in the Throwable _message_ so that we can distinguish those newlines from the newlines
-   * that separate lines of the _stack trace_.
-   *
-   * Why replace()+split() instead of a regex with lookbehind? Because lookbehind isn't available
-   * until ECMAScript 2018.
-   *
-   * Why not have Throwable generate \n\u200b instead of \u200b\n so that we can use lookahead?
-   * Because u200b, despite being a "zero-width space," does take up space when it appears at the
-   * _beginning_ of a line, at least in some terminals, including xterm. Ditto for u2060 (word
-   * joiner) and probably other "zero-width" characters.
-   */
   private static native JsArrayString split(Object t) /*-{
     var e = t.@Throwable::backingJsObject;
-    return (e && e.stack) ? e.stack.replace('\u200b\n', ' ').split('\n') : [];
+    if (e && e.stack) {
+      var stack = e.stack;
+      // If the stack starts with toString of Error, drop it.
+      var toString = e + "\n";
+      if (stack.substring(0, toString.length) == toString) {
+        stack = stack.substring(toString.length);
+      }
+      return stack.split('\n');
+    }
+    return [];
   }-*/;
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Throwable.java b/user/super/com/google/gwt/emul/java/lang/Throwable.java
index fb7eddd..7467655 100644
--- a/user/super/com/google/gwt/emul/java/lang/Throwable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Throwable.java
@@ -106,16 +106,7 @@
   }
 
   private void initializeBackingError() {
-    /*
-     * Prefix newlines in the _message_ with u200b so that StackTraceCreator can distinguish them
-     * from the newlines in the JavaScript Error _stack trace_. (StackTraceCreator's input is a
-     * string that often contains both the message and the stack trace.)
-     */
-    String message =
-        detailMessage == null ? null : detailMessage.nativeReplaceAll("\n", "\u200b\n");
-    String errorMessage = toString(message);
-
-    setBackingJsObject(fixIE(createError(errorMessage)));
+    setBackingJsObject(fixIE(createError(toString(detailMessage))));
 
     captureStackTrace();
   }
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
index 81a72d9..198fa5b 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
@@ -64,8 +64,7 @@
     assertStackTrace(StackTraceExamples.chrome_31_file(), expected);
   }
 
-  // Asserts expected behavior but it is broken
-  public void _disabled_testChrome_31_multiline() {
+  public void testChrome_31_multiline() {
     StackTraceElement[] expected = new StackTraceElement[] {
         createSTE("dumpException6", "http://www.example.com/test/ABCD.cache.js@20", 82),
         createSTE("onclick", "http://www.example.com/test/ABCD.cache.js@122", 101),
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java b/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
index 333547a..d959e16 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
@@ -135,7 +135,8 @@
           "                };\n" +
           "            } has no method 'nonExistentMethod'\n" +
           "    at dumpException6 (http://www.example.com/test/ABCD.cache.js:82:20)\n" +
-          "    at HTMLButtonElement.onclick (http://www.example.com/test/ABCD.cache.js:101:122)"
+          "    at HTMLButtonElement.onclick (http://www.example.com/test/ABCD.cache.js:101:122)",
+      toString: function() { return this.name + ": " + this.message; },
     };
   }-*/;
 
diff --git a/user/test/com/google/gwt/emultest/java/lang/ThrowableTest.java b/user/test/com/google/gwt/emultest/java/lang/ThrowableTest.java
index 820eb3a..cd72e7d 100644
--- a/user/test/com/google/gwt/emultest/java/lang/ThrowableTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/ThrowableTest.java
@@ -102,7 +102,7 @@
     }
     Throwable e = new Throwable("my\nmsg");
     Object caughtNative = catchNative(createThrower(e));
-    assertTrue(caughtNative.toString().contains("my\u200b\nmsg"));
+    assertTrue(caughtNative.toString().contains("my\nmsg"));
   }
 
   public void testJavaNativeJavaSandwichCatch() {