<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[193611] trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/193611">193611</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2015-12-06 22:13:26 -0800 (Sun, 06 Dec 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>[INTL] Implement String.prototype.toLocaleLowerCase in ECMA-402
https://bugs.webkit.org/show_bug.cgi?id=147608
Patch by Andy VanWagoner <thetalecrafter@gmail.com> on 2015-12-06
Reviewed by Benjamin Poulain.
Source/JavaScriptCore:
Add toLocaleLowerCase using icu u_strToLower.
* runtime/IntlObject.cpp:
(JSC::defaultLocale): Expose.
(JSC::bestAvailableLocale): Expose.
(JSC::removeUnicodeLocaleExtension): Expose.
* runtime/IntlObject.h:
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):
(JSC::stringProtoFuncToLocaleLowerCase): Add.
LayoutTests:
* js/script-tests/string-toLocaleLowerCase.js: Added.
* js/string-toLocaleLowerCase-expected.txt: Added.
* js/string-toLocaleLowerCase.html: Added.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlObjectcpp">trunk/Source/JavaScriptCore/runtime/IntlObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeIntlObjecth">trunk/Source/JavaScriptCore/runtime/IntlObject.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeStringPrototypecpp">trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsjsscripttestsstringtoLocaleLowerCasejs">trunk/LayoutTests/js/script-tests/string-toLocaleLowerCase.js</a></li>
<li><a href="#trunkLayoutTestsjsstringtoLocaleLowerCaseexpectedtxt">trunk/LayoutTests/js/string-toLocaleLowerCase-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsstringtoLocaleLowerCasehtml">trunk/LayoutTests/js/string-toLocaleLowerCase.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (193610 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-12-07 05:14:30 UTC (rev 193610)
+++ trunk/LayoutTests/ChangeLog        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2015-12-06 Andy VanWagoner <thetalecrafter@gmail.com>
+
+ [INTL] Implement String.prototype.toLocaleLowerCase in ECMA-402
+ https://bugs.webkit.org/show_bug.cgi?id=147608
+
+ Reviewed by Benjamin Poulain.
+
+ * js/script-tests/string-toLocaleLowerCase.js: Added.
+ * js/string-toLocaleLowerCase-expected.txt: Added.
+ * js/string-toLocaleLowerCase.html: Added.
+
</ins><span class="cx"> 2015-12-06 Simon Fraser <simon.fraser@apple.com>
</span><span class="cx">
</span><span class="cx"> REGRESSION (r187121): Can't get to the main content of the page at https://theintercept.com/drone-papers/
</span></span></pre></div>
<a id="trunkLayoutTestsjsscripttestsstringtoLocaleLowerCasejs"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/script-tests/string-toLocaleLowerCase.js (0 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/string-toLocaleLowerCase.js         (rev 0)
+++ trunk/LayoutTests/js/script-tests/string-toLocaleLowerCase.js        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -0,0 +1,96 @@
</span><ins>+description("This test checks String.prototype.toLocaleLowerCase().");
+
+shouldBe("String.prototype.toLocaleLowerCase.length", "0");
+
+// Check empty string optimization.
+shouldBe("''.toLocaleLowerCase()", "''");
+
+// Generic
+shouldBe("String.prototype.toLocaleLowerCase.call({ toString() { return 'A' } })", "'a'");
+shouldThrow("String.prototype.toLocaleLowerCase.call({ toString() { throw Error('1') } })", "'Error: 1'");
+shouldThrow("String.prototype.toLocaleLowerCase.call(null)");
+shouldThrow("String.prototype.toLocaleLowerCase.call(undefined)");
+
+// Ignores non-object, non-string locale list.
+shouldBe("'A'.toLocaleLowerCase(9)", "'a'");
+// Handles array-like objects with holes.
+shouldBe("'\\u0130'.toLocaleLowerCase({ length: 4, 1: 'az', 3: 'en' })", "'i'");
+// Doesn't throw, but ignores private tags.
+shouldBe("'A'.toLocaleLowerCase('x-some-thing')", "'a'");
+// Throws on problems with length, get, or toString.
+shouldThrow("'A'.toLocaleLowerCase(Object.create(null, { length: { get() { throw Error('a') } } }))", "'Error: a'");
+shouldThrow("'A'.toLocaleLowerCase(Object.create(null, { length: { value: 1 }, 0: { get() { throw Error('b') } } }))", "'Error: b'");
+shouldThrow("'A'.toLocaleLowerCase([ { toString() { throw Error('c') } } ])", "'Error: c'");
+// Throws on bad tags.
+shouldThrow("'A'.toLocaleLowerCase([ 5 ])", "'TypeError: locale value must be a string or object'");
+shouldThrow("'A'.toLocaleLowerCase('')", "'RangeError: invalid language tag: '");
+shouldThrow("'A'.toLocaleLowerCase('a')", "'RangeError: invalid language tag: a'");
+shouldThrow("'A'.toLocaleLowerCase('abcdefghij')", "'RangeError: invalid language tag: abcdefghij'");
+shouldThrow("'A'.toLocaleLowerCase('#$')", "'RangeError: invalid language tag: #$'");
+shouldThrow("'A'.toLocaleLowerCase('en-@-abc')", "'RangeError: invalid language tag: en-@-abc'");
+shouldThrow("'A'.toLocaleLowerCase('en-u')", "'RangeError: invalid language tag: en-u'");
+shouldThrow("'A'.toLocaleLowerCase('en-u-kn-true-u-ko-true')", "'RangeError: invalid language tag: en-u-kn-true-u-ko-true'");
+shouldThrow("'A'.toLocaleLowerCase('en-x')", "'RangeError: invalid language tag: en-x'");
+shouldThrow("'A'.toLocaleLowerCase('en-*')", "'RangeError: invalid language tag: en-*'");
+shouldThrow("'A'.toLocaleLowerCase('en-')", "'RangeError: invalid language tag: en-'");
+shouldThrow("'A'.toLocaleLowerCase('en--US')", "'RangeError: invalid language tag: en--US'");
+
+// Check ascii and accents.
+shouldBe("'AbCdEfGhIjKlMnOpQRStuvWXyZ0123456789'.toLocaleLowerCase()", "'abcdefghijklmnopqrstuvwxyz0123456789'");
+shouldBe("'ÀÉÎÖŒØŪÑ'.toLocaleLowerCase()", "'àéîöœøūñ'");
+
+// Check non-special case for dotted I.
+shouldBe("'\\u0130'.toLocaleLowerCase('und')", "'\\u0069\\u0307'");
+
+// Check for special casing of Azeri.
+shouldBe("'\\u0130'.toLocaleLowerCase('az')", "'i'");
+shouldBe("'I\\u0307'.toLocaleLowerCase('az')", "'i'");
+shouldBe("'I\\u0323\\u0307'.toLocaleLowerCase('az')", "'i\\u0323'");
+shouldBe("'I\\uD800\\uDDFD\\u0307'.toLocaleLowerCase('az')", "'i\\uD800\\uDDFD'");
+shouldBe("'IA\\u0307'.toLocaleLowerCase('az')", "'\\u0131a\\u0307'");
+shouldBe("'I\\u0300\\u0307'.toLocaleLowerCase('az')", "'\\u0131\\u0300\\u0307'");
+shouldBe("'I\\uD834\\uDD85\\u0307'.toLocaleLowerCase('az')", "'\\u0131\\uD834\\uDD85\\u0307'");
+shouldBe("'I'.toLocaleLowerCase('az')", "'\\u0131'");
+shouldBe("'i'.toLocaleLowerCase('az')", "'i'");
+shouldBe("'\\u0131'.toLocaleLowerCase('az')", "'\\u0131'");
+
+// Check for special casing of Lithuanian.
+shouldBe("'I\\u0300'.toLocaleLowerCase('lt')", "'i\\u0307\\u0300'");
+shouldBe("'J\\u0300'.toLocaleLowerCase('lt')", "'j\\u0307\\u0300'");
+shouldBe("'\\u012E\\u0300'.toLocaleLowerCase('lt')", "'\\u012F\\u0307\\u0300'");
+shouldBe("'I\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'i\\u0307\\uD834\\uDD85'");
+shouldBe("'J\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'j\\u0307\\uD834\\uDD85'");
+shouldBe("'\\u012E\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'\\u012F\\u0307\\uD834\\uDD85'");
+shouldBe("'I\\u0325\\u0300'.toLocaleLowerCase('lt')", "'i\\u0307\\u0325\\u0300'");
+shouldBe("'J\\u0325\\u0300'.toLocaleLowerCase('lt')", "'j\\u0307\\u0325\\u0300'");
+shouldBe("'\\u012E\\u0325\\u0300'.toLocaleLowerCase('lt')", "'\\u012F\\u0307\\u0325\\u0300'");
+shouldBe("'I\\uD800\\uDDFD\\u0300'.toLocaleLowerCase('lt')", "'i\\u0307\\uD800\\uDDFD\\u0300'");
+shouldBe("'J\\uD800\\uDDFD\\u0300'.toLocaleLowerCase('lt')", "'j\\u0307\\uD800\\uDDFD\\u0300'");
+shouldBe("'\\u012E\\uD800\\uDDFD\\u0300'.toLocaleLowerCase('lt')", "'\\u012F\\u0307\\uD800\\uDDFD\\u0300'");
+shouldBe("'I\\u0325\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'i\\u0307\\u0325\\uD834\\uDD85'");
+shouldBe("'J\\u0325\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'j\\u0307\\u0325\\uD834\\uDD85'");
+shouldBe("'\\u012E\\u0325\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'\\u012F\\u0307\\u0325\\uD834\\uDD85'");
+shouldBe("'I\\uD800\\uDDFD\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'i\\u0307\\uD800\\uDDFD\\uD834\\uDD85'");
+shouldBe("'J\\uD800\\uDDFD\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'j\\u0307\\uD800\\uDDFD\\uD834\\uDD85'");
+shouldBe("'\\u012E\\uD800\\uDDFD\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'\\u012F\\u0307\\uD800\\uDDFD\\uD834\\uDD85'");
+shouldBe("'IA\\u0300'.toLocaleLowerCase('lt')", "'ia\\u0300'");
+shouldBe("'JA\\u0300'.toLocaleLowerCase('lt')", "'ja\\u0300'");
+shouldBe("'\\u012EA\\u0300'.toLocaleLowerCase('lt')", "'\\u012Fa\\u0300'");
+shouldBe("'IA\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'ia\\uD834\\uDD85'");
+shouldBe("'JA\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'ja\\uD834\\uDD85'");
+shouldBe("'\\u012EA\\uD834\\uDD85'.toLocaleLowerCase('lt')", "'\\u012Fa\\uD834\\uDD85'");
+shouldBe("'\\u00CC'.toLocaleLowerCase('lt')", "'\\u0069\\u0307\\u0300'");
+shouldBe("'\\u00CD'.toLocaleLowerCase('lt')", "'\\u0069\\u0307\\u0301'");
+shouldBe("'\\u0128'.toLocaleLowerCase('lt')", "'\\u0069\\u0307\\u0303'");
+
+// Check for special casing of Turkish.
+shouldBe("'\\u0130'.toLocaleLowerCase('tr')", "'i'");
+shouldBe("'I\\u0307'.toLocaleLowerCase('tr')", "'i'");
+shouldBe("'I\\u0323\\u0307'.toLocaleLowerCase('tr')", "'i\\u0323'");
+shouldBe("'I\\uD800\\uDDFD\\u0307'.toLocaleLowerCase('tr')", "'i\\uD800\\uDDFD'");
+shouldBe("'IA\\u0307'.toLocaleLowerCase('tr')", "'\\u0131a\\u0307'");
+shouldBe("'I\\u0300\\u0307'.toLocaleLowerCase('tr')", "'\\u0131\\u0300\\u0307'");
+shouldBe("'I\\uD834\\uDD85\\u0307'.toLocaleLowerCase('tr')", "'\\u0131\\uD834\\uDD85\\u0307'");
+shouldBe("'I'.toLocaleLowerCase('tr')", "'\\u0131'");
+shouldBe("'i'.toLocaleLowerCase('tr')", "'i'");
+shouldBe("'\\u0131'.toLocaleLowerCase('tr')", "'\\u0131'");
</ins></span></pre></div>
<a id="trunkLayoutTestsjsstringtoLocaleLowerCaseexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/string-toLocaleLowerCase-expected.txt (0 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/string-toLocaleLowerCase-expected.txt         (rev 0)
+++ trunk/LayoutTests/js/string-toLocaleLowerCase-expected.txt        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -0,0 +1,83 @@
</span><ins>+This test checks String.prototype.toLocaleLowerCase().
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS String.prototype.toLocaleLowerCase.length is 0
+PASS ''.toLocaleLowerCase() is ''
+PASS String.prototype.toLocaleLowerCase.call({ toString() { return 'A' } }) is 'a'
+PASS String.prototype.toLocaleLowerCase.call({ toString() { throw Error('1') } }) threw exception Error: 1.
+PASS String.prototype.toLocaleLowerCase.call(null) threw exception TypeError: Type error.
+PASS String.prototype.toLocaleLowerCase.call(undefined) threw exception TypeError: Type error.
+PASS 'A'.toLocaleLowerCase(9) is 'a'
+PASS '\u0130'.toLocaleLowerCase({ length: 4, 1: 'az', 3: 'en' }) is 'i'
+PASS 'A'.toLocaleLowerCase('x-some-thing') is 'a'
+PASS 'A'.toLocaleLowerCase(Object.create(null, { length: { get() { throw Error('a') } } })) threw exception Error: a.
+PASS 'A'.toLocaleLowerCase(Object.create(null, { length: { value: 1 }, 0: { get() { throw Error('b') } } })) threw exception Error: b.
+PASS 'A'.toLocaleLowerCase([ { toString() { throw Error('c') } } ]) threw exception Error: c.
+PASS 'A'.toLocaleLowerCase([ 5 ]) threw exception TypeError: locale value must be a string or object.
+PASS 'A'.toLocaleLowerCase('') threw exception RangeError: invalid language tag: .
+PASS 'A'.toLocaleLowerCase('a') threw exception RangeError: invalid language tag: a.
+PASS 'A'.toLocaleLowerCase('abcdefghij') threw exception RangeError: invalid language tag: abcdefghij.
+PASS 'A'.toLocaleLowerCase('#$') threw exception RangeError: invalid language tag: #$.
+PASS 'A'.toLocaleLowerCase('en-@-abc') threw exception RangeError: invalid language tag: en-@-abc.
+PASS 'A'.toLocaleLowerCase('en-u') threw exception RangeError: invalid language tag: en-u.
+PASS 'A'.toLocaleLowerCase('en-u-kn-true-u-ko-true') threw exception RangeError: invalid language tag: en-u-kn-true-u-ko-true.
+PASS 'A'.toLocaleLowerCase('en-x') threw exception RangeError: invalid language tag: en-x.
+PASS 'A'.toLocaleLowerCase('en-*') threw exception RangeError: invalid language tag: en-*.
+PASS 'A'.toLocaleLowerCase('en-') threw exception RangeError: invalid language tag: en-.
+PASS 'A'.toLocaleLowerCase('en--US') threw exception RangeError: invalid language tag: en--US.
+PASS 'AbCdEfGhIjKlMnOpQRStuvWXyZ0123456789'.toLocaleLowerCase() is 'abcdefghijklmnopqrstuvwxyz0123456789'
+PASS 'ÀÉÎÖŒØŪÑ'.toLocaleLowerCase() is 'àéîöœøūñ'
+PASS '\u0130'.toLocaleLowerCase('und') is '\u0069\u0307'
+PASS '\u0130'.toLocaleLowerCase('az') is 'i'
+PASS 'I\u0307'.toLocaleLowerCase('az') is 'i'
+PASS 'I\u0323\u0307'.toLocaleLowerCase('az') is 'i\u0323'
+PASS 'I\uD800\uDDFD\u0307'.toLocaleLowerCase('az') is 'i\uD800\uDDFD'
+PASS 'IA\u0307'.toLocaleLowerCase('az') is '\u0131a\u0307'
+PASS 'I\u0300\u0307'.toLocaleLowerCase('az') is '\u0131\u0300\u0307'
+PASS 'I\uD834\uDD85\u0307'.toLocaleLowerCase('az') is '\u0131\uD834\uDD85\u0307'
+PASS 'I'.toLocaleLowerCase('az') is '\u0131'
+PASS 'i'.toLocaleLowerCase('az') is 'i'
+PASS '\u0131'.toLocaleLowerCase('az') is '\u0131'
+PASS 'I\u0300'.toLocaleLowerCase('lt') is 'i\u0307\u0300'
+PASS 'J\u0300'.toLocaleLowerCase('lt') is 'j\u0307\u0300'
+PASS '\u012E\u0300'.toLocaleLowerCase('lt') is '\u012F\u0307\u0300'
+PASS 'I\uD834\uDD85'.toLocaleLowerCase('lt') is 'i\u0307\uD834\uDD85'
+PASS 'J\uD834\uDD85'.toLocaleLowerCase('lt') is 'j\u0307\uD834\uDD85'
+PASS '\u012E\uD834\uDD85'.toLocaleLowerCase('lt') is '\u012F\u0307\uD834\uDD85'
+PASS 'I\u0325\u0300'.toLocaleLowerCase('lt') is 'i\u0307\u0325\u0300'
+PASS 'J\u0325\u0300'.toLocaleLowerCase('lt') is 'j\u0307\u0325\u0300'
+PASS '\u012E\u0325\u0300'.toLocaleLowerCase('lt') is '\u012F\u0307\u0325\u0300'
+PASS 'I\uD800\uDDFD\u0300'.toLocaleLowerCase('lt') is 'i\u0307\uD800\uDDFD\u0300'
+PASS 'J\uD800\uDDFD\u0300'.toLocaleLowerCase('lt') is 'j\u0307\uD800\uDDFD\u0300'
+PASS '\u012E\uD800\uDDFD\u0300'.toLocaleLowerCase('lt') is '\u012F\u0307\uD800\uDDFD\u0300'
+PASS 'I\u0325\uD834\uDD85'.toLocaleLowerCase('lt') is 'i\u0307\u0325\uD834\uDD85'
+PASS 'J\u0325\uD834\uDD85'.toLocaleLowerCase('lt') is 'j\u0307\u0325\uD834\uDD85'
+PASS '\u012E\u0325\uD834\uDD85'.toLocaleLowerCase('lt') is '\u012F\u0307\u0325\uD834\uDD85'
+PASS 'I\uD800\uDDFD\uD834\uDD85'.toLocaleLowerCase('lt') is 'i\u0307\uD800\uDDFD\uD834\uDD85'
+PASS 'J\uD800\uDDFD\uD834\uDD85'.toLocaleLowerCase('lt') is 'j\u0307\uD800\uDDFD\uD834\uDD85'
+PASS '\u012E\uD800\uDDFD\uD834\uDD85'.toLocaleLowerCase('lt') is '\u012F\u0307\uD800\uDDFD\uD834\uDD85'
+PASS 'IA\u0300'.toLocaleLowerCase('lt') is 'ia\u0300'
+PASS 'JA\u0300'.toLocaleLowerCase('lt') is 'ja\u0300'
+PASS '\u012EA\u0300'.toLocaleLowerCase('lt') is '\u012Fa\u0300'
+PASS 'IA\uD834\uDD85'.toLocaleLowerCase('lt') is 'ia\uD834\uDD85'
+PASS 'JA\uD834\uDD85'.toLocaleLowerCase('lt') is 'ja\uD834\uDD85'
+PASS '\u012EA\uD834\uDD85'.toLocaleLowerCase('lt') is '\u012Fa\uD834\uDD85'
+PASS '\u00CC'.toLocaleLowerCase('lt') is '\u0069\u0307\u0300'
+PASS '\u00CD'.toLocaleLowerCase('lt') is '\u0069\u0307\u0301'
+PASS '\u0128'.toLocaleLowerCase('lt') is '\u0069\u0307\u0303'
+PASS '\u0130'.toLocaleLowerCase('tr') is 'i'
+PASS 'I\u0307'.toLocaleLowerCase('tr') is 'i'
+PASS 'I\u0323\u0307'.toLocaleLowerCase('tr') is 'i\u0323'
+PASS 'I\uD800\uDDFD\u0307'.toLocaleLowerCase('tr') is 'i\uD800\uDDFD'
+PASS 'IA\u0307'.toLocaleLowerCase('tr') is '\u0131a\u0307'
+PASS 'I\u0300\u0307'.toLocaleLowerCase('tr') is '\u0131\u0300\u0307'
+PASS 'I\uD834\uDD85\u0307'.toLocaleLowerCase('tr') is '\u0131\uD834\uDD85\u0307'
+PASS 'I'.toLocaleLowerCase('tr') is '\u0131'
+PASS 'i'.toLocaleLowerCase('tr') is 'i'
+PASS '\u0131'.toLocaleLowerCase('tr') is '\u0131'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsjsstringtoLocaleLowerCasehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/js/string-toLocaleLowerCase.html (0 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/string-toLocaleLowerCase.html         (rev 0)
+++ trunk/LayoutTests/js/string-toLocaleLowerCase.html        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -0,0 +1,11 @@
</span><ins>+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<meta charset="utf8">
+<script src="../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/string-toLocaleLowerCase.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (193610 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-12-07 05:14:30 UTC (rev 193610)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -1,3 +1,21 @@
</span><ins>+2015-12-06 Andy VanWagoner <thetalecrafter@gmail.com>
+
+ [INTL] Implement String.prototype.toLocaleLowerCase in ECMA-402
+ https://bugs.webkit.org/show_bug.cgi?id=147608
+
+ Reviewed by Benjamin Poulain.
+
+ Add toLocaleLowerCase using icu u_strToLower.
+
+ * runtime/IntlObject.cpp:
+ (JSC::defaultLocale): Expose.
+ (JSC::bestAvailableLocale): Expose.
+ (JSC::removeUnicodeLocaleExtension): Expose.
+ * runtime/IntlObject.h:
+ * runtime/StringPrototype.cpp:
+ (JSC::StringPrototype::finishCreation):
+ (JSC::stringProtoFuncToLocaleLowerCase): Add.
+
</ins><span class="cx"> 2015-12-06 David Kilzer <ddkilzer@apple.com>
</span><span class="cx">
</span><span class="cx"> REGRESSION(r193584): Causes heap use-after-free crashes in Web Inspector tests with AddressSanitizer (Requested by ddkilzer on #webkit).
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlObject.cpp (193610 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlObject.cpp        2015-12-07 05:14:30 UTC (rev 193610)
+++ trunk/Source/JavaScriptCore/runtime/IntlObject.cpp        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -112,7 +112,7 @@
</span><span class="cx"> return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
</span><span class="cx"> }
</span><span class="cx">
</span><del>-static String defaultLocale()
</del><ins>+String defaultLocale()
</ins><span class="cx"> {
</span><span class="cx"> // 6.2.4 DefaultLocale ()
</span><span class="cx"> // FIXME: Implement this method.
</span><span class="lines">@@ -576,7 +576,7 @@
</span><span class="cx"> return seen;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-static String bestAvailableLocale(const HashSet<String>& availableLocales, const String& locale)
</del><ins>+String bestAvailableLocale(const HashSet<String>& availableLocales, const String& locale)
</ins><span class="cx"> {
</span><span class="cx"> // 9.2.2 BestAvailableLocale (availableLocales, locale)
</span><span class="cx"> // 1. Let candidate be locale.
</span><span class="lines">@@ -604,7 +604,7 @@
</span><span class="cx"> return String();
</span><span class="cx"> }
</span><span class="cx">
</span><del>-static String removeUnicodeLocaleExtension(const String& locale)
</del><ins>+String removeUnicodeLocaleExtension(const String& locale)
</ins><span class="cx"> {
</span><span class="cx"> Vector<String> parts;
</span><span class="cx"> locale.split('-', parts);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeIntlObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/IntlObject.h (193610 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/IntlObject.h        2015-12-07 05:14:30 UTC (rev 193610)
+++ trunk/Source/JavaScriptCore/runtime/IntlObject.h        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -57,11 +57,14 @@
</span><span class="cx"> IntlObject(VM&, Structure*);
</span><span class="cx"> };
</span><span class="cx">
</span><ins>+String defaultLocale();
</ins><span class="cx"> bool intlBooleanOption(ExecState&, JSValue options, PropertyName, bool& usesFallback);
</span><span class="cx"> String intlStringOption(ExecState&, JSValue options, PropertyName, const HashSet<String>& values, const char* notFound, String fallback);
</span><span class="cx"> Vector<String> canonicalizeLocaleList(ExecState&, JSValue locales);
</span><span class="cx"> HashMap<String, String> resolveLocale(const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, const HashMap<String, String>& options, const Vector<String>& relevantExtensionKeys, Vector<String> (*localeData)(const String&, const String&));
</span><span class="cx"> JSValue supportedLocales(ExecState&, const HashSet<String>& availableLocales, const Vector<String>& requestedLocales, JSValue options);
</span><ins>+String removeUnicodeLocaleExtension(const String&);
+String bestAvailableLocale(const HashSet<String>&, const String&);
</ins><span class="cx">
</span><span class="cx"> } // namespace JSC
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeStringPrototypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp (193610 => 193611)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp        2015-12-07 05:14:30 UTC (rev 193610)
+++ trunk/Source/JavaScriptCore/runtime/StringPrototype.cpp        2015-12-07 06:13:26 UTC (rev 193611)
</span><span class="lines">@@ -28,14 +28,15 @@
</span><span class="cx"> #include "CopiedSpaceInlines.h"
</span><span class="cx"> #include "Error.h"
</span><span class="cx"> #include "Executable.h"
</span><ins>+#include "IntlObject.h"
+#include "JSCInlines.h"
</ins><span class="cx"> #include "JSGlobalObjectFunctions.h"
</span><span class="cx"> #include "JSArray.h"
</span><span class="cx"> #include "JSFunction.h"
</span><span class="cx"> #include "JSStringBuilder.h"
</span><ins>+#include "JSStringIterator.h"
</ins><span class="cx"> #include "Lookup.h"
</span><span class="cx"> #include "ObjectPrototype.h"
</span><del>-#include "JSCInlines.h"
-#include "JSStringIterator.h"
</del><span class="cx"> #include "PropertyNameArray.h"
</span><span class="cx"> #include "RegExpCache.h"
</span><span class="cx"> #include "RegExpConstructor.h"
</span><span class="lines">@@ -44,6 +45,7 @@
</span><span class="cx"> #include <algorithm>
</span><span class="cx"> #include <unicode/uconfig.h>
</span><span class="cx"> #include <unicode/unorm.h>
</span><ins>+#include <unicode/ustring.h>
</ins><span class="cx"> #include <wtf/ASCIICType.h>
</span><span class="cx"> #include <wtf/MathExtras.h>
</span><span class="cx"> #include <wtf/text/StringView.h>
</span><span class="lines">@@ -73,6 +75,7 @@
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
</span><ins>+EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState*);
</ins><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
</span><span class="lines">@@ -127,7 +130,11 @@
</span><span class="cx"> JSC_NATIVE_FUNCTION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
</span><span class="cx"> JSC_NATIVE_FUNCTION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
</span><span class="cx"> JSC_NATIVE_FUNCTION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
</span><ins>+#if ENABLE(INTL)
+ JSC_NATIVE_FUNCTION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, DontEnum, 0);
+#else
</ins><span class="cx"> JSC_NATIVE_FUNCTION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
</span><ins>+#endif
</ins><span class="cx"> JSC_NATIVE_FUNCTION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
</span><span class="cx"> JSC_NATIVE_FUNCTION("big", stringProtoFuncBig, DontEnum, 0);
</span><span class="cx"> JSC_NATIVE_FUNCTION("small", stringProtoFuncSmall, DontEnum, 0);
</span><span class="lines">@@ -1438,6 +1445,93 @@
</span><span class="cx"> return JSValue::encode(jsNumber(Collator().collate(s, a0.toString(exec)->value(exec))));
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+#if ENABLE(INTL)
+EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState* state)
+{
+ // 13.1.2 String.prototype.toLocaleLowerCase ([locales])
+ // http://ecma-international.org/publications/standards/Ecma-402.htm
+
+ // 1. Let O be RequireObjectCoercible(this value).
+ JSValue thisValue = state->thisValue();
+ if (!checkObjectCoercible(thisValue))
+ return throwVMTypeError(state);
+
+ // 2. Let S be ToString(O).
+ JSString* sVal = thisValue.toString(state);
+ const String& s = sVal->value(state);
+
+ // 3. ReturnIfAbrupt(S).
+ if (state->hadException())
+ return JSValue::encode(jsUndefined());
+
+ // Optimization for empty strings.
+ if (s.isEmpty())
+ return JSValue::encode(sVal);
+
+ // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
+ Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
+
+ // 5. ReturnIfAbrupt(requestedLocales).
+ if (state->hadException())
+ return JSValue::encode(jsUndefined());
+
+ // 6. Let len be the number of elements in requestedLocales.
+ size_t len = requestedLocales.size();
+
+ // 7. If len > 0, then
+ // a. Let requestedLocale be the first element of requestedLocales.
+ // 8. Else
+ // a. Let requestedLocale be DefaultLocale().
+ String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale();
+
+ // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
+ String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
+
+ // 10. Let availableLocales be a List with the language tags of the languages for which the Unicode character database contains language sensitive case mappings.
+ // Note 1: As of Unicode 5.1, the availableLocales list contains the elements "az", "lt", and "tr".
+ const HashSet<String> availableLocales({ ASCIILiteral("az"), ASCIILiteral("lt"), ASCIILiteral("tr") });
+
+ // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
+ String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
+
+ // 12. If locale is undefined, let locale be "und".
+ if (locale.isNull())
+ locale = ASCIILiteral("und");
+
+ CString utf8LocaleBuffer = locale.utf8();
+ const StringView view(s);
+ const int32_t viewLength = view.length();
+
+ // Delegate the following steps to u_strToLower.
+ // 13. Let cpList be a List containing in order the code points of S as defined in ES2015, 6.1.4, starting at the first element of S.
+ // 14. For each code point c in cpList, if the Unicode Character Database provides a lower case equivalent of c that is either language insensitive or for the language locale, then replace c in cpList with that/those equivalent code point(s).
+ // 15. Let cuList be a new List.
+ // 16. For each code point c in cpList, in order, append to cuList the elements of the UTF-16 Encoding (defined in ES2015, 6.1.4) of c.
+ // 17. Let L be a String whose elements are, in order, the elements of cuList.
+
+ // Most strings lower case will be the same size as original, so try that first.
+ UErrorCode error(U_ZERO_ERROR);
+ Vector<UChar> buffer(viewLength);
+ String lower;
+ const int32_t resultLength = u_strToLower(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
+ if (U_SUCCESS(error))
+ lower = String(buffer.data(), resultLength);
+ else if (error == U_BUFFER_OVERFLOW_ERROR) {
+ // Lower case needs more space than original. Try again.
+ UErrorCode error(U_ZERO_ERROR);
+ Vector<UChar> buffer(resultLength);
+ u_strToLower(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
+ if (U_FAILURE(error))
+ return throwVMTypeError(state, u_errorName(error));
+ lower = String(buffer.data(), resultLength);
+ } else
+ return throwVMTypeError(state, u_errorName(error));
+
+ // 18. Return L.
+ return JSValue::encode(jsString(state, lower));
+}
+#endif // ENABLE(INTL)
+
</ins><span class="cx"> EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx"> JSValue thisValue = exec->thisValue();
</span></span></pre>
</div>
</div>
</body>
</html>