<!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 &lt;thetalecrafter@gmail.com&gt; 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  &lt;thetalecrafter@gmail.com&gt;
+
+        [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  &lt;simon.fraser@apple.com&gt;
</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(&quot;This test checks String.prototype.toLocaleLowerCase().&quot;);
+
+shouldBe(&quot;String.prototype.toLocaleLowerCase.length&quot;, &quot;0&quot;);
+
+// Check empty string optimization.
+shouldBe(&quot;''.toLocaleLowerCase()&quot;, &quot;''&quot;);
+
+// Generic
+shouldBe(&quot;String.prototype.toLocaleLowerCase.call({ toString() { return 'A' } })&quot;, &quot;'a'&quot;);
+shouldThrow(&quot;String.prototype.toLocaleLowerCase.call({ toString() { throw Error('1') } })&quot;, &quot;'Error: 1'&quot;);
+shouldThrow(&quot;String.prototype.toLocaleLowerCase.call(null)&quot;);
+shouldThrow(&quot;String.prototype.toLocaleLowerCase.call(undefined)&quot;);
+
+// Ignores non-object, non-string locale list.
+shouldBe(&quot;'A'.toLocaleLowerCase(9)&quot;, &quot;'a'&quot;);
+// Handles array-like objects with holes.
+shouldBe(&quot;'\\u0130'.toLocaleLowerCase({ length: 4, 1: 'az', 3: 'en' })&quot;, &quot;'i'&quot;);
+// Doesn't throw, but ignores private tags.
+shouldBe(&quot;'A'.toLocaleLowerCase('x-some-thing')&quot;, &quot;'a'&quot;);
+// Throws on problems with length, get, or toString.
+shouldThrow(&quot;'A'.toLocaleLowerCase(Object.create(null, { length: { get() { throw Error('a') } } }))&quot;, &quot;'Error: a'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase(Object.create(null, { length: { value: 1 }, 0: { get() { throw Error('b') } } }))&quot;, &quot;'Error: b'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase([ { toString() { throw Error('c') } } ])&quot;, &quot;'Error: c'&quot;);
+// Throws on bad tags.
+shouldThrow(&quot;'A'.toLocaleLowerCase([ 5 ])&quot;, &quot;'TypeError: locale value must be a string or object'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('')&quot;, &quot;'RangeError: invalid language tag: '&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('a')&quot;, &quot;'RangeError: invalid language tag: a'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('abcdefghij')&quot;, &quot;'RangeError: invalid language tag: abcdefghij'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('#$')&quot;, &quot;'RangeError: invalid language tag: #$'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en-@-abc')&quot;, &quot;'RangeError: invalid language tag: en-@-abc'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en-u')&quot;, &quot;'RangeError: invalid language tag: en-u'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en-u-kn-true-u-ko-true')&quot;, &quot;'RangeError: invalid language tag: en-u-kn-true-u-ko-true'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en-x')&quot;, &quot;'RangeError: invalid language tag: en-x'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en-*')&quot;, &quot;'RangeError: invalid language tag: en-*'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en-')&quot;, &quot;'RangeError: invalid language tag: en-'&quot;);
+shouldThrow(&quot;'A'.toLocaleLowerCase('en--US')&quot;, &quot;'RangeError: invalid language tag: en--US'&quot;);
+
+// Check ascii and accents.
+shouldBe(&quot;'AbCdEfGhIjKlMnOpQRStuvWXyZ0123456789'.toLocaleLowerCase()&quot;, &quot;'abcdefghijklmnopqrstuvwxyz0123456789'&quot;);
+shouldBe(&quot;'ÀÉÎÖŒØŪÑ'.toLocaleLowerCase()&quot;, &quot;'àéîöœøūñ'&quot;);
+
+// Check non-special case for dotted I.
+shouldBe(&quot;'\\u0130'.toLocaleLowerCase('und')&quot;, &quot;'\\u0069\\u0307'&quot;);
+
+// Check for special casing of Azeri.
+shouldBe(&quot;'\\u0130'.toLocaleLowerCase('az')&quot;, &quot;'i'&quot;);
+shouldBe(&quot;'I\\u0307'.toLocaleLowerCase('az')&quot;, &quot;'i'&quot;);
+shouldBe(&quot;'I\\u0323\\u0307'.toLocaleLowerCase('az')&quot;, &quot;'i\\u0323'&quot;);
+shouldBe(&quot;'I\\uD800\\uDDFD\\u0307'.toLocaleLowerCase('az')&quot;, &quot;'i\\uD800\\uDDFD'&quot;);
+shouldBe(&quot;'IA\\u0307'.toLocaleLowerCase('az')&quot;, &quot;'\\u0131a\\u0307'&quot;);
+shouldBe(&quot;'I\\u0300\\u0307'.toLocaleLowerCase('az')&quot;, &quot;'\\u0131\\u0300\\u0307'&quot;);
+shouldBe(&quot;'I\\uD834\\uDD85\\u0307'.toLocaleLowerCase('az')&quot;, &quot;'\\u0131\\uD834\\uDD85\\u0307'&quot;);
+shouldBe(&quot;'I'.toLocaleLowerCase('az')&quot;, &quot;'\\u0131'&quot;);
+shouldBe(&quot;'i'.toLocaleLowerCase('az')&quot;, &quot;'i'&quot;);
+shouldBe(&quot;'\\u0131'.toLocaleLowerCase('az')&quot;, &quot;'\\u0131'&quot;);
+
+// Check for special casing of Lithuanian.
+shouldBe(&quot;'I\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'i\\u0307\\u0300'&quot;);
+shouldBe(&quot;'J\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'j\\u0307\\u0300'&quot;);
+shouldBe(&quot;'\\u012E\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012F\\u0307\\u0300'&quot;);
+shouldBe(&quot;'I\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'i\\u0307\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'J\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'j\\u0307\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'\\u012E\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012F\\u0307\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'I\\u0325\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'i\\u0307\\u0325\\u0300'&quot;);
+shouldBe(&quot;'J\\u0325\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'j\\u0307\\u0325\\u0300'&quot;);
+shouldBe(&quot;'\\u012E\\u0325\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012F\\u0307\\u0325\\u0300'&quot;);
+shouldBe(&quot;'I\\uD800\\uDDFD\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'i\\u0307\\uD800\\uDDFD\\u0300'&quot;);
+shouldBe(&quot;'J\\uD800\\uDDFD\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'j\\u0307\\uD800\\uDDFD\\u0300'&quot;);
+shouldBe(&quot;'\\u012E\\uD800\\uDDFD\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012F\\u0307\\uD800\\uDDFD\\u0300'&quot;);
+shouldBe(&quot;'I\\u0325\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'i\\u0307\\u0325\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'J\\u0325\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'j\\u0307\\u0325\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'\\u012E\\u0325\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012F\\u0307\\u0325\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'I\\uD800\\uDDFD\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'i\\u0307\\uD800\\uDDFD\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'J\\uD800\\uDDFD\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'j\\u0307\\uD800\\uDDFD\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'\\u012E\\uD800\\uDDFD\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012F\\u0307\\uD800\\uDDFD\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'IA\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'ia\\u0300'&quot;);
+shouldBe(&quot;'JA\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'ja\\u0300'&quot;);
+shouldBe(&quot;'\\u012EA\\u0300'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012Fa\\u0300'&quot;);
+shouldBe(&quot;'IA\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'ia\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'JA\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'ja\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'\\u012EA\\uD834\\uDD85'.toLocaleLowerCase('lt')&quot;, &quot;'\\u012Fa\\uD834\\uDD85'&quot;);
+shouldBe(&quot;'\\u00CC'.toLocaleLowerCase('lt')&quot;, &quot;'\\u0069\\u0307\\u0300'&quot;);
+shouldBe(&quot;'\\u00CD'.toLocaleLowerCase('lt')&quot;, &quot;'\\u0069\\u0307\\u0301'&quot;);
+shouldBe(&quot;'\\u0128'.toLocaleLowerCase('lt')&quot;, &quot;'\\u0069\\u0307\\u0303'&quot;);
+
+// Check for special casing of Turkish.
+shouldBe(&quot;'\\u0130'.toLocaleLowerCase('tr')&quot;, &quot;'i'&quot;);
+shouldBe(&quot;'I\\u0307'.toLocaleLowerCase('tr')&quot;, &quot;'i'&quot;);
+shouldBe(&quot;'I\\u0323\\u0307'.toLocaleLowerCase('tr')&quot;, &quot;'i\\u0323'&quot;);
+shouldBe(&quot;'I\\uD800\\uDDFD\\u0307'.toLocaleLowerCase('tr')&quot;, &quot;'i\\uD800\\uDDFD'&quot;);
+shouldBe(&quot;'IA\\u0307'.toLocaleLowerCase('tr')&quot;, &quot;'\\u0131a\\u0307'&quot;);
+shouldBe(&quot;'I\\u0300\\u0307'.toLocaleLowerCase('tr')&quot;, &quot;'\\u0131\\u0300\\u0307'&quot;);
+shouldBe(&quot;'I\\uD834\\uDD85\\u0307'.toLocaleLowerCase('tr')&quot;, &quot;'\\u0131\\uD834\\uDD85\\u0307'&quot;);
+shouldBe(&quot;'I'.toLocaleLowerCase('tr')&quot;, &quot;'\\u0131'&quot;);
+shouldBe(&quot;'i'.toLocaleLowerCase('tr')&quot;, &quot;'i'&quot;);
+shouldBe(&quot;'\\u0131'.toLocaleLowerCase('tr')&quot;, &quot;'\\u0131'&quot;);
</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 &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+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>+&lt;!DOCTYPE HTML PUBLIC &quot;-//IETF//DTD HTML//EN&quot;&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;meta charset=&quot;utf8&quot;&gt;
+&lt;script src=&quot;../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;script src=&quot;script-tests/string-toLocaleLowerCase.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</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  &lt;thetalecrafter@gmail.com&gt;
+
+        [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  &lt;ddkilzer@apple.com&gt;
</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&lt;String&gt;&amp; availableLocales, const String&amp; locale)
</del><ins>+String bestAvailableLocale(const HashSet&lt;String&gt;&amp; availableLocales, const String&amp; 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&amp; locale)
</del><ins>+String removeUnicodeLocaleExtension(const String&amp; locale)
</ins><span class="cx"> {
</span><span class="cx">     Vector&lt;String&gt; 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&amp;, Structure*);
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+String defaultLocale();
</ins><span class="cx"> bool intlBooleanOption(ExecState&amp;, JSValue options, PropertyName, bool&amp; usesFallback);
</span><span class="cx"> String intlStringOption(ExecState&amp;, JSValue options, PropertyName, const HashSet&lt;String&gt;&amp; values, const char* notFound, String fallback);
</span><span class="cx"> Vector&lt;String&gt; canonicalizeLocaleList(ExecState&amp;, JSValue locales);
</span><span class="cx"> HashMap&lt;String, String&gt; resolveLocale(const HashSet&lt;String&gt;&amp; availableLocales, const Vector&lt;String&gt;&amp; requestedLocales, const HashMap&lt;String, String&gt;&amp; options, const Vector&lt;String&gt;&amp; relevantExtensionKeys, Vector&lt;String&gt; (*localeData)(const String&amp;, const String&amp;));
</span><span class="cx"> JSValue supportedLocales(ExecState&amp;, const HashSet&lt;String&gt;&amp; availableLocales, const Vector&lt;String&gt;&amp; requestedLocales, JSValue options);
</span><ins>+String removeUnicodeLocaleExtension(const String&amp;);
+String bestAvailableLocale(const HashSet&lt;String&gt;&amp;, const String&amp;);
</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 &quot;CopiedSpaceInlines.h&quot;
</span><span class="cx"> #include &quot;Error.h&quot;
</span><span class="cx"> #include &quot;Executable.h&quot;
</span><ins>+#include &quot;IntlObject.h&quot;
+#include &quot;JSCInlines.h&quot;
</ins><span class="cx"> #include &quot;JSGlobalObjectFunctions.h&quot;
</span><span class="cx"> #include &quot;JSArray.h&quot;
</span><span class="cx"> #include &quot;JSFunction.h&quot;
</span><span class="cx"> #include &quot;JSStringBuilder.h&quot;
</span><ins>+#include &quot;JSStringIterator.h&quot;
</ins><span class="cx"> #include &quot;Lookup.h&quot;
</span><span class="cx"> #include &quot;ObjectPrototype.h&quot;
</span><del>-#include &quot;JSCInlines.h&quot;
-#include &quot;JSStringIterator.h&quot;
</del><span class="cx"> #include &quot;PropertyNameArray.h&quot;
</span><span class="cx"> #include &quot;RegExpCache.h&quot;
</span><span class="cx"> #include &quot;RegExpConstructor.h&quot;
</span><span class="lines">@@ -44,6 +45,7 @@
</span><span class="cx"> #include &lt;algorithm&gt;
</span><span class="cx"> #include &lt;unicode/uconfig.h&gt;
</span><span class="cx"> #include &lt;unicode/unorm.h&gt;
</span><ins>+#include &lt;unicode/ustring.h&gt;
</ins><span class="cx"> #include &lt;wtf/ASCIICType.h&gt;
</span><span class="cx"> #include &lt;wtf/MathExtras.h&gt;
</span><span class="cx"> #include &lt;wtf/text/StringView.h&gt;
</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(&quot;toLowerCase&quot;, stringProtoFuncToLowerCase, DontEnum, 0);
</span><span class="cx">     JSC_NATIVE_FUNCTION(&quot;toUpperCase&quot;, stringProtoFuncToUpperCase, DontEnum, 0);
</span><span class="cx">     JSC_NATIVE_FUNCTION(&quot;localeCompare&quot;, stringProtoFuncLocaleCompare, DontEnum, 1);
</span><ins>+#if ENABLE(INTL)
+    JSC_NATIVE_FUNCTION(&quot;toLocaleLowerCase&quot;, stringProtoFuncToLocaleLowerCase, DontEnum, 0);
+#else
</ins><span class="cx">     JSC_NATIVE_FUNCTION(&quot;toLocaleLowerCase&quot;, stringProtoFuncToLowerCase, DontEnum, 0);
</span><ins>+#endif
</ins><span class="cx">     JSC_NATIVE_FUNCTION(&quot;toLocaleUpperCase&quot;, stringProtoFuncToUpperCase, DontEnum, 0);
</span><span class="cx">     JSC_NATIVE_FUNCTION(&quot;big&quot;, stringProtoFuncBig, DontEnum, 0);
</span><span class="cx">     JSC_NATIVE_FUNCTION(&quot;small&quot;, 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)-&gt;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-&gt;thisValue();
+    if (!checkObjectCoercible(thisValue))
+        return throwVMTypeError(state);
+
+    // 2. Let S be ToString(O).
+    JSString* sVal = thisValue.toString(state);
+    const String&amp; s = sVal-&gt;value(state);
+
+    // 3. ReturnIfAbrupt(S).
+    if (state-&gt;hadException())
+        return JSValue::encode(jsUndefined());
+
+    // Optimization for empty strings.
+    if (s.isEmpty())
+        return JSValue::encode(sVal);
+
+    // 4. Let requestedLocales be CanonicalizeLocaleList(locales).
+    Vector&lt;String&gt; requestedLocales = canonicalizeLocaleList(*state, state-&gt;argument(0));
+
+    // 5. ReturnIfAbrupt(requestedLocales).
+    if (state-&gt;hadException())
+        return JSValue::encode(jsUndefined());
+
+    // 6. Let len be the number of elements in requestedLocales.
+    size_t len = requestedLocales.size();
+
+    // 7. If len &gt; 0, then
+    // a. Let requestedLocale be the first element of requestedLocales.
+    // 8. Else
+    // a. Let requestedLocale be DefaultLocale().
+    String requestedLocale = len &gt; 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 &quot;az&quot;, &quot;lt&quot;, and &quot;tr&quot;.
+    const HashSet&lt;String&gt; availableLocales({ ASCIILiteral(&quot;az&quot;), ASCIILiteral(&quot;lt&quot;), ASCIILiteral(&quot;tr&quot;) });
+
+    // 11. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
+    String locale = bestAvailableLocale(availableLocales, noExtensionsLocale);
+
+    // 12. If locale is undefined, let locale be &quot;und&quot;.
+    if (locale.isNull())
+        locale = ASCIILiteral(&quot;und&quot;);
+
+    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&lt;UChar&gt; buffer(viewLength);
+    String lower;
+    const int32_t resultLength = u_strToLower(buffer.data(), viewLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &amp;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&lt;UChar&gt; buffer(resultLength);
+        u_strToLower(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &amp;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-&gt;thisValue();
</span></span></pre>
</div>
</div>

</body>
</html>