Bug 1045958. r=joshmoz
☠☠ backed out by ae5890b31d91 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Tue, 23 Sep 2014 15:30:01 +0900
changeset 208549 01bbf35c82bdc31c7fe4bf129fe8a8e421f68cef
parent 208548 58012f4bb5f7d2dab5855bf121811a1a90d56311
child 208550 0ffdd0092d9b617c33d3cfeb661c863baa5112b7
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjoshmoz
bugs1045958
milestone35.0a1
Bug 1045958. r=joshmoz
intl/build/moz.build
intl/locale/mac/moz.build
intl/locale/mac/nsCollationMacUC.cpp
intl/locale/mac/nsCollationMacUC.h
intl/locale/tests/unit/test_collation_mac_icu.js
intl/locale/tests/unit/xpcshell.ini
modules/libpref/init/all.js
--- a/intl/build/moz.build
+++ b/intl/build/moz.build
@@ -5,15 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'nsI18nModule.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
+    '../icu/source/common',
+    '../icu/source/i18n',
     '../locale',
     '../lwbrk',
     '../strres',
     '../uconv',
     '../unicharutil',
 ]
 
--- a/intl/locale/mac/moz.build
+++ b/intl/locale/mac/moz.build
@@ -8,10 +8,12 @@ UNIFIED_SOURCES += [
     'nsCollationMacUC.cpp',
     'nsDateTimeFormatMac.cpp',
     'nsMacCharset.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '..',
+    '../../icu/source/common',
+    '../../icu/source/i18n',
 ]
 
--- a/intl/locale/mac/nsCollationMacUC.cpp
+++ b/intl/locale/mac/nsCollationMacUC.cpp
@@ -1,43 +1,93 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsCollationMacUC.h"
 #include "nsILocaleService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
 #include "nsIServiceManager.h"
 #include "prmem.h"
 #include "nsString.h"
 
 NS_IMPL_ISUPPORTS(nsCollationMacUC, nsICollation)
 
 nsCollationMacUC::nsCollationMacUC() 
   : mInit(false)
   , mHasCollator(false)
+  , mLocaleICU(nullptr)
   , mLocale(nullptr)
   , mLastStrength(-1)
+  , mCollatorICU(nullptr)
   , mCollator(nullptr)
   , mBuffer(nullptr)
   , mBufferLen(1)
+  , mUseICU(true)
 {
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  if (prefs) {
+    prefs->GetBoolPref("intl.collation.mac.use_icu", &mUseICU);
+  }
 }
 
 nsCollationMacUC::~nsCollationMacUC() 
 {
-  if (mHasCollator) {
 #ifdef DEBUG
-    OSStatus err =
+  nsresult res =
 #endif
-      ::UCDisposeCollator(&mCollator);
-    mHasCollator = false;
-    NS_ASSERTION((err == noErr), "UCDisposeCollator failed");
+    CleanUpCollator();
+  NS_ASSERTION(NS_SUCCEEDED(res), "CleanUpCollator failed");
+  if (mUseICU) {
+    if (mLocaleICU) {
+      moz_free(mLocaleICU);
+      mLocaleICU = nullptr;
+    }
+  } else {
+    if (mBuffer) {
+      moz_free(mBuffer);
+      mBuffer = nullptr;
+    }
   }
-  PR_FREEIF(mBuffer);
+}
+
+nsresult nsCollationMacUC::ConvertStrength(const int32_t aNSStrength,
+                                           UCollationStrength* aICUStrength,
+                                           UColAttributeValue* aCaseLevelOut)
+{
+  NS_ENSURE_ARG_POINTER(aICUStrength);
+  NS_ENSURE_TRUE((aNSStrength < 4), NS_ERROR_FAILURE);
+
+  UCollationStrength strength = UCOL_DEFAULT;
+  UColAttributeValue caseLevel = UCOL_OFF;
+  switch (aNSStrength) {
+    case kCollationCaseInSensitive:
+      strength = UCOL_PRIMARY;
+      break;
+    case kCollationCaseInsensitiveAscii:
+      strength = UCOL_SECONDARY;
+      break;
+    case kCollationAccentInsenstive:
+      caseLevel = UCOL_ON;
+      strength = UCOL_PRIMARY;
+      break;
+    case kCollationCaseSensitive:
+      strength = UCOL_TERTIARY;
+      break;
+    default:
+      NS_WARNING("Bad aNSStrength passed to ConvertStrength.");
+      return NS_ERROR_FAILURE;
+  }
+
+  *aICUStrength = strength;
+  *aCaseLevelOut = caseLevel;
+
+  return NS_OK;
 }
 
 nsresult nsCollationMacUC::StrengthToOptions(const int32_t aStrength,
                                              UCCollateOptions* aOptions)
 {
   NS_ENSURE_ARG_POINTER(aOptions);
   NS_ENSURE_TRUE((aStrength < 4), NS_ERROR_FAILURE);
   // set our default collation options
@@ -45,16 +95,38 @@ nsresult nsCollationMacUC::StrengthToOpt
   if (aStrength & kCollationCaseInsensitiveAscii)
     options |= kUCCollateCaseInsensitiveMask;
   if (aStrength & kCollationAccentInsenstive)
     options |= kUCCollateDiacritInsensitiveMask;
   *aOptions = options;
   return NS_OK;
 }
 
+nsresult nsCollationMacUC::ConvertLocaleICU(nsILocale* aNSLocale, char** aICULocale)
+{
+  NS_ENSURE_ARG_POINTER(aNSLocale);
+  NS_ENSURE_ARG_POINTER(aICULocale);
+
+  nsAutoString localeString;
+  nsresult res = aNSLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), localeString);
+  NS_ENSURE_TRUE(NS_SUCCEEDED(res) && !localeString.IsEmpty(),
+                 NS_ERROR_FAILURE);
+  NS_LossyConvertUTF16toASCII tmp(localeString);
+  tmp.ReplaceChar('-', '_');
+  char* locale = (char*)moz_malloc(tmp.Length() + 1);
+  if (!locale) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  strcpy(locale, tmp.get());
+
+  *aICULocale = locale;
+
+  return NS_OK;
+}
+
 nsresult nsCollationMacUC::ConvertLocale(nsILocale* aNSLocale, LocaleRef* aMacLocale) 
 {
   NS_ENSURE_ARG_POINTER(aNSLocale);
   NS_ENSURE_ARG_POINTER(aMacLocale);
 
   nsAutoString localeString;
   nsresult res = aNSLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), localeString);
   NS_ENSURE_TRUE(NS_SUCCEEDED(res) && !localeString.IsEmpty(),
@@ -69,51 +141,98 @@ nsresult nsCollationMacUC::ConvertLocale
 }
 
 nsresult nsCollationMacUC::EnsureCollator(const int32_t newStrength) 
 {
   NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
   if (mHasCollator && (mLastStrength == newStrength))
     return NS_OK;
 
-  OSStatus err;
-  if (mHasCollator) {
-    err = ::UCDisposeCollator(&mCollator);
-    mHasCollator = false;
+  nsresult res;
+  res = CleanUpCollator();
+  NS_ENSURE_SUCCESS(res, res);
+
+  if (mUseICU) {
+    NS_ENSURE_TRUE(mLocaleICU, NS_ERROR_NOT_INITIALIZED);
+
+    UErrorCode status;
+    status = U_ZERO_ERROR;
+    mCollatorICU = ucol_open(mLocaleICU, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+
+    UCollationStrength strength;
+    UColAttributeValue caseLevel;
+    res = ConvertStrength(newStrength, &strength, &caseLevel);
+    NS_ENSURE_SUCCESS(res, res);
+
+    status = U_ZERO_ERROR;
+    ucol_setAttribute(mCollatorICU, UCOL_STRENGTH, strength, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+    ucol_setAttribute(mCollatorICU, UCOL_CASE_LEVEL, caseLevel, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+    ucol_setAttribute(mCollatorICU, UCOL_ALTERNATE_HANDLING, UCOL_DEFAULT, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+    ucol_setAttribute(mCollatorICU, UCOL_NUMERIC_COLLATION, UCOL_OFF, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+    ucol_setAttribute(mCollatorICU, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+    ucol_setAttribute(mCollatorICU, UCOL_CASE_FIRST, UCOL_DEFAULT, &status);
+    NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+  } else {
+    OSStatus err;
+    UCCollateOptions newOptions;
+    res = StrengthToOptions(newStrength, &newOptions);
+    NS_ENSURE_SUCCESS(res, res);
+
+    LocaleOperationVariant opVariant = 0; // default variant for now
+    err = ::UCCreateCollator(mLocale, opVariant, newOptions, &mCollator);
     NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE);
   }
 
-  UCCollateOptions newOptions;
-  nsresult res = StrengthToOptions(newStrength, &newOptions);
-  NS_ENSURE_SUCCESS(res, res);
-  
-  LocaleOperationVariant opVariant = 0; // default variant for now
-  err = ::UCCreateCollator(mLocale, opVariant, newOptions, &mCollator);
-  NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE);
   mHasCollator = true;
 
   mLastStrength = newStrength;
   return NS_OK;
 }
 
+nsresult nsCollationMacUC::CleanUpCollator(void)
+{
+  if (mHasCollator) {
+    if (mUseICU) {
+      ucol_close(mCollatorICU);
+      mHasCollator = false;
+    } else {
+      OSStatus err = ::UCDisposeCollator(&mCollator);
+      mHasCollator = false;
+      NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE);
+    }
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsCollationMacUC::Initialize(nsILocale* locale) 
 {
   NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED);
   nsCOMPtr<nsILocale> appLocale;
 
   nsresult rv;
   if (!locale) {
     nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
     NS_ENSURE_SUCCESS(rv, rv);
     locale = appLocale;
   }
 
-  rv = ConvertLocale(locale, &mLocale);
+  if (mUseICU) {
+    rv = ConvertLocaleICU(locale, &mLocaleICU);
+  } else {
+    rv = ConvertLocale(locale, &mLocale);
+  }
   NS_ENSURE_SUCCESS(rv, rv);
 
   mInit = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsCollationMacUC::AllocateRawSortKey(int32_t strength, const nsAString& stringIn,
                                                    uint8_t** key, uint32_t* outLen)
@@ -121,42 +240,70 @@ NS_IMETHODIMP nsCollationMacUC::Allocate
   NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_ARG_POINTER(key);
   NS_ENSURE_ARG_POINTER(outLen);
 
   nsresult res = EnsureCollator(strength);
   NS_ENSURE_SUCCESS(res, res);
 
   uint32_t stringInLen = stringIn.Length();
+
+  if (mUseICU) {
+    const UChar* str = (const UChar*)PromiseFlatString(stringIn).get();
+
+    int32_t keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, nullptr, 0);
+    NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
+
+    // Since key is freed elsewhere with PR_Free, allocate with PR_Malloc.
+    uint8_t* newKey = (uint8_t*)PR_Malloc(keyLength + 1);
+    if (!newKey) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, newKey, keyLength + 1);
+    NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
+
+    *key = newKey;
+    *outLen = keyLength;
+
+    return NS_OK;
+  }
+
   uint32_t maxKeyLen = (1 + stringInLen) * kCollationValueSizeFactor * sizeof(UCCollationValue);
   if (maxKeyLen > mBufferLen) {
     uint32_t newBufferLen = mBufferLen;
     do {
       newBufferLen *= 2;
     } while (newBufferLen < maxKeyLen);
-    void *newBuffer = PR_Malloc(newBufferLen);
-    if (!newBuffer)
+    void* newBuffer = moz_malloc(newBufferLen);
+    if (!newBuffer) {
       return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-    PR_FREEIF(mBuffer);
+    if (mBuffer) {
+      moz_free(mBuffer);
+      mBuffer = nullptr;
+    }
     mBuffer = newBuffer;
     mBufferLen = newBufferLen;
   }
 
   ItemCount actual;
   OSStatus err = ::UCGetCollationKey(mCollator, (const UniChar*) PromiseFlatString(stringIn).get(),
                                      (UniCharCount) stringInLen,
                                      (ItemCount) (mBufferLen / sizeof(UCCollationValue)),
                                      &actual, (UCCollationValue *)mBuffer);
   NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE);
 
   uint32_t keyLength = actual * sizeof(UCCollationValue);
-  void *newKey = PR_Malloc(keyLength);
-  if (!newKey)
+  // Since key is freed elsewhere with PR_Free, allocate with PR_Malloc.
+  void* newKey = PR_Malloc(keyLength);
+  if (!newKey) {
     return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   memcpy(newKey, mBuffer, keyLength);
   *key = (uint8_t *)newKey;
   *outLen = keyLength;
 
   return NS_OK;
 }
 
@@ -164,16 +311,33 @@ NS_IMETHODIMP nsCollationMacUC::CompareS
                                               const nsAString& string2, int32_t* result) 
 {
   NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_ARG_POINTER(result);
   *result = 0;
 
   nsresult res = EnsureCollator(strength);
   NS_ENSURE_SUCCESS(res, res);
+
+  if (mUseICU) {
+    UCollationResult uresult;
+    uresult = ucol_strcoll(mCollatorICU,
+                           (const UChar*)PromiseFlatString(string1).get(), string1.Length(),
+                           (const UChar*)PromiseFlatString(string2).get(), string2.Length());
+    int32_t res;
+    switch (uresult) {
+      case UCOL_LESS: res = -1; break;
+      case UCOL_EQUAL: res = 0; break;
+      case UCOL_GREATER: res = 1; break;
+      default: MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
+    }
+    *result = res;
+    return NS_OK;
+  }
+
   *result = 0;
 
   OSStatus err;
   err = ::UCCompareText(mCollator, 
                         (const UniChar *) PromiseFlatString(string1).get(), (UniCharCount) string1.Length(),
                         (const UniChar *) PromiseFlatString(string2).get(), (UniCharCount) string2.Length(),
                         nullptr, (SInt32*) result);
 
@@ -186,16 +350,30 @@ NS_IMETHODIMP nsCollationMacUC::CompareR
                                                   int32_t* result)
 {
   NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_ARG_POINTER(key1);
   NS_ENSURE_ARG_POINTER(key2);
   NS_ENSURE_ARG_POINTER(result);
   *result = 0;
 
+  if (mUseICU) {
+    int32_t tmpResult = strcmp((const char*)key1, (const char*)key2);
+    int32_t res;
+    if (tmpResult < 0) {
+      res = -1;
+    } else if (tmpResult > 0) {
+      res = 1;
+    } else {
+      res = 0;
+    }
+    *result = res;
+    return NS_OK;
+  }
+
   OSStatus err;
   err = ::UCCompareCollationKeys((const UCCollationValue*) key1, (ItemCount) len1,
                                  (const UCCollationValue*) key2, (ItemCount) len2,
                                  nullptr, (SInt32*) result);
 
   NS_ENSURE_TRUE((err == noErr), NS_ERROR_FAILURE);
 
   return NS_OK;
--- a/intl/locale/mac/nsCollationMacUC.h
+++ b/intl/locale/mac/nsCollationMacUC.h
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsCollationMacUC_h_
 #define nsCollationMacUC_h_
 
 #include "nsICollation.h"
 #include "nsCollation.h"
 #include "mozilla/Attributes.h"
+
+#include "unicode/ucol.h"
 #include <Carbon/Carbon.h>
 
 // Maximum number of characters for a buffer to remember 
 // the generated collation key.
 const uint32_t kCacheSize = 128;
 // According to the documentation, the length of the key should typically be
 // at least 5 * textLength, but 6* would be safer.
 const uint32_t kCollationValueSizeFactor = 6;
@@ -27,24 +29,32 @@ public:
   NS_DECL_ISUPPORTS
 
   // nsICollation interface
   NS_DECL_NSICOLLATION
 
 protected:
   ~nsCollationMacUC(); 
 
+  nsresult ConvertLocaleICU(nsILocale* aNSLocale, char** aICULocale);
   nsresult ConvertLocale(nsILocale* aNSLocale, LocaleRef* aMacLocale);
+  nsresult ConvertStrength(const int32_t aStrength,
+                           UCollationStrength* aStrengthOut,
+                           UColAttributeValue* aCaseLevelOut);
   nsresult StrengthToOptions(const int32_t aStrength,
                              UCCollateOptions* aOptions);
   nsresult EnsureCollator(const int32_t newStrength);
+  nsresult CleanUpCollator(void);
 
 private:
   bool mInit;
   bool mHasCollator;
+  char* mLocaleICU;
   LocaleRef mLocale;
   int32_t mLastStrength;
+  UCollator* mCollatorICU;
   CollatorRef mCollator;
   void *mBuffer; // temporary buffer to generate collation keys
   uint32_t mBufferLen; // byte length of buffer
+  bool mUseICU;
 };
 
 #endif  /* nsCollationMacUC_h_ */
new file mode 100644
--- /dev/null
+++ b/intl/locale/tests/unit/test_collation_mac_icu.js
@@ -0,0 +1,108 @@
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+function check_sort() {
+  var input = [
+    "Argentina",
+    "Oerlikon",
+    "Offenbach",
+    "Sverige",
+    "Vaticano",
+    "Zimbabwe",
+    "la France",
+    "¡viva España!",
+    "Österreich",
+    "中国",
+    "日本",
+    "한국",
+  ];
+
+  function test(locale, expected) {
+    var localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"].
+      getService(Ci.nsILocaleService);
+    var collator = Cc["@mozilla.org/intl/collation-factory;1"].
+      createInstance(Ci.nsICollationFactory).
+      CreateCollation(localeSvc.newLocale(locale));
+    var strength = Ci.nsICollation.kCollationStrengthDefault;
+    var actual = input.sort((x, y) => collator.compareString(strength, x,y));
+    deepEqual(actual, expected, locale);
+  }
+
+  // Locale en-US; default options.
+  test("en-US", [
+    "¡viva España!",
+    "Argentina",
+    "la France",
+    "Oerlikon",
+    "Offenbach",
+    "Österreich",
+    "Sverige",
+    "Vaticano",
+    "Zimbabwe",
+    "한국",
+    "中国",
+    "日本",
+  ]);
+
+  // Locale sv-SE; default options.
+  // Swedish treats "Ö" as a separate character, which sorts after "Z".
+  test("sv-SE", [
+    "¡viva España!",
+    "Argentina",
+    "la France",
+    "Oerlikon",
+    "Offenbach",
+    "Sverige",
+    "Vaticano",
+    "Zimbabwe",
+    "Österreich",
+    "한국",
+    "中国",
+    "日本",
+  ]);
+
+  // Locale de-DE; default options.
+  // In German standard sorting, umlauted characters are treated as variants
+  // of their base characters: ä ≅ a, ö ≅ o, ü ≅ u.
+  test("de-DE", [
+    "¡viva España!",
+    "Argentina",
+    "la France",
+    "Oerlikon",
+    "Offenbach",
+    "Österreich",
+    "Sverige",
+    "Vaticano",
+    "Zimbabwe",
+    "한국",
+    "中国",
+    "日本",
+  ]);
+}
+
+function test_default() {
+  Services.prefs.clearUserPref("intl.collation.mac.use_icu");
+  check_sort();
+}
+
+function test_ICU() {
+  Services.prefs.setBoolPref("intl.collation.mac.use_icu", true);
+  check_sort();
+}
+
+function test_CoreServices() {
+  Services.prefs.setBoolPref("intl.collation.mac.use_icu", false);
+  check_sort();
+}
+
+function run_test()
+{
+  test_default();
+  test_ICU();
+  if (Services.sysinfo.getProperty("arch") == "x86") {
+    test_CoreServices();
+  }
+
+  Services.prefs.clearUserPref("intl.collation.mac.use_icu");
+}
--- a/intl/locale/tests/unit/xpcshell.ini
+++ b/intl/locale/tests/unit/xpcshell.ini
@@ -4,11 +4,14 @@ tail =
 
 [test_bug22310.js]
 run-if = toolkit == "windows" || toolkit == "cocoa"
 
 [test_bug371611.js]
 [test_bug374040.js]
 skip-if = toolkit == "windows" || toolkit == "cocoa"
 
+[test_collation_mac_icu.js]
+run-if = toolkit == "cocoa"
+
 [test_pluralForm.js]
 [test_pluralForm_english.js]
 [test_pluralForm_makeGetter.js]
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4325,8 +4325,13 @@ pref("camera.control.low_memory_threshol
 pref("dom.udpsocket.enabled", false);
 
 // Experiment: Get TTL from DNS records.
 //     Unset initially (0); Randomly chosen on first run; will remain unchanged
 //     unless adjusted by the user or experiment ends. Variants defined in
 //     nsHostResolver.cpp.
 pref("dns.ttl-experiment.variant", 0);
 pref("dns.ttl-experiment.enabled", true);
+
+// Use raw ICU instead of CoreServices API in Unicode collation
+#ifdef XP_MACOSX
+pref("intl.collation.mac.use_icu", true);
+#endif