0

I have encountered a strange issue with IE11. Consider the following (part of the riot.js framework):

var s = "{JSON.stringify(\\{ amount: Math.floor(reward.amount) \\})}";
var s1 = s.replace(/\\{/g, '\uFFF0');

When running this code on localhost, it runs fine. But when running from our staging environment, the \{ fragment is replaced not by \uFFF0 (codepoint 65520) but by \uFFFD (codepoint 65533). That means it fails later when trying to replace the special character back to {.

The replace method is the browser's native one. The file that contain both the HTML (string is a DOM attribute) and the javascript is returned by the server with charset=utf-8 header and encoded as such. In staging environment, it is bundled with other files (not compression or mangling though) and still encoded in utf-8.
I have no idea why it does that, or why it's not systematic.

Antoine
  • 5,055
  • 11
  • 54
  • 82

2 Answers2

0

\uFFFD, or as visualized: �, is the browsers's way of showing you the character in the string is an invalid character.

The string itself will still contain \uFFF0, but since that character is not defined, the browser renders �, instead.

For me, in the console,
Google Chrome shows: ￰ (White box, black border, with a question mark).
Safari shows: ￰ (White box, black border, with a question mark).
Internet explorer shows: ￰ (White box, black border).
Edge shows: ￰ (White box, black border).
Firefox shows: nothing.

They're all the same string. Just a different visual representation, depending on the browser.

var s = "{JSON.stringify(\\{ amount: Math.floor(reward.amount) \\})}",
    s1 = s.replace(/\\{/g, '\uFFF0'),
    charCode = s1.charCodeAt(16);

document.write(charCode + ' ' + String.fromCharCode(charCode));
document.write('|');
document.write('￰'.charCodeAt(0));
document.write('|');
document.write('x'.replace('x', '\uFFF0').charCodeAt(0));

(For me, in Chrome, this snippet shows me: 65520 ￰|65520|65520)

Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • Well, `s1.charCodeAt(17)` return 65533; not 65520, so the string indeed contains the replacement char and not the intended one. Also, `s1.replace('\uFFF0', '{')` does nothing, which means the string does not contain the '\uFFF0' char. Then again, it's only when executing from distant env, works ok on local. – Antoine Nov 20 '15 at 08:50
  • The issue occurs only in IE11, only when executing code from a specific environment. That's why I included server/encoding details. – Antoine Nov 20 '15 at 09:00
  • My point is that browsers don't replace the character. Running my snippet in IE11 gives the same results as chrome, except that the 2nd number is 65533, because the actual character in the string there is `�` instead of `￰`. This probably ins't a browser issue, but something else. – Cerbrus Nov 20 '15 at 09:05
0

I tracked the issue down to UglifyJs. By default, it replaces escaped unicode chars with the actual character. So:

var s1 = s.replace(/\\{/g, '\uFFF0');

Becomes this in the bundled file:

var s1 = s.replace(/\\{/g, '￰');

Which is not visible when debugging with source map, and does not behave well in IE11.

Solution is to add ASCIIOnly: true option to Uglify.
NB: Uglify doc refers to this option as either ascii-only or ascii_only, but the only variant really taken into consideration is ASCIIOnly.

Antoine
  • 5,055
  • 11
  • 54
  • 82